From 8f3fab4bf5701c8e1115d809def45a93d18cd753 Mon Sep 17 00:00:00 2001 From: Markus Gans Date: Wed, 4 Nov 2015 00:14:23 +0100 Subject: [PATCH] Improve keyboard shortcut handling in menus --- ChangeLog | 3 ++ src/fmenu.cpp | 111 ++++++++++++++++++++++++---------------------- src/fmenu.h | 3 +- src/fmenubar.cpp | 111 +++++++++++++++++++++++++++++++++++++++------- src/fmenubar.h | 3 ++ src/fmenuitem.cpp | 78 ++++++++++++++++---------------- src/fmenulist.cpp | 5 --- 7 files changed, 203 insertions(+), 111 deletions(-) diff --git a/ChangeLog b/ChangeLog index a537fadc..71cd0124 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2015-11-03 Markus Gans + * Improve keyboard shortcut handling in menus + 2015-11-01 Markus Gans * First working version of an application menu, it uses the new classes FMenuBar, FMenu and FMenuItem diff --git a/src/fmenu.cpp b/src/fmenu.cpp index d00de056..4deb2c14 100644 --- a/src/fmenu.cpp +++ b/src/fmenu.cpp @@ -115,9 +115,9 @@ void FMenu::init(FWidget* parent) { if ( isMenuBar(parent) ) { - FMenuBar* mb = dynamic_cast(parent); - if ( mb ) - mb->menu_dimension(); + FMenuBar* mbar = dynamic_cast(parent); + if ( mbar ) + mbar->menu_dimension(); } setSuperMenu(parent); } @@ -201,15 +201,15 @@ void FMenu::hideSuperMenus() { if ( isMenuBar(super) ) { - FMenuBar* mb = reinterpret_cast(super); - FMenuItem* selectedMenuItem = mb->selectedMenuItem; + FMenuBar* mbar = reinterpret_cast(super); + FMenuItem* selectedMenuItem = mbar->selectedMenuItem; if ( selectedMenuItem ) { selectedMenuItem->unsetSelected(); selectedMenuItem = 0; - mb->mouse_down = false; - mb->redraw(); + mbar->mouse_down = false; + mbar->redraw(); } } else if ( isMenu(super) ) @@ -321,19 +321,57 @@ bool FMenu::selectPrevItem() } //---------------------------------------------------------------------- -void FMenu::keypressMenuBar (FKeyEvent* ev) +void FMenu::keypressMenuBar (FKeyEvent*& ev) { FWidget* super = getSuperMenu(); if ( super ) { if ( isMenuBar(super) ) { - FMenuBar* mb = reinterpret_cast(super); - mb->onKeyPress(ev); + FMenuBar* mbar = reinterpret_cast(super); + mbar->onKeyPress(ev); } } } +//---------------------------------------------------------------------- +bool FMenu::hotkeyMenu (FKeyEvent*& ev) +{ + std::vector::const_iterator iter, end; + iter = itemlist.begin(); + end = itemlist.end(); + + while ( iter != end ) + { + if ( (*iter)->hasHotkey() ) + { + bool found = false; + int hotkey = (*iter)->getHotkey(); + int key = ev->key(); + + if ( isalpha(hotkey) || isdigit(hotkey) ) + { + if ( tolower(hotkey) == key || toupper(hotkey) == key ) + found = true; + } + else if ( hotkey == key ) + found = true; + + if ( found ) + { + unselectItemInList(); + hide(); + hideSuperMenus(); + ev->accept(); + (*iter)->processClicked(); + return true; + } + } + ++iter; + } + return false; +} + //---------------------------------------------------------------------- int FMenu::getHotkeyPos (wchar_t*& src, wchar_t*& dest, uInt length) { @@ -554,16 +592,6 @@ void FMenu::drawItems() setReverse(true); delete[] item_text; } - /*if ( is_selected && statusBar() ) - { - FString msg = (*iter)->getStatusbarMessage(); - FString curMsg = statusBar()->getMessage(); - if ( curMsg != msg ) - { - statusBar()->setMessage(msg); - statusBar()->drawMessage(); - } - }*/ ++iter; y++; } @@ -597,38 +625,17 @@ void FMenu::processActivate() //---------------------------------------------------------------------- void FMenu::onKeyPress (FKeyEvent* ev) { - // looking for a hotkey - std::vector::const_iterator iter, end; - iter = itemlist.begin(); - end = itemlist.end(); + // looking for menu hotkey + if ( hotkeyMenu(ev) ) + return; - while ( iter != end ) + // looking for menu bar hotkey + FWidget* smenu = getSuperMenu(); + if ( smenu && isMenuBar(smenu)) { - if ( (*iter)->hasHotkey() ) - { - bool found = false; - int hotkey = (*iter)->getHotkey(); - int key = ev->key(); - - if ( isalpha(hotkey) || isdigit(hotkey) ) - { - if ( tolower(hotkey) == key || toupper(hotkey) == key ) - found = true; - } - else if ( hotkey == key ) - found = true; - - if ( found ) - { - unselectItemInList(); - hide(); - hideSuperMenus(); - ev->accept(); - (*iter)->processClicked(); - return; - } - } - ++iter; + FMenuBar* mbar = reinterpret_cast(smenu); + if ( mbar->hotkeyMenu(ev) ) + return; } switch ( ev->key() ) @@ -911,8 +918,8 @@ void FMenu::onMouseMove (FMouseEvent* ev) int b = ev->getButton(); ev = new FMouseEvent (MouseMove_Event, p, g, b); setClickedWidget(menubar); - FMenuBar* sm = reinterpret_cast(menubar); - sm->onMouseDown(ev); + FMenuBar* mbar = reinterpret_cast(menubar); + mbar->onMouseDown(ev); delete ev; } diff --git a/src/fmenu.h b/src/fmenu.h index 4db2d0b4..43f8d3ee 100644 --- a/src/fmenu.h +++ b/src/fmenu.h @@ -67,7 +67,8 @@ class FMenu : public FWindow, public FMenuList bool containsMenuStructure (int, int) const; bool selectNextItem(); bool selectPrevItem(); - void keypressMenuBar (FKeyEvent*); + void keypressMenuBar (FKeyEvent*&); + bool hotkeyMenu (FKeyEvent*&); int getHotkeyPos (wchar_t*&, wchar_t*&, uInt); void draw(); void drawBorder(); diff --git a/src/fmenubar.cpp b/src/fmenubar.cpp index 4d8b01a8..70076845 100644 --- a/src/fmenubar.cpp +++ b/src/fmenubar.cpp @@ -47,6 +47,7 @@ void FMenuBar::init() setGeometry (1, 1, getColumnNumber(), 1, false); getRootWidget()->setTopPadding(1, true); setMenuBar(this); + addAccelerator (fc::Fkey_f10, this); foregroundColor = wc.menu_active_fg; backgroundColor = wc.menu_active_bg; window_object = true; @@ -191,6 +192,60 @@ bool FMenuBar::selectPrevItem() return true; } +//---------------------------------------------------------------------- +bool FMenuBar::hotkeyMenu (FKeyEvent*& ev) +{ + std::vector::const_iterator iter, end; + iter = itemlist.begin(); + end = itemlist.end(); + + while ( iter != end ) + { + if ( (*iter)->isEnabled() ) + { + int hotkey = (*iter)->getHotkey(); + int key = ev->key(); + + if ( 0x20000e0+tolower(hotkey) == key ) + { + FMenuItem* sel_item = getSelectedMenuItem(); + + if ( sel_item && sel_item->hasMenu() ) + sel_item->getMenu()->unselectItemInList(); + + unselectItemInMenu(); + + if ( (*iter)->hasMenu() ) + { + FMenuItem* first_item; + FMenu* menu = (*iter)->getMenu(); + (*iter)->setSelected(); + (*iter)->setFocus(); + menu->selectFirstItemInList(); + first_item = menu->getSelectedListItem(); + if ( first_item ) + first_item->setFocus(); + menu->redraw(); + if ( statusBar() ) + statusBar()->drawMessage(); + selectedMenuItem = *iter; + redraw(); + } + else + { + selectedMenuItem = 0; + redraw(); + (*iter)->processClicked(); + } + ev->accept(); + return true; + } + } + ++iter; + } + return false; +} + //---------------------------------------------------------------------- int FMenuBar::getHotkeyPos (wchar_t*& src, wchar_t*& dest, uInt length) { @@ -369,17 +424,6 @@ void FMenuBar::drawItems() setReverse(false); setUpdateVTerm(true); -/* - if ( hasSelectedMenuItem() && statusBar() ) - { - FString msg = getSelectedMenuItem()->getStatusbarMessage(); - FString curMsg = statusBar()->getMessage(); - if ( curMsg != msg ) - { - statusBar()->setMessage(msg); - statusBar()->drawMessage(); - } - }*/ } //---------------------------------------------------------------------- @@ -467,8 +511,7 @@ void FMenuBar::onMouseDown (FMouseEvent* ev) end = itemlist.end(); mouse_x = ev->getX(); mouse_y = ev->getY(); -//FMessageBox::info (this, "Info", FString().sprintf("local(%d,%d) global(%d,%d)", ev->getX(),ev->getY(),ev->getGlobalX(), ev->getGlobalY())); -// #include "fmessagebox.h" + while ( iter != end ) { int x1, x2; @@ -486,9 +529,7 @@ void FMenuBar::onMouseDown (FMouseEvent* ev) FFocusEvent out (FocusOut_Event); FApplication::queueEvent(focused_widget, &out); (*iter)->setSelected(); -//FMessageBox::info (this, "Info", (*iter)->getStatusbarMessage()); (*iter)->setFocus(); -//FMessageBox::info (this, "Info", statusBar()->getMessage()); selectedMenuItem = *iter; focus_changed = true; if ( focused_widget ) @@ -684,6 +725,20 @@ void FMenuBar::onMouseMove (FMouseEvent* ev) } } +//---------------------------------------------------------------------- +void FMenuBar::onAccel (FAccelEvent* ev) +{ + if ( ! hasSelectedMenuItem() ) + { + selectFirstItemInMenu(); + getSelectedMenuItem()->setFocus(); + if ( statusBar() ) + statusBar()->drawMessage(); + redraw(); + } + ev->accept(); +} + //---------------------------------------------------------------------- void FMenuBar::hide() { @@ -706,6 +761,32 @@ void FMenuBar::hide() delete[] blank; } +//---------------------------------------------------------------------- +void FMenuBar::selectFirstItemInMenu() +{ + std::vector::const_iterator iter, end; + iter = itemlist.begin(); + end = itemlist.end(); + + if ( itemlist.empty() ) + return; + + if ( hasSelectedMenuItem() ) + unselectItemInMenu(); + + while ( iter != end ) + { + if ( (*iter)->isEnabled() && ! (*iter)->isSeparator() ) + { + // select first enabled item + (*iter)->setSelected(); + selectedMenuItem = *iter; + break; + } + ++iter; + } +} + //---------------------------------------------------------------------- void FMenuBar::unselectItemInMenu() { diff --git a/src/fmenubar.h b/src/fmenubar.h index 28606879..e67907c3 100644 --- a/src/fmenubar.h +++ b/src/fmenubar.h @@ -56,6 +56,7 @@ class FMenuBar : public FWindow, public FMenuList bool isMenu (FMenuItem*) const; bool selectNextItem(); bool selectPrevItem(); + bool hotkeyMenu (FKeyEvent*&); int getHotkeyPos (wchar_t*&, wchar_t*&, uInt); void draw(); void drawItems(); @@ -70,7 +71,9 @@ class FMenuBar : public FWindow, public FMenuList void onMouseDown (FMouseEvent*); void onMouseUp (FMouseEvent*); void onMouseMove (FMouseEvent*); + void onAccel (FAccelEvent*); void hide(); + void selectFirstItemInMenu(); void unselectItemInMenu(); FMenuItem* getSelectedMenuItem() const; bool hasSelectedMenuItem() const; diff --git a/src/fmenuitem.cpp b/src/fmenuitem.cpp index 5e820f05..1d5f0bda 100644 --- a/src/fmenuitem.cpp +++ b/src/fmenuitem.cpp @@ -211,16 +211,18 @@ void FMenuItem::onKeyPress (FKeyEvent* ev) { if ( isMenu(super_menu) ) { - FMenu* sm = dynamic_cast(super_menu); - if ( sm ) - sm->onKeyPress(ev); + FMenu* smenu = dynamic_cast(super_menu); + if ( smenu ) + smenu->onKeyPress(ev); } if ( isMenuBar(super_menu) ) { - FMenuBar* mb = dynamic_cast(super_menu); - if ( mb ) - mb->onKeyPress(ev); + FMenuBar* mbar = dynamic_cast(super_menu); + if ( mbar->hotkeyMenu(ev) ) + return; + if ( mbar ) + mbar->onKeyPress(ev); } } } @@ -235,24 +237,24 @@ void FMenuItem::onMouseDown (FMouseEvent* ev) if ( isMenu(super_menu) ) { - FMenu* sm = dynamic_cast(super_menu); - if ( sm ) + FMenu* smenu = dynamic_cast(super_menu); + if ( smenu ) { - const FPoint& p2 = sm->globalToLocalPos(g); + const FPoint& p2 = smenu->globalToLocalPos(g); ev = new FMouseEvent (MouseMove_Event, p2, g, b); - sm->onMouseDown(ev); + smenu->onMouseDown(ev); delete ev; } } if ( isMenuBar(super_menu) ) { - FMenuBar* mb = dynamic_cast(super_menu); - if ( mb ) + FMenuBar* mbar = dynamic_cast(super_menu); + if ( mbar ) { - const FPoint& p2 = mb->globalToLocalPos(g); + const FPoint& p2 = mbar->globalToLocalPos(g); ev = new FMouseEvent (MouseMove_Event, p2, g, b); - mb->onMouseDown(ev); + mbar->onMouseDown(ev); delete ev; } } @@ -269,24 +271,24 @@ void FMenuItem::onMouseUp (FMouseEvent* ev) if ( isMenu(super_menu) ) { - FMenu* sm = dynamic_cast(super_menu); - if ( sm ) + FMenu* smenu = dynamic_cast(super_menu); + if ( smenu ) { - const FPoint& p2 = sm->globalToLocalPos(g); + const FPoint& p2 = smenu->globalToLocalPos(g); ev = new FMouseEvent (MouseMove_Event, p2, g, b); - sm->onMouseUp(ev); + smenu->onMouseUp(ev); delete ev; } } if ( isMenuBar(super_menu) ) { - FMenuBar* mb = dynamic_cast(super_menu); - if ( mb ) + FMenuBar* mbar = dynamic_cast(super_menu); + if ( mbar ) { - const FPoint& p2 = mb->globalToLocalPos(g); + const FPoint& p2 = mbar->globalToLocalPos(g); ev = new FMouseEvent (MouseMove_Event, p2, g, b); - mb->onMouseUp(ev); + mbar->onMouseUp(ev); delete ev; } } @@ -303,24 +305,24 @@ void FMenuItem::onMouseMove (FMouseEvent* ev) if ( isMenu(super_menu) ) { - FMenu* sm = dynamic_cast(super_menu); - if ( sm ) + FMenu* smenu = dynamic_cast(super_menu); + if ( smenu ) { - const FPoint& p2 = sm->globalToLocalPos(g); + const FPoint& p2 = smenu->globalToLocalPos(g); ev = new FMouseEvent (MouseMove_Event, p2, g, b); - sm->onMouseMove(ev); + smenu->onMouseMove(ev); delete ev; } } if ( isMenuBar(super_menu) ) { - FMenuBar* mb = dynamic_cast(super_menu); - if ( mb ) + FMenuBar* mbar = dynamic_cast(super_menu); + if ( mbar ) { - const FPoint& p2 = mb->globalToLocalPos(g); + const FPoint& p2 = mbar->globalToLocalPos(g); ev = new FMouseEvent (MouseMove_Event, p2, g, b); - mb->onMouseMove(ev); + mbar->onMouseMove(ev); delete ev; } } @@ -335,17 +337,17 @@ void FMenuItem::onAccel (FAccelEvent* ev) { if ( super_menu && isMenuBar(super_menu) ) { - FMenuBar* mb = dynamic_cast(super_menu); - if ( mb ) + FMenuBar* mbar = dynamic_cast(super_menu); + if ( mbar ) { if ( menu && ! menu->hasSelectedListItem() ) { FWidget* focused_widget; - if ( mb->getSelectedMenuItem() ) - mb->getSelectedMenuItem()->unsetSelected(); + if ( mbar->getSelectedMenuItem() ) + mbar->getSelectedMenuItem()->unsetSelected(); setSelected(); - mb->selectedMenuItem = this; + mbar->selectedMenuItem = this; focused_widget = static_cast(ev->focusedWidget()); @@ -358,13 +360,13 @@ void FMenuItem::onAccel (FAccelEvent* ev) menu->redraw(); if ( statusBar() ) statusBar()->drawMessage(); - mb->redraw(); + mbar->redraw(); } else { unsetSelected(); - mb->selectedMenuItem = 0; - mb->redraw(); + mbar->selectedMenuItem = 0; + mbar->redraw(); processClicked(); } ev->accept(); diff --git a/src/fmenulist.cpp b/src/fmenulist.cpp index 66a27369..822c0067 100644 --- a/src/fmenulist.cpp +++ b/src/fmenulist.cpp @@ -31,11 +31,6 @@ FMenuList::~FMenuList() // destructor } -// private methods of FMenuList -//---------------------------------------------------------------------- - - - // public methods of FMenuList //---------------------------------------------------------------------- bool FMenuList::hasSelectedItem()