From 321e6576615fbd001e38a24b4ab6e27ec9fdd24b Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 15 Apr 2014 11:13:49 +0400 Subject: [PATCH] popup and menu improvements --- src/dlangui/platforms/common/platform.d | 18 ++++++++++-- src/dlangui/widgets/menu.d | 11 ++++++- src/dlangui/widgets/popup.d | 39 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index e43b8a56..1668aca6 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -78,6 +78,7 @@ class Window { for (int j = i; j < _popups.length - 1; j++) _popups[j] = _popups[j + 1]; _popups.length--; + p.onClose(); destroy(p); // force redraw _mainWidget.invalidate(); @@ -340,14 +341,25 @@ class Window { processed = checkRemoveTracking(event); } if (!res) { + bool insideOneOfPopups = false; for (int i = cast(int)_popups.length - 1; i >= 0; i--) { auto p = _popups[i]; - if (dispatchMouseEvent(p, event)) - return true; + if (p.isPointInside(event.x, event.y)) + insideOneOfPopups = true; + } + for (int i = cast(int)_popups.length - 1; i >= 0; i--) { + auto p = _popups[i]; + if (!insideOneOfPopups) { + if (p.onMouseEventOutside(event)) // stop loop when true is returned, but allow other main widget to handle event + break; + } else { + if (dispatchMouseEvent(p, event)) + return true; + } } res = dispatchMouseEvent(_mainWidget, event); } - return res || processed; + return res || processed || _mainWidget.needDraw; } /// checks content widgets for necessary redraw and/or layout diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index fd096058..44110205 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -102,11 +102,20 @@ class MenuWidgetBase : ListWidget { ownAdapter = adapter; } + protected void onPopupClosed(PopupWidget p) { + _openedPopup = null; + _openedMenu = null; + selectItem(-1); + } + protected void openSubmenu(MenuItemWidget itemWidget) { - if (_openedPopup !is null) + if (_openedPopup !is null) { _openedPopup.close(); + } PopupMenu popupMenu = new PopupMenu(itemWidget.item, this); PopupWidget popup = window.showPopup(popupMenu, itemWidget, PopupAlign.Below); + popup.onPopupCloseListener = &onPopupClosed; + popup.flags = PopupFlags.CloseOnClickOutside; _openedPopup = popup; _openedMenu = popupMenu; } diff --git a/src/dlangui/widgets/popup.d b/src/dlangui/widgets/popup.d index 0f65b699..cba6018d 100644 --- a/src/dlangui/widgets/popup.d +++ b/src/dlangui/widgets/popup.d @@ -17,10 +17,29 @@ struct PopupAnchor { uint alignment = PopupAlign.Center; } +/// popup behavior flags - for PopupWidget.flags property +enum PopupFlags : uint { + /// close popup when mouse button clicked outside of its bounds + CloseOnClickOutside = 1, +} + /// popup widget container class PopupWidget : LinearLayout { protected PopupAnchor _anchor; protected bool _modal; + + protected uint _flags; + protected void delegate(PopupWidget popup) _onPopupCloseListener; + /// popup close listener (called right before closing) + @property void delegate(PopupWidget popup) onPopupCloseListener() { return _onPopupCloseListener; } + /// set popup close listener (to call right before closing) + @property PopupWidget onPopupCloseListener(void delegate(PopupWidget popup) listener) { _onPopupCloseListener = listener; return this; } + + /// returns popup behavior flags (combination of PopupFlags) + @property uint flags() { return _flags; } + /// set popup behavior flags (combination of PopupFlags) + @property PopupWidget flags(uint flags) { _flags = flags; return this; } + /// access to popup anchor @property ref PopupAnchor anchor() { return _anchor; } /// returns true if popup is modal @@ -37,6 +56,12 @@ class PopupWidget : LinearLayout { window.removePopup(this); } + /// just call on close listener + void onClose() { + if (_onPopupCloseListener !is null) + _onPopupCloseListener(this); + } + /// 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) { @@ -77,4 +102,18 @@ class PopupWidget : LinearLayout { //styleId = "POPUP_MENU"; addChild(content); } + + /// called for mouse activity outside shown popup bounds + bool onMouseEventOutside(MouseEvent event) { + if (visibility != Visibility.Visible) + return false; + if (_flags & PopupFlags.CloseOnClickOutside) { + if (event.action == MouseAction.ButtonDown) { + // clicked outside - close popup + close(); + return false; + } + } + return false; + } }