From 692f8248e118e0961b763c0c5f18a83cd22ae9b6 Mon Sep 17 00:00:00 2001 From: Markus Gans Date: Tue, 24 Nov 2015 23:40:41 +0100 Subject: [PATCH] Improved mouse and keyboard handling in sub-menus --- ChangeLog | 3 + src/fapp.cpp | 6 +- src/fmenu.cpp | 215 ++++++++++++++++++++++++++++++++-------------- src/fmenu.h | 7 +- src/fmenubar.cpp | 1 + src/fmenuitem.cpp | 36 ++++---- src/fmenuitem.h | 10 +-- src/fmenulist.h | 2 +- 8 files changed, 188 insertions(+), 92 deletions(-) diff --git a/ChangeLog b/ChangeLog index a9810b73..9012d891 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2015-11-24 Markus Gans + * Improved mouse and keyboard handling in sub-menus + 2015-11-22 Markus Gans * Add sub-menu support diff --git a/src/fapp.cpp b/src/fapp.cpp index 57f03cbc..0e71a190 100644 --- a/src/fapp.cpp +++ b/src/fapp.cpp @@ -356,13 +356,15 @@ void FApplication::processKeyboardEvent() FKeyEvent k_press_ev (KeyPress_Event, key); sendEvent (widget, &k_press_ev); - if ( ! k_press_ev.isAccepted() && ! k_down_ev.isAccepted() ) + if ( ! open_menu + && ! k_press_ev.isAccepted() + && ! k_down_ev.isAccepted() ) { // keyboard accelerator FWidget* window = static_cast(active_window); if ( ! window ) window = getRootWidget(); - if ( window + if ( window && window->accelerator_list && ! window->accelerator_list->empty() ) { diff --git a/src/fmenu.cpp b/src/fmenu.cpp index 264c0805..4a51d8ab 100644 --- a/src/fmenu.cpp +++ b/src/fmenu.cpp @@ -242,15 +242,11 @@ void FMenu::openSubMenu (FMenu* sub_menu) //---------------------------------------------------------------------- void FMenu::hideSubMenus() { - if ( ! hasSelectedItem() ) - return; - // hide all sub-menus - if ( getSelectedItem()->hasMenu() ) + if ( open_sub_menu ) { - FMenu* m = getSelectedItem()->getMenu(); - m->hideSubMenus(); - m->hide(); + open_sub_menu->hideSubMenus(); + open_sub_menu->hide(); open_sub_menu = 0; } unselectItem(); @@ -295,6 +291,26 @@ bool FMenu::containsMenuStructure (int x, int y) const return false; } +//---------------------------------------------------------------------- +FMenu* FMenu::superMenuAt (int x, int y) const +{ + // Check mouse click position for super menu + if ( getGeometryGlobal().contains(x,y) ) + return 0; + FWidget* super = getSuperMenu(); + if ( super && isMenu(super) ) + { + if ( super->getGeometryGlobal().contains(x,y) ) + return dynamic_cast(super); + else + { + FMenu* smenu = dynamic_cast(getSuperMenu()); + return smenu->superMenuAt(x,y); + } + } + return 0; +} + //---------------------------------------------------------------------- bool FMenu::selectNextItem() { @@ -422,8 +438,11 @@ bool FMenu::hotkeyMenu (FKeyEvent*& ev) if ( found ) { unselectItem(); + hideSubMenus(); hide(); hideSuperMenus(); + updateTerminal(); + flush_out(); ev->accept(); (*iter)->processClicked(); return true; @@ -757,10 +776,10 @@ void FMenu::onKeyPress (FKeyEvent* ev) return; // looking for menu bar hotkey - FWidget* smenu = getSuperMenu(); - if ( smenu && isMenuBar(smenu)) + FWidget* menubar = menuBar(); + if ( menubar ) { - FMenuBar* mbar = reinterpret_cast(smenu); + FMenuBar* mbar = reinterpret_cast(menubar); if ( mbar->hotkeyMenu(ev) ) return; } @@ -783,17 +802,14 @@ void FMenu::onKeyPress (FKeyEvent* ev) sel_item->processClicked(); } } - ev->accept(); break; case fc::Fkey_up: selectPrevItem(); - ev->accept(); break; case fc::Fkey_down: selectNextItem(); - ev->accept(); break; case fc::Fkey_left: @@ -811,19 +827,14 @@ void FMenu::onKeyPress (FKeyEvent* ev) } else keypressMenuBar(ev); // select previous menu - ev->accept(); break; case fc::Fkey_right: if ( hasSelectedItem() && getSelectedItem()->hasMenu() ) { FMenu* sub_menu = getSelectedItem()->getMenu(); - if ( ! sub_menu->isVisible() ) - { openSubMenu (sub_menu); - ev->accept(); - } else keypressMenuBar(ev); // select next menu } @@ -853,12 +864,14 @@ void FMenu::onKeyPress (FKeyEvent* ev) statusBar()->drawMessage(); updateTerminal(); flush_out(); - ev->accept(); break; default: break; } + + // always accept key event -> no forwarding to the parent widget + ev->accept(); } //---------------------------------------------------------------------- @@ -878,6 +891,7 @@ void FMenu::onMouseDown (FMouseEvent* ev) if ( ! itemlist.empty() ) { std::vector::const_iterator iter, end; + FMenu* show_sub_menu = 0; bool focus_changed = false; FPoint mouse_pos; @@ -898,28 +912,70 @@ void FMenu::onMouseDown (FMouseEvent* ev) if ( mouse_x >= x1 && mouse_x <= x2 - && mouse_y == y - && ! (*iter)->isSelected() ) + && mouse_y == y ) { // Mouse pointer over item - FWidget* focused_widget = getFocusWidget(); - FFocusEvent out (FocusOut_Event); - FApplication::queueEvent(focused_widget, &out); if ( hasSelectedItem() ) + { + FMenuItem* sel_item = getSelectedItem(); + if ( sel_item + && sel_item->hasMenu() + && sel_item->getMenu() == open_sub_menu ) + { + if ( sel_item != *iter ) + hideSubMenus(); + else + { + open_sub_menu->unselectItem(); + raiseWindow (open_sub_menu); + open_sub_menu->redraw(); + sel_item->setFocus(); + if ( statusBar() ) + statusBar()->drawMessage(); + updateTerminal(); + flush_out(); + } + } + } + if ( ! (*iter)->isSelected() ) + { unselectItem(); - (*iter)->setSelected(); - setSelectedItem(*iter); - (*iter)->setFocus(); - if ( focused_widget ) - focused_widget->redraw(); - if ( statusBar() ) - statusBar()->drawMessage(); - focus_changed = true; + FWidget* focused_widget = getFocusWidget(); + FFocusEvent out (FocusOut_Event); + FApplication::queueEvent(focused_widget, &out); + (*iter)->setSelected(); + setSelectedItem(*iter); + (*iter)->setFocus(); + if ( focused_widget ) + focused_widget->redraw(); + if ( statusBar() ) + statusBar()->drawMessage(); + if ( (*iter)->hasMenu() ) + { + FMenu* sub_menu = (*iter)->getMenu(); + if ( ! sub_menu->isVisible() ) + show_sub_menu = sub_menu; + } + focus_changed = true; + } } ++iter; } + if ( focus_changed ) redraw(); + + if ( show_sub_menu ) + { + // open sub menu + show_sub_menu->setVisible(); + show_sub_menu->show(); + open_sub_menu = show_sub_menu; + raiseWindow (show_sub_menu); + show_sub_menu->redraw(); + updateTerminal(); + flush_out(); + } } } @@ -960,10 +1016,44 @@ void FMenu::onMouseUp (FMouseEvent* ev) && mouse_y == y ) { // Mouse pointer over item - unselectItem(); - hide(); - hideSuperMenus(); - (*iter)->processClicked(); + if ( (*iter)->hasMenu() ) + { + FMenu* sub_menu = (*iter)->getMenu(); + if ( ! sub_menu->isVisible() ) + openSubMenu (sub_menu); + else if ( open_sub_menu ) + { + if ( open_sub_menu->hasSelectedItem() ) + { + FMenuItem* sel_item = getSelectedItem(); + hideSubMenus(); + sel_item->setFocus(); + redraw(); + if ( statusBar() ) + statusBar()->drawMessage(); + updateTerminal(); + flush_out(); + } + else + { + open_sub_menu->selectFirstItem(); + open_sub_menu->getSelectedItem()->setFocus(); + open_sub_menu->redraw(); + if ( statusBar() ) + statusBar()->drawMessage(); + updateTerminal(); + flush_out(); + } + } + return; + } + else + { + unselectItem(); + hide(); + hideSuperMenus(); + (*iter)->processClicked(); + } } } ++iter; @@ -988,10 +1078,12 @@ void FMenu::onMouseMove (FMouseEvent* ev) if ( mouse_down && ! itemlist.empty() ) { std::vector::const_iterator iter, end; + FMenu* smenu = 0; bool focus_changed = false; bool mouse_over_menu = false; bool mouse_over_submenu = false; bool mouse_over_supermenu = false; + bool mouse_over_menubar = false; bool hide_sub_menu = false; FMenu* show_sub_menu = 0; FPoint mouse_pos; @@ -1013,11 +1105,18 @@ void FMenu::onMouseMove (FMouseEvent* ev) if ( isSubMenu() ) { - const FRect& supermenu_geometry = getSuperMenu()->getGeometryGlobal(); - if ( supermenu_geometry.contains(ev->getGlobalPos()) ) + smenu = superMenuAt (ev->getGlobalPos()); + if ( smenu ) mouse_over_supermenu = true; } + if ( menuBar() + && isMenuBar(menuBar()) + && menuBar()->getGeometryGlobal().contains(ev->getGlobalPos()) ) + { + mouse_over_menubar = true; + } + while ( iter != end ) { int x1, x2, y, mouse_x, mouse_y; @@ -1089,7 +1188,6 @@ void FMenu::onMouseMove (FMouseEvent* ev) else if ( ! mouse_over_menu && mouse_over_supermenu ) { // Mouse event handover to super-menu - FMenu* smenu = dynamic_cast(getSuperMenu()); const FPoint& g = ev->getGlobalPos(); const FPoint& p = smenu->globalToLocalPos(g); int b = ev->getButton(); @@ -1098,6 +1196,20 @@ void FMenu::onMouseMove (FMouseEvent* ev) setClickedWidget(smenu); smenu->onMouseMove(ev); } + else if ( mouse_over_menubar ) + { + // Mouse event handover to the menu bar + FWidget* menubar = menuBar(); + const FPoint& g = ev->getGlobalPos(); + const FPoint& p = menubar->globalToLocalPos(g); + int b = ev->getButton(); + ev = new FMouseEvent (MouseMove_Event, p, g, b); + setClickedWidget(menubar); + FMenuBar* mbar = reinterpret_cast(menubar); + mbar->mouse_down = true; + mbar->onMouseMove(ev); + delete ev; + } else if ( ! hasSelectedItem() && statusBar() && mouse_over_menu ) { // Mouse is over border or separator @@ -1112,27 +1224,10 @@ void FMenu::onMouseMove (FMouseEvent* ev) hide_sub_menu = true; } - // Mouse event handover to the menu bar - FWidget* menubar = getSuperMenu(); - if ( menubar - && isMenuBar(menubar) - && menubar->getGeometryGlobal().contains(ev->getGlobalPos()) ) - { - const FPoint& g = ev->getGlobalPos(); - const FPoint& p = menubar->globalToLocalPos(g); - int b = ev->getButton(); - ev = new FMouseEvent (MouseMove_Event, p, g, b); - setClickedWidget(menubar); - FMenuBar* mbar = reinterpret_cast(menubar); - mbar->mouse_down = true; - mbar->onMouseMove(ev); - delete ev; - } - if ( focus_changed ) redraw(); - if ( show_sub_menu ) + if ( show_sub_menu ) { // open sub menu show_sub_menu->setVisible(); @@ -1213,12 +1308,6 @@ void FMenu::setStatusbarMessage(FString msg) item->setStatusbarMessage(msg); } -//---------------------------------------------------------------------- -void FMenu::cb_menuitem_activated (FWidget*, void*) -{ - -} - //---------------------------------------------------------------------- void FMenu::cb_menuitem_toggled (FWidget* widget, void*) { diff --git a/src/fmenu.h b/src/fmenu.h index 7a0866d4..3075ea84 100644 --- a/src/fmenu.h +++ b/src/fmenu.h @@ -69,6 +69,8 @@ class FMenu : public FWindow, public FMenuList void hideSuperMenus(); bool containsMenuStructure (const FPoint&) const; bool containsMenuStructure (int, int) const; + FMenu* superMenuAt (const FPoint&) const; + FMenu* superMenuAt (int, int) const; bool selectNextItem(); bool selectPrevItem(); void keypressMenuBar (FKeyEvent*&); @@ -120,7 +122,6 @@ class FMenu : public FWindow, public FMenuList void setText (FString&); void setText (const std::string&); void setText (const char*); - void cb_menuitem_activated (FWidget*, void*); void cb_menuitem_toggled (FWidget*, void*); private: @@ -146,6 +147,10 @@ inline void FMenu::setSuperMenu (FWidget* smenu) inline bool FMenu::containsMenuStructure (const FPoint& p) const { return containsMenuStructure (p.getX(), p.getY()); } +//---------------------------------------------------------------------- +inline FMenu* FMenu::superMenuAt (const FPoint& p) const +{ return superMenuAt (p.getX(), p.getY()); } + //---------------------------------------------------------------------- inline const char* FMenu::getClassName() const { return "FMenu"; } diff --git a/src/fmenubar.cpp b/src/fmenubar.cpp index d5607e8b..aa1a276a 100644 --- a/src/fmenubar.cpp +++ b/src/fmenubar.cpp @@ -848,5 +848,6 @@ void FMenuBar::cb_item_deactivated (FWidget* widget, void*) { FMenu* menu = menuitem->getMenu(); menu->hide(); + menu->hideSubMenus(); } } diff --git a/src/fmenuitem.cpp b/src/fmenuitem.cpp index a06b43a6..7e7d757e 100644 --- a/src/fmenuitem.cpp +++ b/src/fmenuitem.cpp @@ -191,12 +191,6 @@ void FMenuItem::init (FWidget* parent) FMenu* menu_ptr = dynamic_cast(parent); if ( menu_ptr ) menu_ptr->menu_dimension(); - - this->addCallback - ( - "activate", - _METHOD_CALLBACK (parent, &FMenu::cb_menuitem_activated) - ); } } if ( hasFocus() ) @@ -656,27 +650,29 @@ void FMenuItem::unsetSelected() //---------------------------------------------------------------------- void FMenuItem::openMenu() { - FMenu* submenu; + FMenu* menu; FMenu* open_menu; if ( hasMenu() ) { - submenu = getMenu(); + menu = getMenu(); + if ( menu->isVisible() ) + return; - if ( ! submenu->isVisible() ) + open_menu = static_cast(getOpenMenu()); + if ( open_menu && open_menu != menu ) { - open_menu = static_cast(getOpenMenu()); - if ( open_menu && open_menu != submenu ) - open_menu->hide(); - setOpenMenu(submenu); - - submenu->setVisible(); - submenu->show(); - submenu->raiseWindow(submenu); - submenu->redraw(); - updateTerminal(); - flush_out(); + open_menu->hide(); + open_menu->hideSubMenus(); } + setOpenMenu(menu); + + menu->setVisible(); + menu->show(); + menu->raiseWindow(menu); + menu->redraw(); + updateTerminal(); + flush_out(); } } diff --git a/src/fmenuitem.h b/src/fmenuitem.h index c53dbcdc..11e9877b 100644 --- a/src/fmenuitem.h +++ b/src/fmenuitem.h @@ -94,7 +94,6 @@ class FMenuItem : public FWidget void onAccel (FAccelEvent*); void onFocusIn (FFocusEvent*); void onFocusOut (FFocusEvent*); - FString getText() const; // make every setEnable from FWidget available using FWidget::setEnable; bool setEnable(bool); @@ -117,6 +116,7 @@ class FMenuItem : public FWidget bool hasMenu() const; void openMenu(); uInt getTextLength() const; + FString getText() const; void setText (FString&); void setText (const std::string&); void setText (const char*); @@ -142,10 +142,6 @@ inline FWidget* FMenuItem::getSuperMenu() const inline void FMenuItem::setSuperMenu (FWidget* smenu) { super_menu = smenu; } -//---------------------------------------------------------------------- -inline FString FMenuItem::getText() const -{ return text; } - //---------------------------------------------------------------------- inline bool FMenuItem::setFocus() { return setFocus(true); } @@ -212,4 +208,8 @@ inline bool FMenuItem::hasMenu() const inline uInt FMenuItem::getTextLength() const { return text_length; } +//---------------------------------------------------------------------- +inline FString FMenuItem::getText() const +{ return text; } + #endif // _FMENUITEM_H diff --git a/src/fmenulist.h b/src/fmenulist.h index dd07485b..dcc0f4b6 100644 --- a/src/fmenulist.h +++ b/src/fmenulist.h @@ -11,7 +11,7 @@ // : *▕▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▏ // ├- - - - - - -▕ FRadioMenuItem ▏ // : ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▏ -// : +// : // : *▕▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▏ // └- - - - - - -▕ FCheckMenuItem ▏ // ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▏