mirror of https://github.com/buggins/dlangui.git
popups support
This commit is contained in:
parent
a33cf6dcec
commit
85c3dd0091
|
@ -329,6 +329,7 @@
|
||||||
<File path="src\dlangui\widgets\layouts.d" />
|
<File path="src\dlangui\widgets\layouts.d" />
|
||||||
<File path="src\dlangui\widgets\lists.d" />
|
<File path="src\dlangui\widgets\lists.d" />
|
||||||
<File path="src\dlangui\widgets\menu.d" />
|
<File path="src\dlangui\widgets\menu.d" />
|
||||||
|
<File path="src\dlangui\widgets\popup.d" />
|
||||||
<File path="src\dlangui\widgets\styles.d" />
|
<File path="src\dlangui\widgets\styles.d" />
|
||||||
<File path="src\dlangui\widgets\tabs.d" />
|
<File path="src\dlangui\widgets\tabs.d" />
|
||||||
<File path="src\dlangui\widgets\widget.d" />
|
<File path="src\dlangui\widgets\widget.d" />
|
||||||
|
|
|
@ -132,6 +132,7 @@ extern (C) int UIAppMain(string[] args) {
|
||||||
window.mainWidget = (new Button()).text("sample button");
|
window.mainWidget = (new Button()).text("sample button");
|
||||||
}
|
}
|
||||||
window.show();
|
window.show();
|
||||||
|
window.showPopup((new TextWidget()).text("POPUP"d));
|
||||||
window.windowCaption = "New Window Caption";
|
window.windowCaption = "New Window Caption";
|
||||||
|
|
||||||
// run message loop
|
// run message loop
|
||||||
|
|
|
@ -2,8 +2,9 @@ module dlangui.platforms.common.platform;
|
||||||
|
|
||||||
public import dlangui.core.events;
|
public import dlangui.core.events;
|
||||||
import dlangui.widgets.widget;
|
import dlangui.widgets.widget;
|
||||||
|
import dlangui.widgets.popup;
|
||||||
import dlangui.graphics.drawbuf;
|
import dlangui.graphics.drawbuf;
|
||||||
import std.file;
|
|
||||||
private import dlangui.graphics.gldrawbuf;
|
private import dlangui.graphics.gldrawbuf;
|
||||||
|
|
||||||
class Window {
|
class Window {
|
||||||
|
@ -26,6 +27,21 @@ class Window {
|
||||||
abstract void show();
|
abstract void show();
|
||||||
abstract @property string windowCaption();
|
abstract @property string windowCaption();
|
||||||
abstract @property void windowCaption(string caption);
|
abstract @property void windowCaption(string caption);
|
||||||
|
void measure() {
|
||||||
|
if (_mainWidget !is null) {
|
||||||
|
_mainWidget.measure(_dx, _dy);
|
||||||
|
}
|
||||||
|
foreach(p; _popups)
|
||||||
|
p.measure(_dx, _dy);
|
||||||
|
}
|
||||||
|
void layout() {
|
||||||
|
Rect rc = Rect(0, 0, _dx, _dy);
|
||||||
|
if (_mainWidget !is null) {
|
||||||
|
_mainWidget.layout(rc);
|
||||||
|
}
|
||||||
|
foreach(p; _popups)
|
||||||
|
p.layout(rc);
|
||||||
|
}
|
||||||
void onResize(int width, int height) {
|
void onResize(int width, int height) {
|
||||||
if (_dx == width && _dy == height)
|
if (_dx == width && _dy == height)
|
||||||
return;
|
return;
|
||||||
|
@ -34,16 +50,53 @@ class Window {
|
||||||
if (_mainWidget !is null) {
|
if (_mainWidget !is null) {
|
||||||
Log.d("onResize ", _dx, "x", _dy);
|
Log.d("onResize ", _dx, "x", _dy);
|
||||||
long measureStart = currentTimeMillis;
|
long measureStart = currentTimeMillis;
|
||||||
_mainWidget.measure(_dx, _dy);
|
measure();
|
||||||
long measureEnd = currentTimeMillis;
|
long measureEnd = currentTimeMillis;
|
||||||
Log.d("measure took ", measureEnd - measureStart, " ms");
|
Log.d("measure took ", measureEnd - measureStart, " ms");
|
||||||
_mainWidget.layout(Rect(0, 0, _dx, _dy));
|
layout();
|
||||||
long layoutEnd = currentTimeMillis;
|
long layoutEnd = currentTimeMillis;
|
||||||
Log.d("layout took ", layoutEnd - measureEnd, " ms");
|
Log.d("layout took ", layoutEnd - measureEnd, " ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long lastDrawTs;
|
protected PopupWidget[] _popups;
|
||||||
|
/// show new popup
|
||||||
|
PopupWidget showPopup(Widget content) {
|
||||||
|
PopupWidget res = new PopupWidget(content, this);
|
||||||
|
res.anchor.widget = _mainWidget;
|
||||||
|
_popups ~= res;
|
||||||
|
if (_mainWidget !is null)
|
||||||
|
_mainWidget.requestLayout();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/// remove popup
|
||||||
|
bool removePopup(PopupWidget popup) {
|
||||||
|
for (int i = 0; i < _popups.length; i++) {
|
||||||
|
PopupWidget p = _popups[i];
|
||||||
|
if (p is popup) {
|
||||||
|
for (int j = i; j < _popups.length - 1; j++)
|
||||||
|
_popups[j] = _popups[j + 1];
|
||||||
|
_popups.length--;
|
||||||
|
destroy(p);
|
||||||
|
// force redraw
|
||||||
|
_mainWidget.invalidate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns true if widget is child of either main widget or one of popups
|
||||||
|
bool isChild(Widget w) {
|
||||||
|
if (_mainWidget !is null && _mainWidget.isChild(w))
|
||||||
|
return true;
|
||||||
|
foreach(p; _popups)
|
||||||
|
if (p.isChild(w))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long lastDrawTs;
|
||||||
|
|
||||||
this() {
|
this() {
|
||||||
_backgroundColor = 0xFFFFFF;
|
_backgroundColor = 0xFFFFFF;
|
||||||
|
@ -53,9 +106,14 @@ class Window {
|
||||||
destroy(_mainWidget);
|
destroy(_mainWidget);
|
||||||
_mainWidget = null;
|
_mainWidget = null;
|
||||||
}
|
}
|
||||||
|
foreach(p; _popups)
|
||||||
|
destroy(p);
|
||||||
|
_popups = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animate(Widget root, long interval) {
|
private void animate(Widget root, long interval) {
|
||||||
|
if (root is null)
|
||||||
|
return;
|
||||||
if (root.visibility != Visibility.Visible)
|
if (root.visibility != Visibility.Visible)
|
||||||
return;
|
return;
|
||||||
for (int i = 0; i < root.childCount; i++)
|
for (int i = 0; i < root.childCount; i++)
|
||||||
|
@ -64,8 +122,13 @@ class Window {
|
||||||
root.animate(interval);
|
root.animate(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void animate(long interval) {
|
||||||
|
animate(_mainWidget, interval);
|
||||||
|
foreach(p; _popups)
|
||||||
|
p.animate(interval);
|
||||||
|
}
|
||||||
|
|
||||||
void onDraw(DrawBuf buf) {
|
void onDraw(DrawBuf buf) {
|
||||||
if (_mainWidget !is null) {
|
|
||||||
bool needDraw = false;
|
bool needDraw = false;
|
||||||
bool needLayout = false;
|
bool needLayout = false;
|
||||||
bool animationActive = false;
|
bool animationActive = false;
|
||||||
|
@ -74,29 +137,32 @@ class Window {
|
||||||
needDraw = true;
|
needDraw = true;
|
||||||
long ts = std.datetime.Clock.currStdTime;
|
long ts = std.datetime.Clock.currStdTime;
|
||||||
if (animationActive && lastDrawTs != 0) {
|
if (animationActive && lastDrawTs != 0) {
|
||||||
animate(_mainWidget, ts - lastDrawTs);
|
animate(ts - lastDrawTs);
|
||||||
// layout required flag could be changed during animate - check again
|
// layout required flag could be changed during animate - check again
|
||||||
checkUpdateNeeded(needDraw, needLayout, animationActive);
|
checkUpdateNeeded(needDraw, needLayout, animationActive);
|
||||||
}
|
}
|
||||||
if (needLayout) {
|
if (needLayout) {
|
||||||
long measureStart = currentTimeMillis;
|
long measureStart = currentTimeMillis;
|
||||||
_mainWidget.measure(_dx, _dy);
|
measure();
|
||||||
long measureEnd = currentTimeMillis;
|
long measureEnd = currentTimeMillis;
|
||||||
Log.d("measure took ", measureEnd - measureStart, " ms");
|
Log.d("measure took ", measureEnd - measureStart, " ms");
|
||||||
_mainWidget.layout(Rect(0, 0, _dx, _dy));
|
layout();
|
||||||
long layoutEnd = currentTimeMillis;
|
long layoutEnd = currentTimeMillis;
|
||||||
Log.d("layout took ", layoutEnd - measureEnd, " ms");
|
Log.d("layout took ", layoutEnd - measureEnd, " ms");
|
||||||
//checkUpdateNeeded(needDraw, needLayout, animationActive);
|
//checkUpdateNeeded(needDraw, needLayout, animationActive);
|
||||||
}
|
}
|
||||||
long drawStart = currentTimeMillis;
|
long drawStart = currentTimeMillis;
|
||||||
|
// draw main widget
|
||||||
_mainWidget.onDraw(buf);
|
_mainWidget.onDraw(buf);
|
||||||
|
// draw popups
|
||||||
|
foreach(p; _popups)
|
||||||
|
p.onDraw(buf);
|
||||||
long drawEnd = currentTimeMillis;
|
long drawEnd = currentTimeMillis;
|
||||||
Log.d("draw took ", drawEnd - drawStart, " ms");
|
Log.d("draw took ", drawEnd - drawStart, " ms");
|
||||||
lastDrawTs = ts;
|
lastDrawTs = ts;
|
||||||
if (animationActive)
|
if (animationActive)
|
||||||
scheduleAnimation();
|
scheduleAnimation();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// after drawing, call to schedule redraw if animation is active
|
/// after drawing, call to schedule redraw if animation is active
|
||||||
void scheduleAnimation() {
|
void scheduleAnimation() {
|
||||||
|
@ -149,7 +215,7 @@ class Window {
|
||||||
bool res = false;
|
bool res = false;
|
||||||
for(int i = _mouseTrackingWidgets.length - 1; i >=0; i--) {
|
for(int i = _mouseTrackingWidgets.length - 1; i >=0; i--) {
|
||||||
Widget w = _mouseTrackingWidgets[i];
|
Widget w = _mouseTrackingWidgets[i];
|
||||||
if (!_mainWidget.isChild(w)) {
|
if (!isChild(w)) {
|
||||||
// std.algorithm.remove does not work for me
|
// std.algorithm.remove does not work for me
|
||||||
//_mouseTrackingWidgets.remove(i);
|
//_mouseTrackingWidgets.remove(i);
|
||||||
for (int j = i; j < _mouseTrackingWidgets.length - 1; j++)
|
for (int j = i; j < _mouseTrackingWidgets.length - 1; j++)
|
||||||
|
@ -197,7 +263,7 @@ class Window {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// check if _mouseCaptureWidget and _mouseTrackingWidget still exist in child of root widget
|
// check if _mouseCaptureWidget and _mouseTrackingWidget still exist in child of root widget
|
||||||
if (_mouseCaptureWidget !is null && !_mainWidget.isChild(_mouseCaptureWidget))
|
if (_mouseCaptureWidget !is null && !isChild(_mouseCaptureWidget))
|
||||||
_mouseCaptureWidget = null;
|
_mouseCaptureWidget = null;
|
||||||
|
|
||||||
//Log.d("dispatchMouseEvent ", event.action, " (", event.x, ",", event.y, ")");
|
//Log.d("dispatchMouseEvent ", event.action, " (", event.x, ",", event.y, ")");
|
||||||
|
@ -265,6 +331,8 @@ class Window {
|
||||||
|
|
||||||
/// checks content widgets for necessary redraw and/or layout
|
/// checks content widgets for necessary redraw and/or layout
|
||||||
protected void checkUpdateNeeded(Widget root, ref bool needDraw, ref bool needLayout, ref bool animationActive) {
|
protected void checkUpdateNeeded(Widget root, ref bool needDraw, ref bool needLayout, ref bool animationActive) {
|
||||||
|
if (root is null)
|
||||||
|
return;
|
||||||
if (!root.visibility == Visibility.Visible)
|
if (!root.visibility == Visibility.Visible)
|
||||||
return;
|
return;
|
||||||
needDraw = root.needDraw || needDraw;
|
needDraw = root.needDraw || needDraw;
|
||||||
|
@ -284,6 +352,8 @@ class Window {
|
||||||
if (_mainWidget is null)
|
if (_mainWidget is null)
|
||||||
return false;
|
return false;
|
||||||
checkUpdateNeeded(_mainWidget, needDraw, needLayout, animationActive);
|
checkUpdateNeeded(_mainWidget, needDraw, needLayout, animationActive);
|
||||||
|
foreach(p; _popups)
|
||||||
|
checkUpdateNeeded(p, needDraw, needLayout, animationActive);
|
||||||
return needDraw || needLayout || animationActive;
|
return needDraw || needLayout || animationActive;
|
||||||
}
|
}
|
||||||
/// requests update for window (unless force is true, update will be performed only if layout, redraw or animation is required).
|
/// requests update for window (unless force is true, update will be performed only if layout, redraw or animation is required).
|
||||||
|
@ -334,6 +404,7 @@ version (Windows) {
|
||||||
|
|
||||||
/// returns current executable path only, including last path delimiter
|
/// returns current executable path only, including last path delimiter
|
||||||
string exePath() {
|
string exePath() {
|
||||||
|
import std.file;
|
||||||
string path = thisExePath();
|
string path = thisExePath();
|
||||||
int lastSlash = 0;
|
int lastSlash = 0;
|
||||||
for (int i = 0; i < path.length; i++)
|
for (int i = 0; i < path.length; i++)
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
module dlangui.widgets.popup;
|
||||||
|
|
||||||
|
import dlangui.widgets.widget;
|
||||||
|
import dlangui.widgets.layouts;
|
||||||
|
import dlangui.platforms.common.platform;
|
||||||
|
|
||||||
|
struct PopupAnchor {
|
||||||
|
Widget widget;
|
||||||
|
Align alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// popup widget container
|
||||||
|
class PopupWidget : LinearLayout {
|
||||||
|
protected PopupAnchor _anchor;
|
||||||
|
protected bool _modal;
|
||||||
|
/// access to popup anchor
|
||||||
|
@property ref PopupAnchor anchor() { return _anchor; }
|
||||||
|
/// returns true if popup is modal
|
||||||
|
bool modal() { return _modal; }
|
||||||
|
/// set modality flag
|
||||||
|
PopupWidget modal(bool modal) { _modal = modal; return this; }
|
||||||
|
|
||||||
|
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
|
||||||
|
override void measure(int parentWidth, int parentHeight) {
|
||||||
|
super.measure(parentWidth, parentHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
|
||||||
|
override void layout(Rect rc) {
|
||||||
|
if (visibility == Visibility.Gone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int w = measuredWidth;
|
||||||
|
int h = measuredHeight;
|
||||||
|
if (w > rc.width)
|
||||||
|
w = rc.width;
|
||||||
|
if (h > rc.height)
|
||||||
|
h = rc.height;
|
||||||
|
int extraw = rc.width - w;
|
||||||
|
int extrah = rc.height - h;
|
||||||
|
|
||||||
|
Rect r;
|
||||||
|
if (anchor.widget !is null)
|
||||||
|
r = anchor.widget.pos;
|
||||||
|
else
|
||||||
|
r = rc;
|
||||||
|
r.left += extraw / 2;
|
||||||
|
r.top += extrah / 2;
|
||||||
|
r.right -= extraw / 2;
|
||||||
|
r.bottom -= extrah / 2;
|
||||||
|
super.layout(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
this(Widget content, Window window) {
|
||||||
|
_window = window;
|
||||||
|
styleId = "POPUP_MENU";
|
||||||
|
addChild(content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -250,6 +250,12 @@ class Widget {
|
||||||
@property int width() { return _pos.width; }
|
@property int width() { return _pos.width; }
|
||||||
/// returns current height of widget in pixels
|
/// returns current height of widget in pixels
|
||||||
@property int height() { return _pos.height; }
|
@property int height() { return _pos.height; }
|
||||||
|
/// returns widget rectangle top position
|
||||||
|
@property int top() { return _pos.top; }
|
||||||
|
/// returns widget rectangle left position
|
||||||
|
@property int left() { return _pos.left; }
|
||||||
|
/// returns widget rectangle
|
||||||
|
@property Rect pos() { return _pos; }
|
||||||
/// returns min width constraint
|
/// returns min width constraint
|
||||||
@property int minWidth() { return style.minWidth; }
|
@property int minWidth() { return style.minWidth; }
|
||||||
/// returns max width constraint (SIZE_UNSPECIFIED if no constraint set)
|
/// returns max width constraint (SIZE_UNSPECIFIED if no constraint set)
|
||||||
|
|
Loading…
Reference in New Issue