diff --git a/examples/example1/src/main.d b/examples/example1/src/main.d index 12f51e0f..964256c1 100644 --- a/examples/example1/src/main.d +++ b/examples/example1/src/main.d @@ -75,6 +75,14 @@ extern (C) int UIAppMain(string[] args) { editItem.add(new Action(EditorActions.Undo, "Undo"d, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control)); editItem.add(new Action(EditorActions.Redo, "Redo"d, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control)); editItem.add(new Action(20, "Preferences..."d)); + + MenuItem editPopupItem = new MenuItem(null); + editPopupItem.add(new Action(EditorActions.Copy, "Copy"d, "edit-copy", KeyCode.KEY_C, KeyFlag.Control)); + editPopupItem.add(new Action(EditorActions.Paste, "Paste"d, "edit-paste", KeyCode.KEY_V, KeyFlag.Control)); + editPopupItem.add(new Action(EditorActions.Cut, "Cut"d, "edit-cut", KeyCode.KEY_X, KeyFlag.Control)); + editPopupItem.add(new Action(EditorActions.Undo, "Undo"d, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control)); + editPopupItem.add(new Action(EditorActions.Redo, "Redo"d, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control)); + MenuItem windowItem = new MenuItem(new Action(3, "&Window"d)); windowItem.add(new Action(30, "Preferences"d)); MenuItem helpItem = new MenuItem(new Action(4, "Help"d)); @@ -119,7 +127,7 @@ extern (C) int UIAppMain(string[] args) { hlayout.addChild((new ImageWidget()).drawableId("btn_check").padding(Rect(5,5,5,5)).alignment(Align.Center)); hlayout.addChild((new TextWidget()).text("in horizontal layout")); hlayout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5)).alignment(Align.Center)); - hlayout.addChild((new EditLine("editline", "Some text to edit"d)).alignment(Align.Center).layoutWidth(FILL_PARENT)); + hlayout.addChild((new EditLine("editline", "Some text to edit"d)).popupMenu(editPopupItem).alignment(Align.Center).layoutWidth(FILL_PARENT)); //hlayout.addChild((new Button()).text(">>")); //.textColor(0x40FF4000) hlayout.backgroundColor = 0x8080C0; layout.addChild(hlayout); @@ -278,6 +286,7 @@ extern (C) int UIAppMain(string[] args) { EditLine editLine = new EditLine("editline1", "Single line editor sample text"); editors.addChild(createEditorSettingsControl(editLine)); editors.addChild(editLine); + editLine.popupMenu = editPopupItem; // EditBox sample editors.addChild(new TextWidget(null, "EditBox: Multiline editor"d)); @@ -296,6 +305,7 @@ extern (C) int UIAppMain(string[] args) { editBox.minFontSize(12).maxFontSize(75); // allow font zoom with Ctrl + MouseWheel editors.addChild(createEditorSettingsControl(editBox)); editors.addChild(editBox); + editBox.popupMenu = editPopupItem; editors.addChild(new TextWidget(null, "EditBox: additional view for the same content (split view testing)"d)); EditBox editBox2 = new EditBox("editbox2", ""d); diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 3be91d3a..a87a684a 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -85,10 +85,12 @@ class Window { protected PopupWidget[] _popups; /// show new popup - PopupWidget showPopup(Widget content, Widget anchor = null, uint alignment = PopupAlign.Center) { + PopupWidget showPopup(Widget content, Widget anchor = null, uint alignment = PopupAlign.Center, int x = 0, int y = 0) { PopupWidget res = new PopupWidget(content, this); res.anchor.widget = anchor !is null ? anchor : _mainWidget; res.anchor.alignment = alignment; + res.anchor.x = x; + res.anchor.y = y; _popups ~= res; if (_mainWidget !is null) _mainWidget.requestLayout(); diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 0c1ce5d9..094021de 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -25,6 +25,8 @@ import dlangui.widgets.controls; import dlangui.core.signals; import dlangui.core.collections; import dlangui.platforms.common.platform; +import dlangui.widgets.menu; +import dlangui.widgets.popup; import std.algorithm; @@ -897,6 +899,36 @@ class EditWidgetBase : WidgetGroup, EditableContentListener { ]); } + protected MenuItem _popupMenu; + @property MenuItem popupMenu() { return _popupMenu; } + @property EditWidgetBase popupMenu(MenuItem popupMenu) { + _popupMenu = popupMenu; + return this; + } + /// returns true if widget can show popup (e.g. by mouse right click at point x,y) + override bool canShowPopupMenu(int x, int y) { + if (_popupMenu is null) + return false; + if (_popupMenu.onBeforeOpeningSubmenu.assigned) + if (!_popupMenu.onBeforeOpeningSubmenu(_popupMenu)) + return false; + return true; + } + /// shows popup at (x,y) + override void showPopupMenu(int x, int y) { + /// if preparation signal handler assigned, call it; don't show popup if false is returned from handler + if (_popupMenu.onBeforeOpeningSubmenu.assigned) + if (!_popupMenu.onBeforeOpeningSubmenu(_popupMenu)) + return; + PopupMenu popupMenu = new PopupMenu(_popupMenu); + PopupWidget popup = window.showPopup(popupMenu, this, PopupAlign.Point | PopupAlign.Right, x, y); + popup.flags = PopupFlags.CloseOnClickOutside; + } + + void onPopupMenuItem(MenuItem item) { + // TODO + } + /// when true, Tab / Shift+Tab presses are processed internally in widget (e.g. insert tab character) instead of focus change navigation. @property bool wantTabs() { return _wantTabs; @@ -1630,7 +1662,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener { return handleAction(new Action(EditorActions.ScrollLineUp)); } } - return false; + return super.onMouseEvent(event); } diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index b3a2789c..7be67ba4 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -97,6 +97,12 @@ class MenuItem { @property const(Action) action() const { return _action; } /// sets item action @property MenuItem action(Action a) { _action = a; return this; } + + /// handle menu item click + Signal!(void, MenuItem) onMenuItem; + /// prepare for opening of submenu, return true if opening is allowed + Signal!(bool, MenuItem) onBeforeOpeningSubmenu; + this() { _enabled = true; } diff --git a/src/dlangui/widgets/popup.d b/src/dlangui/widgets/popup.d index d2811f32..1cc71e37 100644 --- a/src/dlangui/widgets/popup.d +++ b/src/dlangui/widgets/popup.d @@ -31,11 +31,15 @@ enum PopupAlign : uint { /// place popup below anchor widget close to lower bound Below = 2, /// place popup below anchor widget close to right bound (when no space enough, align near left bound) - Right = 4, + Right = 4, + /// align to specified point + Point = 8, } struct PopupAnchor { - Widget widget; + Widget widget; + int x; + int y; uint alignment = PopupAlign.Center; } @@ -105,17 +109,22 @@ class PopupWidget : LinearLayout { Rect r; Point anchorPt; - if (anchor.alignment & PopupAlign.Center) { - // center around center of anchor widget - r.left = anchorrc.middlex - w / 2; - r.top = anchorrc.middley - h / 2; - } else if (anchor.alignment & PopupAlign.Below) { - r.left = anchorrc.left; - r.top = anchorrc.bottom; - } else if (anchor.alignment & PopupAlign.Right) { - r.left = anchorrc.right; - r.top = anchorrc.top; - } + if (anchor.alignment & PopupAlign.Point) { + r.left = anchor.x; + r.top = anchor.y; + } else { + if (anchor.alignment & PopupAlign.Center) { + // center around center of anchor widget + r.left = anchorrc.middlex - w / 2; + r.top = anchorrc.middley - h / 2; + } else if (anchor.alignment & PopupAlign.Below) { + r.left = anchorrc.left; + r.top = anchorrc.bottom; + } else if (anchor.alignment & PopupAlign.Right) { + r.left = anchorrc.right; + r.top = anchorrc.top; + } + } r.right = r.left + w; r.bottom = r.top + h; r.moveToFit(rc); diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 84dfedcd..75c8e2f5 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -866,6 +866,12 @@ class Widget { return true; } } + if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Right) { + if (canShowPopupMenu(event.x, event.y)) { + showPopupMenu(event.x, event.y); + return true; + } + } if (canFocus && event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) { setFocus(); return true; @@ -1034,6 +1040,17 @@ class Widget { } } + // =========================================================== + // popup menu support + /// returns true if widget can show popup menu (e.g. by mouse right click at point x,y) + bool canShowPopupMenu(int x, int y) { + return false; + } + /// shows popup menu at (x,y) + void showPopupMenu(int x, int y) { + // override to show popup + } + // =========================================================== // Widget hierarhy methods