Improve keyboard shortcut handling in menus

This commit is contained in:
Markus Gans 2015-11-04 00:14:23 +01:00
parent 18a7d991ff
commit 8f3fab4bf5
7 changed files with 203 additions and 111 deletions

View File

@ -1,3 +1,6 @@
2015-11-03 Markus Gans <guru.mail@muenster.de>
* Improve keyboard shortcut handling in menus
2015-11-01 Markus Gans <guru.mail@muenster.de> 2015-11-01 Markus Gans <guru.mail@muenster.de>
* First working version of an application menu, * First working version of an application menu,
it uses the new classes FMenuBar, FMenu and FMenuItem it uses the new classes FMenuBar, FMenu and FMenuItem

View File

@ -115,9 +115,9 @@ void FMenu::init(FWidget* parent)
{ {
if ( isMenuBar(parent) ) if ( isMenuBar(parent) )
{ {
FMenuBar* mb = dynamic_cast<FMenuBar*>(parent); FMenuBar* mbar = dynamic_cast<FMenuBar*>(parent);
if ( mb ) if ( mbar )
mb->menu_dimension(); mbar->menu_dimension();
} }
setSuperMenu(parent); setSuperMenu(parent);
} }
@ -201,15 +201,15 @@ void FMenu::hideSuperMenus()
{ {
if ( isMenuBar(super) ) if ( isMenuBar(super) )
{ {
FMenuBar* mb = reinterpret_cast<FMenuBar*>(super); FMenuBar* mbar = reinterpret_cast<FMenuBar*>(super);
FMenuItem* selectedMenuItem = mb->selectedMenuItem; FMenuItem* selectedMenuItem = mbar->selectedMenuItem;
if ( selectedMenuItem ) if ( selectedMenuItem )
{ {
selectedMenuItem->unsetSelected(); selectedMenuItem->unsetSelected();
selectedMenuItem = 0; selectedMenuItem = 0;
mb->mouse_down = false; mbar->mouse_down = false;
mb->redraw(); mbar->redraw();
} }
} }
else if ( isMenu(super) ) else if ( isMenu(super) )
@ -321,19 +321,57 @@ bool FMenu::selectPrevItem()
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void FMenu::keypressMenuBar (FKeyEvent* ev) void FMenu::keypressMenuBar (FKeyEvent*& ev)
{ {
FWidget* super = getSuperMenu(); FWidget* super = getSuperMenu();
if ( super ) if ( super )
{ {
if ( isMenuBar(super) ) if ( isMenuBar(super) )
{ {
FMenuBar* mb = reinterpret_cast<FMenuBar*>(super); FMenuBar* mbar = reinterpret_cast<FMenuBar*>(super);
mb->onKeyPress(ev); mbar->onKeyPress(ev);
} }
} }
} }
//----------------------------------------------------------------------
bool FMenu::hotkeyMenu (FKeyEvent*& ev)
{
std::vector<FMenuItem*>::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) int FMenu::getHotkeyPos (wchar_t*& src, wchar_t*& dest, uInt length)
{ {
@ -554,16 +592,6 @@ void FMenu::drawItems()
setReverse(true); setReverse(true);
delete[] item_text; delete[] item_text;
} }
/*if ( is_selected && statusBar() )
{
FString msg = (*iter)->getStatusbarMessage();
FString curMsg = statusBar()->getMessage();
if ( curMsg != msg )
{
statusBar()->setMessage(msg);
statusBar()->drawMessage();
}
}*/
++iter; ++iter;
y++; y++;
} }
@ -597,38 +625,17 @@ void FMenu::processActivate()
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void FMenu::onKeyPress (FKeyEvent* ev) void FMenu::onKeyPress (FKeyEvent* ev)
{ {
// looking for a hotkey // looking for menu hotkey
std::vector<FMenuItem*>::const_iterator iter, end; if ( hotkeyMenu(ev) )
iter = itemlist.begin(); return;
end = itemlist.end();
while ( iter != end ) // looking for menu bar hotkey
FWidget* smenu = getSuperMenu();
if ( smenu && isMenuBar(smenu))
{ {
if ( (*iter)->hasHotkey() ) FMenuBar* mbar = reinterpret_cast<FMenuBar*>(smenu);
{ if ( mbar->hotkeyMenu(ev) )
bool found = false; return;
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;
} }
switch ( ev->key() ) switch ( ev->key() )
@ -911,8 +918,8 @@ void FMenu::onMouseMove (FMouseEvent* ev)
int b = ev->getButton(); int b = ev->getButton();
ev = new FMouseEvent (MouseMove_Event, p, g, b); ev = new FMouseEvent (MouseMove_Event, p, g, b);
setClickedWidget(menubar); setClickedWidget(menubar);
FMenuBar* sm = reinterpret_cast<FMenuBar*>(menubar); FMenuBar* mbar = reinterpret_cast<FMenuBar*>(menubar);
sm->onMouseDown(ev); mbar->onMouseDown(ev);
delete ev; delete ev;
} }

View File

@ -67,7 +67,8 @@ class FMenu : public FWindow, public FMenuList
bool containsMenuStructure (int, int) const; bool containsMenuStructure (int, int) const;
bool selectNextItem(); bool selectNextItem();
bool selectPrevItem(); bool selectPrevItem();
void keypressMenuBar (FKeyEvent*); void keypressMenuBar (FKeyEvent*&);
bool hotkeyMenu (FKeyEvent*&);
int getHotkeyPos (wchar_t*&, wchar_t*&, uInt); int getHotkeyPos (wchar_t*&, wchar_t*&, uInt);
void draw(); void draw();
void drawBorder(); void drawBorder();

View File

@ -47,6 +47,7 @@ void FMenuBar::init()
setGeometry (1, 1, getColumnNumber(), 1, false); setGeometry (1, 1, getColumnNumber(), 1, false);
getRootWidget()->setTopPadding(1, true); getRootWidget()->setTopPadding(1, true);
setMenuBar(this); setMenuBar(this);
addAccelerator (fc::Fkey_f10, this);
foregroundColor = wc.menu_active_fg; foregroundColor = wc.menu_active_fg;
backgroundColor = wc.menu_active_bg; backgroundColor = wc.menu_active_bg;
window_object = true; window_object = true;
@ -191,6 +192,60 @@ bool FMenuBar::selectPrevItem()
return true; return true;
} }
//----------------------------------------------------------------------
bool FMenuBar::hotkeyMenu (FKeyEvent*& ev)
{
std::vector<FMenuItem*>::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) int FMenuBar::getHotkeyPos (wchar_t*& src, wchar_t*& dest, uInt length)
{ {
@ -369,17 +424,6 @@ void FMenuBar::drawItems()
setReverse(false); setReverse(false);
setUpdateVTerm(true); 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(); end = itemlist.end();
mouse_x = ev->getX(); mouse_x = ev->getX();
mouse_y = ev->getY(); 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 ) while ( iter != end )
{ {
int x1, x2; int x1, x2;
@ -486,9 +529,7 @@ void FMenuBar::onMouseDown (FMouseEvent* ev)
FFocusEvent out (FocusOut_Event); FFocusEvent out (FocusOut_Event);
FApplication::queueEvent(focused_widget, &out); FApplication::queueEvent(focused_widget, &out);
(*iter)->setSelected(); (*iter)->setSelected();
//FMessageBox::info (this, "Info", (*iter)->getStatusbarMessage());
(*iter)->setFocus(); (*iter)->setFocus();
//FMessageBox::info (this, "Info", statusBar()->getMessage());
selectedMenuItem = *iter; selectedMenuItem = *iter;
focus_changed = true; focus_changed = true;
if ( focused_widget ) 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() void FMenuBar::hide()
{ {
@ -706,6 +761,32 @@ void FMenuBar::hide()
delete[] blank; delete[] blank;
} }
//----------------------------------------------------------------------
void FMenuBar::selectFirstItemInMenu()
{
std::vector<FMenuItem*>::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() void FMenuBar::unselectItemInMenu()
{ {

View File

@ -56,6 +56,7 @@ class FMenuBar : public FWindow, public FMenuList
bool isMenu (FMenuItem*) const; bool isMenu (FMenuItem*) const;
bool selectNextItem(); bool selectNextItem();
bool selectPrevItem(); bool selectPrevItem();
bool hotkeyMenu (FKeyEvent*&);
int getHotkeyPos (wchar_t*&, wchar_t*&, uInt); int getHotkeyPos (wchar_t*&, wchar_t*&, uInt);
void draw(); void draw();
void drawItems(); void drawItems();
@ -70,7 +71,9 @@ class FMenuBar : public FWindow, public FMenuList
void onMouseDown (FMouseEvent*); void onMouseDown (FMouseEvent*);
void onMouseUp (FMouseEvent*); void onMouseUp (FMouseEvent*);
void onMouseMove (FMouseEvent*); void onMouseMove (FMouseEvent*);
void onAccel (FAccelEvent*);
void hide(); void hide();
void selectFirstItemInMenu();
void unselectItemInMenu(); void unselectItemInMenu();
FMenuItem* getSelectedMenuItem() const; FMenuItem* getSelectedMenuItem() const;
bool hasSelectedMenuItem() const; bool hasSelectedMenuItem() const;

View File

@ -211,16 +211,18 @@ void FMenuItem::onKeyPress (FKeyEvent* ev)
{ {
if ( isMenu(super_menu) ) if ( isMenu(super_menu) )
{ {
FMenu* sm = dynamic_cast<FMenu*>(super_menu); FMenu* smenu = dynamic_cast<FMenu*>(super_menu);
if ( sm ) if ( smenu )
sm->onKeyPress(ev); smenu->onKeyPress(ev);
} }
if ( isMenuBar(super_menu) ) if ( isMenuBar(super_menu) )
{ {
FMenuBar* mb = dynamic_cast<FMenuBar*>(super_menu); FMenuBar* mbar = dynamic_cast<FMenuBar*>(super_menu);
if ( mb ) if ( mbar->hotkeyMenu(ev) )
mb->onKeyPress(ev); return;
if ( mbar )
mbar->onKeyPress(ev);
} }
} }
} }
@ -235,24 +237,24 @@ void FMenuItem::onMouseDown (FMouseEvent* ev)
if ( isMenu(super_menu) ) if ( isMenu(super_menu) )
{ {
FMenu* sm = dynamic_cast<FMenu*>(super_menu); FMenu* smenu = dynamic_cast<FMenu*>(super_menu);
if ( sm ) if ( smenu )
{ {
const FPoint& p2 = sm->globalToLocalPos(g); const FPoint& p2 = smenu->globalToLocalPos(g);
ev = new FMouseEvent (MouseMove_Event, p2, g, b); ev = new FMouseEvent (MouseMove_Event, p2, g, b);
sm->onMouseDown(ev); smenu->onMouseDown(ev);
delete ev; delete ev;
} }
} }
if ( isMenuBar(super_menu) ) if ( isMenuBar(super_menu) )
{ {
FMenuBar* mb = dynamic_cast<FMenuBar*>(super_menu); FMenuBar* mbar = dynamic_cast<FMenuBar*>(super_menu);
if ( mb ) if ( mbar )
{ {
const FPoint& p2 = mb->globalToLocalPos(g); const FPoint& p2 = mbar->globalToLocalPos(g);
ev = new FMouseEvent (MouseMove_Event, p2, g, b); ev = new FMouseEvent (MouseMove_Event, p2, g, b);
mb->onMouseDown(ev); mbar->onMouseDown(ev);
delete ev; delete ev;
} }
} }
@ -269,24 +271,24 @@ void FMenuItem::onMouseUp (FMouseEvent* ev)
if ( isMenu(super_menu) ) if ( isMenu(super_menu) )
{ {
FMenu* sm = dynamic_cast<FMenu*>(super_menu); FMenu* smenu = dynamic_cast<FMenu*>(super_menu);
if ( sm ) if ( smenu )
{ {
const FPoint& p2 = sm->globalToLocalPos(g); const FPoint& p2 = smenu->globalToLocalPos(g);
ev = new FMouseEvent (MouseMove_Event, p2, g, b); ev = new FMouseEvent (MouseMove_Event, p2, g, b);
sm->onMouseUp(ev); smenu->onMouseUp(ev);
delete ev; delete ev;
} }
} }
if ( isMenuBar(super_menu) ) if ( isMenuBar(super_menu) )
{ {
FMenuBar* mb = dynamic_cast<FMenuBar*>(super_menu); FMenuBar* mbar = dynamic_cast<FMenuBar*>(super_menu);
if ( mb ) if ( mbar )
{ {
const FPoint& p2 = mb->globalToLocalPos(g); const FPoint& p2 = mbar->globalToLocalPos(g);
ev = new FMouseEvent (MouseMove_Event, p2, g, b); ev = new FMouseEvent (MouseMove_Event, p2, g, b);
mb->onMouseUp(ev); mbar->onMouseUp(ev);
delete ev; delete ev;
} }
} }
@ -303,24 +305,24 @@ void FMenuItem::onMouseMove (FMouseEvent* ev)
if ( isMenu(super_menu) ) if ( isMenu(super_menu) )
{ {
FMenu* sm = dynamic_cast<FMenu*>(super_menu); FMenu* smenu = dynamic_cast<FMenu*>(super_menu);
if ( sm ) if ( smenu )
{ {
const FPoint& p2 = sm->globalToLocalPos(g); const FPoint& p2 = smenu->globalToLocalPos(g);
ev = new FMouseEvent (MouseMove_Event, p2, g, b); ev = new FMouseEvent (MouseMove_Event, p2, g, b);
sm->onMouseMove(ev); smenu->onMouseMove(ev);
delete ev; delete ev;
} }
} }
if ( isMenuBar(super_menu) ) if ( isMenuBar(super_menu) )
{ {
FMenuBar* mb = dynamic_cast<FMenuBar*>(super_menu); FMenuBar* mbar = dynamic_cast<FMenuBar*>(super_menu);
if ( mb ) if ( mbar )
{ {
const FPoint& p2 = mb->globalToLocalPos(g); const FPoint& p2 = mbar->globalToLocalPos(g);
ev = new FMouseEvent (MouseMove_Event, p2, g, b); ev = new FMouseEvent (MouseMove_Event, p2, g, b);
mb->onMouseMove(ev); mbar->onMouseMove(ev);
delete ev; delete ev;
} }
} }
@ -335,17 +337,17 @@ void FMenuItem::onAccel (FAccelEvent* ev)
{ {
if ( super_menu && isMenuBar(super_menu) ) if ( super_menu && isMenuBar(super_menu) )
{ {
FMenuBar* mb = dynamic_cast<FMenuBar*>(super_menu); FMenuBar* mbar = dynamic_cast<FMenuBar*>(super_menu);
if ( mb ) if ( mbar )
{ {
if ( menu && ! menu->hasSelectedListItem() ) if ( menu && ! menu->hasSelectedListItem() )
{ {
FWidget* focused_widget; FWidget* focused_widget;
if ( mb->getSelectedMenuItem() ) if ( mbar->getSelectedMenuItem() )
mb->getSelectedMenuItem()->unsetSelected(); mbar->getSelectedMenuItem()->unsetSelected();
setSelected(); setSelected();
mb->selectedMenuItem = this; mbar->selectedMenuItem = this;
focused_widget = static_cast<FWidget*>(ev->focusedWidget()); focused_widget = static_cast<FWidget*>(ev->focusedWidget());
@ -358,13 +360,13 @@ void FMenuItem::onAccel (FAccelEvent* ev)
menu->redraw(); menu->redraw();
if ( statusBar() ) if ( statusBar() )
statusBar()->drawMessage(); statusBar()->drawMessage();
mb->redraw(); mbar->redraw();
} }
else else
{ {
unsetSelected(); unsetSelected();
mb->selectedMenuItem = 0; mbar->selectedMenuItem = 0;
mb->redraw(); mbar->redraw();
processClicked(); processClicked();
} }
ev->accept(); ev->accept();

View File

@ -31,11 +31,6 @@ FMenuList::~FMenuList() // destructor
} }
// private methods of FMenuList
//----------------------------------------------------------------------
// public methods of FMenuList // public methods of FMenuList
//---------------------------------------------------------------------- //----------------------------------------------------------------------
bool FMenuList::hasSelectedItem() bool FMenuList::hasSelectedItem()