diff --git a/src/dlangui/core/events.d b/src/dlangui/core/events.d index 68388783..8e9a0ff3 100644 --- a/src/dlangui/core/events.d +++ b/src/dlangui/core/events.d @@ -134,6 +134,7 @@ class MouseEvent { protected ButtonDetails _lbutton; protected ButtonDetails _mbutton; protected ButtonDetails _rbutton; + protected bool _doNotTrackButtonDown; @property ref ButtonDetails lbutton() { return _lbutton; } @property ref ButtonDetails rbutton() { return _rbutton; } @property ref ButtonDetails mbutton() { return _mbutton; } @@ -146,6 +147,8 @@ class MouseEvent { @property short y() { return _y; } /// get event tracking widget to override @property Widget trackingWidget() { return _trackingWidget; } + @property bool doNotTrackButtonDown() { return _doNotTrackButtonDown; } + @property void doNotTrackButtonDown(bool flg) { _doNotTrackButtonDown = flg; } /// override mouse tracking widget void track(Widget w) { _trackingWidget = w; diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 9390401d..e43b8a56 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -191,7 +191,7 @@ class Window { // if not processed by children, offer event to root if (sendAndCheckOverride(root, event)) { Log.d("MouseEvent is processed"); - if (event.action == MouseAction.ButtonDown && _mouseCaptureWidget is null) { + if (event.action == MouseAction.ButtonDown && _mouseCaptureWidget is null && !event.doNotTrackButtonDown) { Log.d("Setting active widget"); setCaptureWidget(root, event); } else if (event.action == MouseAction.Move) { @@ -263,6 +263,8 @@ class Window { } protected bool sendAndCheckOverride(Widget widget, MouseEvent event) { + if (!isChild(widget)) + return false; bool res = widget.onMouseEvent(event); if (event.trackingWidget !is null && _mouseCaptureWidget !is event.trackingWidget) { setCaptureWidget(event.trackingWidget, event); diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index 57e19236..7788e892 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -87,6 +87,9 @@ class ListWidget : WidgetGroup, OnScrollHandler { /// when true, mouse hover selects underlying item @property ListWidget selectOnHover(bool select) { _selectOnHover = select; return this; } + /// if true, generate itemClicked on mouse down instead mouse up event + protected bool _clickOnButtonDown; + /// returns rectangle for item (not scrolled, first item starts at 0,0) Rect itemRectNoScroll(int index) { Rect res; @@ -183,6 +186,9 @@ class ListWidget : WidgetGroup, OnScrollHandler { /// override to handle change of selection protected void selectionChanged(int index, int previouslySelectedItem = -1, MouseEvent event = null) { } + /// override to handle mouse up on item + protected void itemClicked(int index) { + } protected void selectItem(int index) { if (_selectedItemIndex == index) @@ -487,14 +493,22 @@ class ListWidget : WidgetGroup, OnScrollHandler { itemrc.bottom += rc.top - scrollOffset.y; if (itemrc.isPointInside(Point(event.x, event.y))) { if ((event.flags & (MouseFlag.LButton || MouseFlag.RButton)) || _selectOnHover) { - if (_selectedItemIndex == i) - return true; - int prevSelection = _selectedItemIndex; - selectItem(i); - setHoverItem(-1); - selectionChanged(_selectedItemIndex, prevSelection, event); + if (_selectedItemIndex != i) { + int prevSelection = _selectedItemIndex; + selectItem(i); + setHoverItem(-1); + selectionChanged(_selectedItemIndex, prevSelection, event); + } } else setHoverItem(i); + if ((event.button == MouseFlag.LButton || event.button == MouseFlag.RButton)) { + if ((_clickOnButtonDown && event.action == MouseAction.ButtonDown) || (!_clickOnButtonDown && event.action == MouseAction.ButtonUp)) { + itemClicked(i); + if (_clickOnButtonDown) + event.doNotTrackButtonDown = true; + } + } + return true; } } return true; diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index 4eb42b9f..28cc9354 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -80,11 +80,13 @@ class MenuItemWidget : HorizontalLayout { } class MenuWidgetBase : ListWidget { + protected MenuWidgetBase _parentMenu; protected MenuItem _item; protected PopupMenu _openedMenu; protected PopupWidget _openedPopup; - this(MenuItem item, Orientation orientation) { + this(MenuWidgetBase parentMenu, MenuItem item, Orientation orientation) { + _parentMenu = parentMenu; _item = item; this.orientation = orientation; id = "popup_menu"; @@ -99,39 +101,98 @@ class MenuWidgetBase : ListWidget { ownAdapter = adapter; } + protected void openSubmenu(MenuItemWidget itemWidget) { + if (_openedPopup !is null) + _openedPopup.close(); + PopupMenu popupMenu = new PopupMenu(itemWidget.item, this); + PopupWidget popup = window.showPopup(popupMenu, itemWidget, PopupAlign.Below); + //if (event !is null && (event.flags & (MouseFlag.LButton || MouseFlag.RButton))) + // event.track(popupMenu); + _openedPopup = popup; + _openedMenu = popupMenu; + } + /// override to handle change of selection override protected void selectionChanged(int index, int previouslySelectedItem = -1, MouseEvent event = null) { MenuItemWidget itemWidget = index >= 0 ? cast(MenuItemWidget)_adapter.itemWidget(index) : null; - if (itemWidget.item.isSubmenu()) { - if (_openedPopup !is null) - _openedPopup.close(); - PopupMenu popupMenu = new PopupMenu(itemWidget.item); - PopupWidget popup = window.showPopup(popupMenu, itemWidget, PopupAlign.Below); - if (event !is null && (event.flags & (MouseFlag.LButton || MouseFlag.RButton))) - event.track(popupMenu); - _openedPopup = popup; - _openedMenu = popupMenu; - selectOnHover = true; - } else { - // normal item - } + if (itemWidget !is null) { + if (itemWidget.item.isSubmenu()) { + if (_selectOnHover) { + openSubmenu(itemWidget); + } + + } else { + // normal item + } + } } + + protected bool delegate(MenuItem item) _onMenuItemClickListener; + @property bool delegate(MenuItem item) onMenuItemListener() { return _onMenuItemClickListener; } + @property MenuWidgetBase onMenuItemListener(bool delegate(MenuItem item) listener) { _onMenuItemClickListener = listener; return this; } + + protected void onMenuItem(MenuItem item) { + if (_openedPopup !is null) { + _openedPopup.close(); + _openedPopup = null; + } + if (_parentMenu !is null) + _parentMenu.onMenuItem(item); + else { + // top level handling + Log.d("onMenuItem ", item.id); + selectItem(-1); + selectOnHover = false; + bool delegate(MenuItem item) listener = _onMenuItemClickListener; + PopupWidget popup = cast(PopupWidget)parent; + if (popup) + popup.close(); + // this pointer now can be invalid - if popup removed + if (listener !is null) + listener(item); + } + } + + /// override to handle mouse up on item + override protected void itemClicked(int index) { + MenuItemWidget itemWidget = index >= 0 ? cast(MenuItemWidget)_adapter.itemWidget(index) : null; + if (itemWidget !is null) { + Log.d("Menu Item clicked ", itemWidget.item.action.id); + if (itemWidget.item.isSubmenu()) { + // submenu clicked + if (_clickOnButtonDown && _openedPopup !is null && _openedMenu._item is itemWidget.item) { + // second click on main menu opened item + _openedPopup.close(); + _openedPopup = null; + selectItem(-1); + selectOnHover = false; + } else { + openSubmenu(itemWidget); + selectOnHover = true; + } + } else { + // normal item + onMenuItem(itemWidget.item); + } + } + } } class MainMenu : MenuWidgetBase { this(MenuItem item) { - super(item, Orientation.Horizontal); + super(null, item, Orientation.Horizontal); id = "MAIN_MENU"; styleId = "MAIN_MENU"; + _clickOnButtonDown = true; } } class PopupMenu : MenuWidgetBase { - this(MenuItem item) { - super(item, Orientation.Vertical); + this(MenuItem item, MenuWidgetBase parentMenu = null) { + super(parentMenu, item, Orientation.Vertical); id = "POPUP_MENU"; styleId = "POPUP_MENU"; selectOnHover = true;