diff --git a/dlanguilib.visualdproj b/dlanguilib.visualdproj
index 8b3bac5d..61c775da 100644
--- a/dlanguilib.visualdproj
+++ b/dlanguilib.visualdproj
@@ -329,6 +329,7 @@
+
diff --git a/examples/example1/main.d b/examples/example1/main.d
index e3e3190c..a9ac975b 100644
--- a/examples/example1/main.d
+++ b/examples/example1/main.d
@@ -132,6 +132,7 @@ extern (C) int UIAppMain(string[] args) {
window.mainWidget = (new Button()).text("sample button");
}
window.show();
+ window.showPopup((new TextWidget()).text("POPUP"d));
window.windowCaption = "New Window Caption";
// run message loop
diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d
index d252ea12..b38e5a74 100644
--- a/src/dlangui/platforms/common/platform.d
+++ b/src/dlangui/platforms/common/platform.d
@@ -2,8 +2,9 @@ module dlangui.platforms.common.platform;
public import dlangui.core.events;
import dlangui.widgets.widget;
+import dlangui.widgets.popup;
import dlangui.graphics.drawbuf;
-import std.file;
+
private import dlangui.graphics.gldrawbuf;
class Window {
@@ -26,6 +27,21 @@ class Window {
abstract void show();
abstract @property string windowCaption();
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) {
if (_dx == width && _dy == height)
return;
@@ -34,16 +50,53 @@ class Window {
if (_mainWidget !is null) {
Log.d("onResize ", _dx, "x", _dy);
long measureStart = currentTimeMillis;
- _mainWidget.measure(_dx, _dy);
+ measure();
long measureEnd = currentTimeMillis;
Log.d("measure took ", measureEnd - measureStart, " ms");
- _mainWidget.layout(Rect(0, 0, _dx, _dy));
+ layout();
long layoutEnd = currentTimeMillis;
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() {
_backgroundColor = 0xFFFFFF;
@@ -51,11 +104,16 @@ class Window {
~this() {
if (_mainWidget !is null) {
destroy(_mainWidget);
- _mainWidget = null;
+ _mainWidget = null;
}
+ foreach(p; _popups)
+ destroy(p);
+ _popups = null;
}
private void animate(Widget root, long interval) {
+ if (root is null)
+ return;
if (root.visibility != Visibility.Visible)
return;
for (int i = 0; i < root.childCount; i++)
@@ -64,38 +122,46 @@ class Window {
root.animate(interval);
}
+ private void animate(long interval) {
+ animate(_mainWidget, interval);
+ foreach(p; _popups)
+ p.animate(interval);
+ }
+
void onDraw(DrawBuf buf) {
- if (_mainWidget !is null) {
- bool needDraw = false;
- bool needLayout = false;
- bool animationActive = false;
+ bool needDraw = false;
+ bool needLayout = false;
+ bool animationActive = false;
+ checkUpdateNeeded(needDraw, needLayout, animationActive);
+ if (needLayout || animationActive)
+ needDraw = true;
+ long ts = std.datetime.Clock.currStdTime;
+ if (animationActive && lastDrawTs != 0) {
+ animate(ts - lastDrawTs);
+ // layout required flag could be changed during animate - check again
checkUpdateNeeded(needDraw, needLayout, animationActive);
- if (needLayout || animationActive)
- needDraw = true;
- long ts = std.datetime.Clock.currStdTime;
- if (animationActive && lastDrawTs != 0) {
- animate(_mainWidget, ts - lastDrawTs);
- // layout required flag could be changed during animate - check again
- checkUpdateNeeded(needDraw, needLayout, animationActive);
- }
- if (needLayout) {
- long measureStart = currentTimeMillis;
- _mainWidget.measure(_dx, _dy);
- long measureEnd = currentTimeMillis;
- Log.d("measure took ", measureEnd - measureStart, " ms");
- _mainWidget.layout(Rect(0, 0, _dx, _dy));
- long layoutEnd = currentTimeMillis;
- Log.d("layout took ", layoutEnd - measureEnd, " ms");
- //checkUpdateNeeded(needDraw, needLayout, animationActive);
- }
- long drawStart = currentTimeMillis;
- _mainWidget.onDraw(buf);
- long drawEnd = currentTimeMillis;
- Log.d("draw took ", drawEnd - drawStart, " ms");
- lastDrawTs = ts;
- if (animationActive)
- scheduleAnimation();
}
+ if (needLayout) {
+ long measureStart = currentTimeMillis;
+ measure();
+ long measureEnd = currentTimeMillis;
+ Log.d("measure took ", measureEnd - measureStart, " ms");
+ layout();
+ long layoutEnd = currentTimeMillis;
+ Log.d("layout took ", layoutEnd - measureEnd, " ms");
+ //checkUpdateNeeded(needDraw, needLayout, animationActive);
+ }
+ long drawStart = currentTimeMillis;
+ // draw main widget
+ _mainWidget.onDraw(buf);
+ // draw popups
+ foreach(p; _popups)
+ p.onDraw(buf);
+ long drawEnd = currentTimeMillis;
+ Log.d("draw took ", drawEnd - drawStart, " ms");
+ lastDrawTs = ts;
+ if (animationActive)
+ scheduleAnimation();
}
/// after drawing, call to schedule redraw if animation is active
@@ -149,7 +215,7 @@ class Window {
bool res = false;
for(int i = _mouseTrackingWidgets.length - 1; i >=0; i--) {
Widget w = _mouseTrackingWidgets[i];
- if (!_mainWidget.isChild(w)) {
+ if (!isChild(w)) {
// std.algorithm.remove does not work for me
//_mouseTrackingWidgets.remove(i);
for (int j = i; j < _mouseTrackingWidgets.length - 1; j++)
@@ -197,7 +263,7 @@ class Window {
return false;
// 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;
//Log.d("dispatchMouseEvent ", event.action, " (", event.x, ",", event.y, ")");
@@ -265,6 +331,8 @@ class Window {
/// checks content widgets for necessary redraw and/or layout
protected void checkUpdateNeeded(Widget root, ref bool needDraw, ref bool needLayout, ref bool animationActive) {
+ if (root is null)
+ return;
if (!root.visibility == Visibility.Visible)
return;
needDraw = root.needDraw || needDraw;
@@ -284,6 +352,8 @@ class Window {
if (_mainWidget is null)
return false;
checkUpdateNeeded(_mainWidget, needDraw, needLayout, animationActive);
+ foreach(p; _popups)
+ checkUpdateNeeded(p, 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).
@@ -334,6 +404,7 @@ version (Windows) {
/// returns current executable path only, including last path delimiter
string exePath() {
+ import std.file;
string path = thisExePath();
int lastSlash = 0;
for (int i = 0; i < path.length; i++)
diff --git a/src/dlangui/widgets/popup.d b/src/dlangui/widgets/popup.d
new file mode 100644
index 00000000..009f0b57
--- /dev/null
+++ b/src/dlangui/widgets/popup.d
@@ -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);
+ }
+}
diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d
index 8ac0a2cd..968df485 100644
--- a/src/dlangui/widgets/widget.d
+++ b/src/dlangui/widgets/widget.d
@@ -250,6 +250,12 @@ class Widget {
@property int width() { return _pos.width; }
/// returns current height of widget in pixels
@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
@property int minWidth() { return style.minWidth; }
/// returns max width constraint (SIZE_UNSPECIFIED if no constraint set)