Add sub-menu support

This commit is contained in:
Markus Gans 2015-11-22 21:41:18 +01:00
parent 4778258364
commit 101c9fcc74
12 changed files with 216 additions and 95 deletions

View File

@ -1,4 +1,10 @@
2015-11-12 Markus Gans <guru.mail@muenster.de>
2015-11-22 Markus Gans <guru.mail@muenster.de>
* Add sub-menu support
2015-11-19 Markus Gans <guru.mail@muenster.de>
* Add the missing resetXTermHighlightBackground method
2015-11-15 Markus Gans <guru.mail@muenster.de>
* Add two new classes FCheckMenuItem and FRadioMenuItem
for menu check marks and menu option marks (bullets)

View File

@ -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;

View File

@ -69,7 +69,7 @@ void FCheckBox::drawCheckButton()
else
{
print ('[');
print ('x');
print (fc::Times); // Times ×
print (']');
}
}

View File

@ -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

View File

@ -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<char*>("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() )
{
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,11 +772,17 @@ void FMenu::onKeyPress (FKeyEvent* ev)
if ( hasSelectedItem() )
{
FMenuItem* sel_item = getSelectedItem();
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() )
FMenu* smenu = reinterpret_cast<FMenu*>(getSuperMenu());
hideSubMenus();
else
keypressMenuBar(ev); // select previous menu
ev->accept();
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();
hide();
if ( isSubMenu() )
{
FMenu* smenu = reinterpret_cast<FMenu*>(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<FMenuItem*>::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();
if ( ! isSubMenu() )
{
FMenu* open_menu = static_cast<FMenu*>(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<FMenuItem*>(widget);
if ( menuitem->hasMenu() )
{
FMenu* menu = menuitem->getMenu();
if ( ! menu->isVisible() )
{
FMenu* open_menu = static_cast<FMenu*>(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);
}
}
}
//----------------------------------------------------------------------

View File

@ -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;

View File

@ -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);

View File

@ -67,25 +67,6 @@ void FMenuList::unselectItem()
setSelectedItem(0);
}
//----------------------------------------------------------------------
bool FMenuList::hasSelectedItem()
{
if ( ! itemlist.empty() )
{
std::vector<FMenuItem*>::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)
{

View File

@ -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;

View File

@ -70,7 +70,7 @@ void FRadioButton::drawRadioButton()
else
{
print ('(');
print (fc::Bullet); // Bullet •
print (fc::BlackCircle); // BlackCircle ●
print (')');
}
}

View File

@ -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()
{

View File

@ -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);