From dd0527a698cffc2a9ae8ed9a3c676952235b4b6a Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Thu, 8 May 2014 10:32:45 +0400 Subject: [PATCH] underline main menu item shortcuts when alt is pressed or main menu is active --- src/dlangui/core/types.d | 8 +++- src/dlangui/graphics/fonts.d | 6 +-- src/dlangui/platforms/common/platform.d | 29 +++++++++++---- src/dlangui/platforms/sdl/sdlapp.d | 49 +++++++++++++++++++++---- src/dlangui/widgets/menu.d | 48 +++++++++++++++++++++--- src/dlangui/widgets/styles.d | 8 +++- src/dlangui/widgets/widget.d | 26 +++++++++++-- 7 files changed, 145 insertions(+), 29 deletions(-) diff --git a/src/dlangui/core/types.d b/src/dlangui/core/types.d index d1402c03..82f7d0fc 100644 --- a/src/dlangui/core/types.d +++ b/src/dlangui/core/types.d @@ -296,7 +296,13 @@ enum State : uint { Parent = 0x10000, // use parent's state } - +/// uppercase unicode character +dchar dcharToUpper(dchar ch) { + // TODO: support non-ascii letters + if (ch >= 'a' && ch <= 'z') + return ch - 'a' + 'A'; + return ch; +} version (Windows) { immutable char PATH_DELIMITER = '\\'; diff --git a/src/dlangui/graphics/fonts.d b/src/dlangui/graphics/fonts.d index c3d12e34..312fefae 100644 --- a/src/dlangui/graphics/fonts.d +++ b/src/dlangui/graphics/fonts.d @@ -244,7 +244,7 @@ class Font : RefCountedObject { charsMeasured = i + 1; x = tabPosition; continue; - } else if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys))) { + } else if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) { pwidths[i] = x; continue; // skip '&' in hot key when measuring } @@ -319,8 +319,8 @@ class Font : RefCountedObject { int underlineY = y + _baseline + underlineHeight * 2; for (int i = 0; i < charsMeasured; i++) { dchar ch = text[i]; - if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys))) { - if (textFlags & TextFlag.UnderlineHotKeys) + if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) { + if (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.UnderlineHotKeysWhenAltPressed)) underline = true; // turn ON underline for hot key continue; // skip '&' in hot key when measuring } diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 8de23010..3be91d3a 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -32,13 +32,15 @@ private import dlangui.graphics.gldrawbuf; class Window { protected int _dx; protected int _dy; + protected uint _keyboardModifiers; protected uint _backgroundColor; protected Widget _mainWidget; - @property uint backgroundColor() { return _backgroundColor; } + @property uint backgroundColor() const { return _backgroundColor; } @property void backgroundColor(uint color) { _backgroundColor = color; } - @property int width() { return _dx; } - @property int height() { return _dy; } - @property Widget mainWidget() { return _mainWidget; } + @property int width() const { return _dx; } + @property int height() const { return _dy; } + @property uint keyboardModifiers() const { return _keyboardModifiers; } + @property Widget mainWidget() { return _mainWidget; } @property void mainWidget(Widget widget) { if (_mainWidget !is null) _mainWidget.window = null; @@ -244,13 +246,24 @@ class Window { /// dispatch keyboard event bool dispatchKeyEvent(KeyEvent event) { + bool res = false; + if (event.action == KeyAction.KeyDown || event.action == KeyAction.KeyUp) { + _keyboardModifiers = event.flags; + if (event.keyCode == KeyCode.ALT || event.keyCode == KeyCode.LALT || event.keyCode == KeyCode.RALT) { + Log.d("ALT key: keyboardModifiers = ", _keyboardModifiers); + if (_mainWidget) { + _mainWidget.invalidate(); + res = true; + } + } + } if (event.action == KeyAction.Text) { // filter text if (event.text.length < 1) - return false; + return res; dchar ch = event.text[0]; if (ch < ' ' || ch == 0x7F) // filter out control symbols - return false; + return res; } Widget focus = focusedWidget; if (focus !is null) { @@ -258,8 +271,8 @@ class Window { return true; // processed by focused widget } if (_mainWidget) - return dispatchKeyEvent(_mainWidget, event); - return false; + return dispatchKeyEvent(_mainWidget, event) || res; + return res; } protected bool dispatchMouseEvent(Widget root, MouseEvent event) { diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d index 2ca0ed66..bf6c325d 100644 --- a/src/dlangui/platforms/sdl/sdlapp.d +++ b/src/dlangui/platforms/sdl/sdlapp.d @@ -446,17 +446,17 @@ version(USE_SDL) { if (flags & KMOD_ALT) res |= KeyFlag.Alt; if (flags & KMOD_RCTRL) - res |= KeyFlag.RControl; + res |= KeyFlag.RControl | KeyFlag.Control; if (flags & KMOD_RSHIFT) - res |= KeyFlag.RShift; + res |= KeyFlag.RShift | KeyFlag.Shift; if (flags & KMOD_RALT) - res |= KeyFlag.RAlt; + res |= KeyFlag.RAlt | KeyFlag.Alt; if (flags & KMOD_LCTRL) - res |= KeyFlag.LControl; + res |= KeyFlag.LControl | KeyFlag.Control; if (flags & KMOD_LSHIFT) - res |= KeyFlag.LShift; + res |= KeyFlag.LShift | KeyFlag.Shift; if (flags & KMOD_LALT) - res |= KeyFlag.LAlt; + res |= KeyFlag.LAlt | KeyFlag.Alt; return res; } @@ -471,10 +471,45 @@ version(USE_SDL) { } return res; } - bool processKeyEvent(KeyAction action, uint keyCode, uint flags) { + + bool processKeyEvent(KeyAction action, uint keyCode, uint flags) { Log.d("processKeyEvent ", action, " SDL key=0x", format("%08x", keyCode), " SDL flags=0x", format("%08x", flags)); keyCode = convertKeyCode(keyCode); flags = convertKeyFlags(flags); + if (action == KeyAction.KeyDown) { + switch(keyCode) { + case KeyCode.ALT: + flags |= KeyFlag.Alt; + break; + case KeyCode.RALT: + flags |= KeyFlag.Alt | KeyFlag.RAlt; + break; + case KeyCode.LALT: + flags |= KeyFlag.Alt | KeyFlag.LAlt; + break; + case KeyCode.CONTROL: + flags |= KeyFlag.Control; + break; + case KeyCode.RCONTROL: + flags |= KeyFlag.Control | KeyFlag.RControl; + break; + case KeyCode.LCONTROL: + flags |= KeyFlag.Control | KeyFlag.LControl; + break; + case KeyCode.SHIFT: + flags |= KeyFlag.Shift; + break; + case KeyCode.RSHIFT: + flags |= KeyFlag.Shift | KeyFlag.RShift; + break; + case KeyCode.LSHIFT: + flags |= KeyFlag.Shift | KeyFlag.LShift; + break; + default: + break; + } + } + Log.d("processKeyEvent ", action, " converted key=0x", format("%08x", keyCode), " converted flags=0x", format("%08x", flags)); bool res = dispatchKeyEvent(new KeyEvent(action, keyCode, flags)); // if ((keyCode & 0x10000) && (keyCode & 0xF000) != 0xF000) { diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index 8197a7ac..015a3adb 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -43,7 +43,33 @@ class MenuItem { MenuItem subitem(int index) { return _subitems[index]; } - /// adds submenu item + + /// get hotkey character from label (e.g. 'F' for item labeled "&File"), 0 if no hotkey + dchar getHotkey() { + dstring s = label; + dchar ch = 0; + for (int i = 0; i < s.length - 1; i++) { + if (s[i] == '&') { + ch = s[i + 1]; + break; + } + } + return dcharToUpper(ch); + } + + /// find subitem by hotkey character, returns subitem index, -1 if not found + int findSubitemByHotkey(dchar ch) { + if (!ch) + return -1; + ch = dcharToUpper(ch); + for (int i = 0; i < _subitems.length; i++) { + if (_subitems[i].getHotkey() == ch) + return i; + } + return -1; + } + + /// adds submenu item MenuItem add(MenuItem subitem) { _subitems ~= subitem; return this; @@ -85,6 +111,7 @@ class MenuItem { /// widget to draw menu item class MenuItemWidget : WidgetGroup { + protected bool _mainMenu; protected MenuItem _item; protected ImageWidget _icon; protected TextWidget _accel; @@ -180,8 +207,9 @@ class MenuItemWidget : WidgetGroup { } } - this(MenuItem item) { + this(MenuItem item, bool mainMenu) { id="menuitem"; + _mainMenu = mainMenu; _item = item; styleId = "MENU_ITEM"; // icon @@ -193,7 +221,7 @@ class MenuItemWidget : WidgetGroup { // label _label = new TextWidget("MENU_LABEL"); _label.text = _item.label; - _label.styleId = "MENU_LABEL"; + _label.styleId = _mainMenu ? "MAIN_MENU_LABEL" : "MENU_LABEL"; addChild(_label); // accelerator dstring acc = _item.acceleratorText; @@ -230,9 +258,10 @@ class MenuWidgetBase : ListWidget { WidgetListAdapter adapter = new WidgetListAdapter(); for (int i=0; i < _item.subitemCount; i++) { MenuItem subitem = _item.subitem(i); - MenuItemWidget widget = new MenuItemWidget(subitem); + MenuItemWidget widget = new MenuItemWidget(subitem, orientation == Orientation.Horizontal); if (orientation == Orientation.Horizontal) widget.styleId = "MAIN_MENU_ITEM"; + widget.parent = this; adapter.widgets.add(widget); } ownAdapter = adapter; @@ -444,7 +473,16 @@ class MainMenu : MenuWidgetBase { return true; } - protected int _menuToggleState; + /// get text flags (bit set of TextFlag enum values) + @property override uint textFlags() { + // override text flags for main menu + if (activated) + return TextFlag.UnderlineHotKeys | TextFlag.HotKeys; + else + return TextFlag.UnderlineHotKeysWhenAltPressed | TextFlag.HotKeys; + } + + protected int _menuToggleState; protected Widget _menuTogglePreviousFocus; diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index 346d989b..16836638 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -37,6 +37,7 @@ immutable ubyte FONT_STYLE_ITALIC = 0x01; /// use as widget.layout() param to avoid applying of parent size immutable int SIZE_UNSPECIFIED = int.max; immutable uint TEXT_FLAGS_UNSPECIFIED = uint.max; +immutable uint TEXT_FLAGS_USE_PARENT = uint.max - 1; immutable int FILL_PARENT = int.max - 1; immutable int WRAP_CONTENT = int.max - 2; @@ -60,8 +61,10 @@ enum TextFlag : uint { HotKeys = 1, /// underline hot key when drawing UnderlineHotKeys = 2, + /// underline hot key when drawing + UnderlineHotKeysWhenAltPressed = 4, /// underline text when drawing - Underline = 4 + Underline = 8 } class DrawableAttribute { @@ -747,7 +750,7 @@ Theme createDefaultTheme() { //res.dumpStats(); Style mainMenu = res.createSubstyle("MAIN_MENU").backgroundColor(0xEFEFF2).layoutWidth(FILL_PARENT); - Style mainMenuItem = res.createSubstyle("MAIN_MENU_ITEM").setPadding(4,2,4,2).backgroundImageId("main_menu_item_background"); + Style mainMenuItem = res.createSubstyle("MAIN_MENU_ITEM").setPadding(4,2,4,2).backgroundImageId("main_menu_item_background").textFlags(TEXT_FLAGS_USE_PARENT); Style menuItem = res.createSubstyle("MENU_ITEM").setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ; menuItem.createState(State.Focused, State.Focused).backgroundColor(0x40C0C000); menuItem.createState(State.Pressed, State.Pressed).backgroundColor(0x4080C000); @@ -755,6 +758,7 @@ Theme createDefaultTheme() { menuItem.createState(State.Hovered, State.Hovered).backgroundColor(0xC0FFFF00); res.createSubstyle("MENU_ICON").setMargins(2,2,2,2).alignment(Align.VCenter|Align.Left); res.createSubstyle("MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TextFlag.UnderlineHotKeys); + res.createSubstyle("MAIN_MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TEXT_FLAGS_USE_PARENT); res.createSubstyle("MENU_ACCEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left); Style transparentButtonBackground = res.createSubstyle("TRANSPARENT_BUTTON_BACKGROUND").backgroundImageId("transparent_button_background").setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ; diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 32342ac9..8d51cada 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -300,12 +300,32 @@ class Widget { return this; } /// get text flags (bit set of TextFlag enum values) - @property uint textFlags() const { return stateStyle.textFlags; } + @property uint textFlags() { + uint res = stateStyle.textFlags; + if (res == TEXT_FLAGS_USE_PARENT) + if (parent) + res = parent.textFlags; + else + res = 0; + if (res & TextFlag.UnderlineHotKeysWhenAltPressed) { + uint modifiers = 0; + if (window !is null) + modifiers = window.keyboardModifiers; + bool altPressed = (modifiers & (KeyFlag.Alt | KeyFlag.LAlt | KeyFlag.RAlt)) != 0; + if (!altPressed) { + res = (res & ~(TextFlag.UnderlineHotKeysWhenAltPressed | TextFlag.UnderlineHotKeys)) | TextFlag.HotKeys; + } else { + res |= TextFlag.UnderlineHotKeys; + } + } + + return res; + } /// set text flags (bit set of TextFlag enum values) @property Widget textFlags(uint value) { ownStyle.textFlags = value; - bool oldHotkeys = (ownStyle.textFlags & (TextFlag.HotKeys | TextFlag.UnderlineHotKeys)) != 0; - bool newHotkeys = (value & (TextFlag.HotKeys | TextFlag.UnderlineHotKeys)) != 0; + bool oldHotkeys = (ownStyle.textFlags & (TextFlag.HotKeys | TextFlag.UnderlineHotKeys | TextFlag.UnderlineHotKeysWhenAltPressed)) != 0; + bool newHotkeys = (value & (TextFlag.HotKeys | TextFlag.UnderlineHotKeys | TextFlag.UnderlineHotKeysWhenAltPressed)) != 0; if (oldHotkeys != newHotkeys) requestLayout(); else