From 101c9fcc7489fa2f28aa7ad4eb6652d16c6d5e7c Mon Sep 17 00:00:00 2001 From: Markus Gans Date: Sun, 22 Nov 2015 21:41:18 +0100 Subject: [PATCH] Add sub-menu support --- ChangeLog | 8 +- src/fcharmap.h | 5 +- src/fcheckbox.cpp | 2 +- src/fenum.h | 4 +- src/fmenu.cpp | 248 +++++++++++++++++++++++++++++++------------ src/fmenu.h | 3 + src/fmenubar.cpp | 4 + src/fmenulist.cpp | 19 ---- src/fmenulist.h | 1 - src/fradiobutton.cpp | 2 +- src/fterm.cpp | 14 ++- src/fterm.h | 1 + 12 files changed, 216 insertions(+), 95 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8fc149b2..a9810b73 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,10 @@ -2015-11-12 Markus Gans +2015-11-22 Markus Gans + * Add sub-menu support + +2015-11-19 Markus Gans + * Add the missing resetXTermHighlightBackground method + +2015-11-15 Markus Gans * Add two new classes FCheckMenuItem and FRadioMenuItem for menu check marks and menu option marks (bullets) diff --git a/src/fcharmap.h b/src/fcharmap.h index 4a74b690..2a9110d6 100644 --- a/src/fcharmap.h +++ b/src/fcharmap.h @@ -28,7 +28,7 @@ static uInt character[][fc::NUM_OF_ENCODINGS] = {0x2260, '|', 0xd8, '!'}, // ≠ - NotEqualTo {0x00b1, 'g', 0xf1, '#'}, // ± - PlusMinus {0x00f7, '/', 0xf6, '/'}, // ÷ - Division sign - {0x00d7, '*', '*', '*'}, // × - Multiplication sign + {0x00d7, 'x', 'x', 'x'}, // × - Multiplication sign {0x02e3, '~', 0xfc, '~'}, // ˣ - Modifier letter small x {0x00b0, 'f', 0xb0, 'o'}, // ° - Degree {0x2022, '`', 0x04, '*'}, // • - Bullet @@ -108,7 +108,8 @@ static uInt character[][fc::NUM_OF_ENCODINGS] = {0x1af4, 0, 0xf4, 0}, // ] - NF_rev_menu_button3 {0x1af5, 0, 0xf5, 0}, // ] - NF_shadow_box_right {0x1afb, 0, 0xfb, 0}, // ✓ - NF_check_mark - {0x221a, 0, 0xfb, 'x'} // √ - square root + {0x221a, 0, 0xfb, 'x'}, // √ - square root + {0x25cf, '`', 0x04, '*'} // ● - black circle }; const int lastCharItem = int(sizeof(character) / sizeof(character[0])) - 1; diff --git a/src/fcheckbox.cpp b/src/fcheckbox.cpp index 2460725e..f74c3e60 100644 --- a/src/fcheckbox.cpp +++ b/src/fcheckbox.cpp @@ -69,7 +69,7 @@ void FCheckBox::drawCheckButton() else { print ('['); - print ('x'); + print (fc::Times); // Times × print (']'); } } diff --git a/src/fenum.h b/src/fenum.h index e647a1f2..3ee5dcfc 100644 --- a/src/fenum.h +++ b/src/fenum.h @@ -87,6 +87,7 @@ class fc LessThanOrEqualTo = 0x2264, // ≤ NotEqualTo = 0x2260, // ≠ PlusMinus = 0x00b1, // ± + Times = 0x00d7, // × Degree = 0x00b0, // ° BlackVerticalRectangle = 0x25ae, // ▮ SmallBullet = 0x00b7, // · @@ -166,7 +167,8 @@ class fc NF_rev_menu_button3 = 0x1af4, // ] NF_shadow_box_right = 0x1af5, // ] NF_check_mark = 0x1afb, // ✓ - SquareRoot = 0x221a // √ + SquareRoot = 0x221a, // √ + BlackCircle = 0x25CF // ● }; // keyboard - single keys diff --git a/src/fmenu.cpp b/src/fmenu.cpp index a7d1ff7d..9cdc5183 100644 --- a/src/fmenu.cpp +++ b/src/fmenu.cpp @@ -15,6 +15,7 @@ FMenu::FMenu(FWidget* parent) : FWindow(parent) , item(0) , super_menu(0) + , open_sub_menu(0) , maxItemWidth(0) , mouse_down(false) , has_checkable_items(false) @@ -27,6 +28,7 @@ FMenu::FMenu (FString& txt, FWidget* parent) : FWindow(parent) , item(0) , super_menu(0) + , open_sub_menu(0) , maxItemWidth(0) , mouse_down(false) , has_checkable_items(false) @@ -40,6 +42,7 @@ FMenu::FMenu (const std::string& txt, FWidget* parent) : FWindow(parent) , item(0) , super_menu(0) + , open_sub_menu(0) , maxItemWidth(0) , mouse_down(false) , has_checkable_items(false) @@ -53,6 +56,7 @@ FMenu::FMenu (const char* txt, FWidget* parent) : FWindow(parent) , item(0) , super_menu(0) + , open_sub_menu(0) , maxItemWidth(0) , mouse_down(false) , has_checkable_items(false) @@ -138,8 +142,13 @@ void FMenu::menu_dimension() { uInt item_width = (*iter)->getTextLength() + 2; int accel_key = (*iter)->accel_key; + bool has_menu = (*iter)->hasMenu(); - if ( accel_key ) + if ( has_menu ) + { + item_width += 3; + } + else if ( accel_key ) { uInt accel_len = getKeyName(accel_key).getLength(); item_width += accel_len + 2; @@ -165,6 +174,14 @@ void FMenu::menu_dimension() while ( iter != end ) { (*iter)->setGeometry (item_X, item_Y, int(maxItemWidth), 1); + + if ( (*iter)->hasMenu() ) + { + int menu_X = (*iter)->getX() + int(maxItemWidth) + 1; + int menu_Y = (*iter)->getY(); + // set sub-menu position + (*iter)->getMenu()->setPos (menu_X, menu_Y, false); + } item_Y++; ++iter; @@ -192,20 +209,51 @@ bool FMenu::isRadioMenuItem (FWidget* w) const , const_cast("FRadioMenuItem") ) == 0 ); } +//---------------------------------------------------------------------- +bool FMenu::isSubMenu() const +{ + FWidget* super = getSuperMenu(); + if ( super && isMenu(super) ) + return true; + else + return false; +} + +//---------------------------------------------------------------------- +void FMenu::openSubMenu (FMenu* sub_menu) +{ + if ( sub_menu->isVisible() ) + return; + + // open sub menu + sub_menu->selectFirstItem(); + sub_menu->getSelectedItem()->setFocus(); + sub_menu->setVisible(); + sub_menu->show(); + open_sub_menu = sub_menu; + raiseWindow (sub_menu); + sub_menu->redraw(); + if ( statusBar() ) + statusBar()->drawMessage(); + updateTerminal(); + flush_out(); +} + //---------------------------------------------------------------------- void FMenu::hideSubMenus() { + if ( ! hasSelectedItem() ) + return; + // hide all sub-menus - if ( hasSelectedItem() ) + if ( getSelectedItem()->hasMenu() ) { - if ( getSelectedItem()->hasMenu() ) - { - FMenu* m = getSelectedItem()->getMenu(); - m->hideSubMenus(); - m->hide(); - } - unselectItem(); + FMenu* m = getSelectedItem()->getMenu(); + m->hideSubMenus(); + m->hide(); + open_sub_menu = 0; } + unselectItem(); } //---------------------------------------------------------------------- @@ -277,10 +325,12 @@ bool FMenu::selectNextItem() unselectItem(); next->setSelected(); setSelectedItem(next); + redraw(); next->setFocus(); if ( statusBar() ) statusBar()->drawMessage(); - redraw(); + updateTerminal(); + flush_out(); break; } ++iter; @@ -323,6 +373,8 @@ bool FMenu::selectPrevItem() if ( statusBar() ) statusBar()->drawMessage(); redraw(); + updateTerminal(); + flush_out(); break; } } while ( iter != begin ); @@ -507,6 +559,7 @@ void FMenu::drawItems() uInt txt_length; int hotkeypos, to_char; int accel_key = (*iter)->accel_key; + bool has_menu = (*iter)->hasMenu(); bool is_enabled = (*iter)->isEnabled(); bool is_checked = (*iter)->isChecked(); bool is_checkable = (*iter)->checkable; @@ -555,7 +608,7 @@ void FMenu::drawItems() { if ( is_radio_btn ) { - print (fc::Bullet); + print (fc::BlackCircle); // BlackCircle ● } else { @@ -623,8 +676,17 @@ void FMenu::drawItems() else print (item_text[z]); } - - if ( accel_key ) + if ( has_menu ) + { + int len = int(maxItemWidth) - (to_char + c + 3); + if ( len > 0 ) + { + FString spaces (len, wchar_t(' ')); + print (spaces + wchar_t(fc::BlackRightPointingPointer)); + to_char = int(maxItemWidth) - (c + 2); + } + } + else if ( accel_key ) { FString accel_name (getKeyName(accel_key)); int accel_len = int(accel_name.getLength()); @@ -710,10 +772,16 @@ void FMenu::onKeyPress (FKeyEvent* ev) if ( hasSelectedItem() ) { FMenuItem* sel_item = getSelectedItem(); - unselectItem(); - hide(); - hideSuperMenus(); - sel_item->processClicked(); + + if ( sel_item->hasMenu() ) + openSubMenu (sel_item->getMenu()); + else + { + unselectItem(); + hide(); + hideSuperMenus(); + sel_item->processClicked(); + } } ev->accept(); break; @@ -729,38 +797,35 @@ void FMenu::onKeyPress (FKeyEvent* ev) break; case fc::Fkey_left: - if ( hasSelectedItem() && getSelectedItem()->hasMenu() ) + if ( isSubMenu() ) { - FMenu* sub_menu = getSelectedItem()->getMenu(); - if ( sub_menu->isVisible() ) - hideSubMenus(); - else - keypressMenuBar(ev); // select previous menu - ev->accept(); + FMenu* smenu = reinterpret_cast(getSuperMenu()); + hideSubMenus(); + hide(); + smenu->getSelectedItem()->setFocus(); + smenu->redraw(); + if ( statusBar() ) + statusBar()->drawMessage(); + updateTerminal(); + flush_out(); } 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() ) { - // open sub menu - sub_menu->selectFirstItem(); - sub_menu->getSelectedItem()->setFocus();; - sub_menu->setVisible(); - sub_menu->show(); - raiseWindow (sub_menu); - sub_menu->redraw(); - updateTerminal(); - flush_out(); + openSubMenu (sub_menu); + ev->accept(); } else keypressMenuBar(ev); // select next menu - ev->accept(); } else keypressMenuBar(ev); // select next menu @@ -769,12 +834,21 @@ void FMenu::onKeyPress (FKeyEvent* ev) case fc::Fkey_escape: case fc::Fkey_escape_mintty: unselectItem(); - hide(); hideSubMenus(); - hideSuperMenus(); - activatePrevWindow(); - getActiveWindow()->getFocusWidget()->setFocus(); - getActiveWindow()->redraw(); + hide(); + if ( isSubMenu() ) + { + FMenu* smenu = reinterpret_cast(getSuperMenu()); + smenu->getSelectedItem()->setFocus(); + smenu->redraw(); + } + else + { + hideSuperMenus(); + activatePrevWindow(); + getActiveWindow()->getFocusWidget()->setFocus(); + getActiveWindow()->redraw(); + } if ( statusBar() ) statusBar()->drawMessage(); updateTerminal(); @@ -827,6 +901,7 @@ void FMenu::onMouseDown (FMouseEvent* ev) && mouse_y == y && ! (*iter)->isSelected() ) { + // Mouse pointer over item FWidget* focused_widget = getFocusWidget(); FFocusEvent out (FocusOut_Event); FApplication::queueEvent(focused_widget, &out); @@ -884,6 +959,7 @@ void FMenu::onMouseUp (FMouseEvent* ev) && mouse_x <= x2 && mouse_y == y ) { + // Mouse pointer over item unselectItem(); hide(); hideSuperMenus(); @@ -913,6 +989,9 @@ void FMenu::onMouseMove (FMouseEvent* ev) { std::vector::const_iterator iter, end; bool focus_changed = false; + bool mouse_over_submenu = false; + bool hide_sub_menu = false; + FMenu* show_sub_menu = 0; FPoint mouse_pos; iter = itemlist.begin(); @@ -920,6 +999,13 @@ void FMenu::onMouseMove (FMouseEvent* ev) mouse_pos = ev->getPos(); mouse_pos -= FPoint(getRightPadding(),getTopPadding()); + if ( open_sub_menu ) + { + const FRect& submenu_geometry = open_sub_menu->getGeometryGlobal(); + if ( submenu_geometry.contains(ev->getGlobalPos()) ) + mouse_over_submenu = true; + } + while ( iter != end ) { int x1, x2, y, mouse_x, mouse_y; @@ -938,6 +1024,7 @@ void FMenu::onMouseMove (FMouseEvent* ev) && ! (*iter)->isSelected() && ! (*iter)->isSeparator() ) { + // Mouse pointer over item FWidget* focused_widget = getFocusWidget(); FFocusEvent out (FocusOut_Event); FApplication::queueEvent(focused_widget, &out); @@ -948,6 +1035,14 @@ void FMenu::onMouseMove (FMouseEvent* ev) focused_widget->redraw(); if ( statusBar() ) statusBar()->drawMessage(); + if ( (*iter)->hasMenu() ) + { + FMenu* sub_menu = (*iter)->getMenu(); + if ( ! sub_menu->isVisible() ) + show_sub_menu = sub_menu; + } + else if ( open_sub_menu ) + hide_sub_menu = true; focus_changed = true; } } @@ -955,8 +1050,10 @@ void FMenu::onMouseMove (FMouseEvent* ev) { if ( getGeometryGlobal().contains(ev->getGlobalPos()) && (*iter)->isEnabled() - && (*iter)->isSelected() ) + && (*iter)->isSelected() + && ! mouse_over_submenu ) { + // Unselect selected item without mouse focus (*iter)->unsetSelected(); if ( getSelectedItem() == *iter ) setSelectedItem(0); @@ -966,10 +1063,21 @@ void FMenu::onMouseMove (FMouseEvent* ev) ++iter; } - // Mouse is over border or separator - if ( ! hasSelectedItem() && statusBar() + if ( mouse_over_submenu ) + { + // Mouse event handover to sub-menu + const FPoint& g = ev->getGlobalPos(); + const FPoint& p = open_sub_menu->globalToLocalPos(g); + int b = ev->getButton(); + ev = new FMouseEvent (MouseMove_Event, p, g, b); + open_sub_menu->mouse_down = true; + setClickedWidget(open_sub_menu); + open_sub_menu->onMouseMove(ev); + } + else if ( ! hasSelectedItem() && statusBar() && getGeometryGlobal().contains(ev->getGlobalPos()) ) { + // Mouse is over border or separator FString msg = getStatusbarMessage(); FString curMsg = statusBar()->getMessage(); if ( curMsg != msg ) @@ -977,6 +1085,8 @@ void FMenu::onMouseMove (FMouseEvent* ev) statusBar()->setMessage(msg); statusBar()->drawMessage(); } + if ( open_sub_menu ) + hide_sub_menu = true; } // Mouse event handover to the menu bar @@ -998,6 +1108,26 @@ void FMenu::onMouseMove (FMouseEvent* ev) 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(); + } + else if ( hide_sub_menu ) + { + open_sub_menu->hideSubMenus(); + open_sub_menu->hide(); + open_sub_menu = 0; + updateTerminal(); + flush_out(); + } } } @@ -1031,10 +1161,13 @@ void FMenu::hide() updateTerminal(); flush_out(); - FMenu* open_menu = static_cast(getOpenMenu()); - if ( open_menu && open_menu != this ) - open_menu->hide(); - setOpenMenu(0); + if ( ! isSubMenu() ) + { + FMenu* open_menu = static_cast(getOpenMenu()); + if ( open_menu && open_menu != this ) + open_menu->hide(); + setOpenMenu(0); + } mouse_down = false; } } @@ -1058,30 +1191,9 @@ void FMenu::setStatusbarMessage(FString msg) } //---------------------------------------------------------------------- -void FMenu::cb_menuitem_activated (FWidget* widget, void*) +void FMenu::cb_menuitem_activated (FWidget*, void*) { - FMenuItem* menuitem = static_cast(widget); - if ( menuitem->hasMenu() ) - { - FMenu* menu = menuitem->getMenu(); - if ( ! menu->isVisible() ) - { - FMenu* open_menu = static_cast(getOpenMenu()); - if ( open_menu && open_menu != menu ) - open_menu->hide(); - setOpenMenu(menu); - - menu->setVisible(); - menu->show(); - raiseWindow (menu); - menu->redraw(); - updateTerminal(); - flush_out(); - if ( ! isMenu(getSuperMenu()) ) - setOpenMenu(menu); - } - } } //---------------------------------------------------------------------- diff --git a/src/fmenu.h b/src/fmenu.h index 1c4c8881..7a0866d4 100644 --- a/src/fmenu.h +++ b/src/fmenu.h @@ -48,6 +48,7 @@ class FMenu : public FWindow, public FMenuList private: FMenuItem* item; FWidget* super_menu; + FMenu* open_sub_menu; uInt maxItemWidth; bool mouse_down; bool has_checkable_items; @@ -62,6 +63,8 @@ class FMenu : public FWindow, public FMenuList bool isRadioMenuItem (FWidget*) const; FWidget* getSuperMenu() const; void setSuperMenu (FWidget*); + bool isSubMenu() const; + void openSubMenu (FMenu*); void hideSubMenus(); void hideSuperMenus(); bool containsMenuStructure (const FPoint&) const; diff --git a/src/fmenubar.cpp b/src/fmenubar.cpp index de12e5e1..1a7a475b 100644 --- a/src/fmenubar.cpp +++ b/src/fmenubar.cpp @@ -558,6 +558,7 @@ void FMenuBar::onMouseDown (FMouseEvent* ev) && mouse_x <= x2 && mouse_y == 1 ) { + // Mouse pointer over item if ( (*iter)->isEnabled() && ! (*iter)->isSelected() ) { FWidget* focused_widget = getFocusWidget(); @@ -636,6 +637,7 @@ void FMenuBar::onMouseUp (FMouseEvent* ev) && (*iter)->isEnabled() && (*iter)->isSelected() ) { + // Mouse pointer over item if ( (*iter)->hasMenu() ) { FMenu* menu = (*iter)->getMenu(); @@ -714,6 +716,7 @@ void FMenuBar::onMouseMove (FMouseEvent* ev) && mouse_x <= x2 && mouse_y == 1 ) { + // Mouse pointer over item if ( (*iter)->isEnabled() && ! (*iter)->isSelected() ) { FWidget* focused_widget = getFocusWidget(); @@ -747,6 +750,7 @@ void FMenuBar::onMouseMove (FMouseEvent* ev) && (*iter)->isEnabled() && (*iter)->isSelected() ) { + // Unselect selected item without mouse focus (*iter)->unsetSelected(); if ( getSelectedItem() == *iter ) setSelectedItem(0); diff --git a/src/fmenulist.cpp b/src/fmenulist.cpp index a4b736a0..0e68dfbf 100644 --- a/src/fmenulist.cpp +++ b/src/fmenulist.cpp @@ -67,25 +67,6 @@ void FMenuList::unselectItem() setSelectedItem(0); } -//---------------------------------------------------------------------- -bool FMenuList::hasSelectedItem() -{ - if ( ! itemlist.empty() ) - { - std::vector::const_iterator iter, end; - iter = itemlist.begin(); - end = itemlist.end(); - - while ( iter != end ) - { - if ( (*iter)->isSelected() ) - return true; - ++iter; - } - } - return false; -} - //---------------------------------------------------------------------- void FMenuList::insert (FMenuItem* i) { diff --git a/src/fmenulist.h b/src/fmenulist.h index d951d735..dd07485b 100644 --- a/src/fmenulist.h +++ b/src/fmenulist.h @@ -52,7 +52,6 @@ class FMenuList bool isSelected (int) const; void selectFirstItem(); void unselectItem(); - bool hasSelectedItem(); FMenuItem* getSelectedItem() const; void setSelectedItem (FMenuItem*); bool hasSelectedItem() const; diff --git a/src/fradiobutton.cpp b/src/fradiobutton.cpp index 4db80b3d..04e269a1 100644 --- a/src/fradiobutton.cpp +++ b/src/fradiobutton.cpp @@ -70,7 +70,7 @@ void FRadioButton::drawRadioButton() else { print ('('); - print (fc::Bullet); // Bullet • + print (fc::BlackCircle); // BlackCircle ● print (')'); } } diff --git a/src/fterm.cpp b/src/fterm.cpp index aefa89a1..41e39c5d 100644 --- a/src/fterm.cpp +++ b/src/fterm.cpp @@ -1505,7 +1505,7 @@ void FTerm::init() // mintty can't reset these settings setXTermBackground("rgb:8080/a4a4/ecec"); setXTermForeground("rgb:0000/0000/0000"); - setXTermHighlightBackground("rgb:b1b1/b1b1/b1b1"); + setXTermHighlightBackground("rgb:4a4a/9090/d9d9"); } setRawMode(); @@ -1606,6 +1606,7 @@ void FTerm::finish() resetXTermCursorColor(); resetXTermForeground(); resetXTermBackground(); + resetXTermHighlightBackground(); setXTermCursorStyle(fc::steady_block); if ( max_color >= 16 && ! kde_konsole && ! tera_terminal ) @@ -2893,6 +2894,17 @@ void FTerm::resetXTermMouseBackground() } } +//---------------------------------------------------------------------- +void FTerm::resetXTermHighlightBackground() +{ + // Reset the highlight background color + if ( xterm || urxvt_terminal ) + { + putstringf ("\033]117\07"); + fflush(stdout); + } +} + //---------------------------------------------------------------------- void FTerm::saveColorMap() { diff --git a/src/fterm.h b/src/fterm.h index c3d7d7d1..8739caeb 100644 --- a/src/fterm.h +++ b/src/fterm.h @@ -325,6 +325,7 @@ class FTerm static void resetXTermCursorColor(); static void resetXTermMouseForeground(); static void resetXTermMouseBackground(); + static void resetXTermHighlightBackground(); static void saveColorMap(); static void resetColorMap(); static void setPalette (int, int, int, int);