main menu, key processing, focus handling improvements

This commit is contained in:
Vadim Lopatin 2014-05-07 15:21:14 +04:00
parent b684955132
commit bc9aeab14e
5 changed files with 139 additions and 14 deletions

View File

@ -22,6 +22,7 @@ Authors: $(WEB coolreader.org, Vadim Lopatin)
module dlangui.platforms.common.platform;
public import dlangui.core.events;
import dlangui.core.collections;
import dlangui.widgets.widget;
import dlangui.widgets.popup;
import dlangui.graphics.drawbuf;
@ -216,16 +217,28 @@ class Window {
if (oldFocus !is null)
oldFocus.resetState(State.Focused);
if (newFocus is null || isChild(newFocus)) {
_focusedWidget = newFocus;
if (_focusedWidget !is null) {
Log.d("new focus: ", _focusedWidget.id);
_focusedWidget.setState(State.Focused);
if (newFocus !is null) {
// when calling, setState(focused), window.focusedWidget is still previously focused widget
Log.d("new focus: ", newFocus.id);
newFocus.setState(State.Focused);
}
_focusedWidget = newFocus;
}
return _focusedWidget;
}
protected bool dispatchKeyEvent(Widget root, KeyEvent event) {
if (root.visibility != Visibility.Visible)
return false;
if (root.wantsKeyTracking) {
if (root.onKeyEvent(event))
return true;
}
for (int i = 0; i < root.childCount; i++) {
Widget w = root.child(i);
if (dispatchKeyEvent(w, event))
return true;
}
return false;
}
@ -244,6 +257,8 @@ class Window {
if (focus.onKeyEvent(event))
return true; // processed by focused widget
}
if (_mainWidget)
return dispatchKeyEvent(_mainWidget, event);
return false;
}

View File

@ -1853,9 +1853,11 @@ class EditBox : EditWidgetBase, OnScrollHandler {
_minFontSize = size;
return this;
}
@property int maxFontSize() {
return _maxFontSize;
}
@property EditBox maxFontSize(int size) {
_maxFontSize = size;
return this;

View File

@ -305,8 +305,10 @@ class ListWidget : WidgetGroup, OnScrollHandler {
else if (newIndex >= itemCount)
newIndex = wrapAround ? 0 : itemCount - 1;
if (newIndex != index) {
if (selectItem(newIndex))
if (selectItem(newIndex)) {
selectionChanged(_selectedItemIndex, index);
return true;
}
index = newIndex;
}
}
@ -314,6 +316,7 @@ class ListWidget : WidgetGroup, OnScrollHandler {
}
bool selectItem(int index, int disabledItemsSkipDirection) {
debug Log.d("selectItem ", index, " skipDirection=", disabledItemsSkipDirection);
if (index == -1 || disabledItemsSkipDirection == 0)
return selectItem(index);
int maxAttempts = itemCount;
@ -330,6 +333,7 @@ class ListWidget : WidgetGroup, OnScrollHandler {
}
bool selectItem(int index) {
debug Log.d("selectItem ", index);
if (_selectedItemIndex == index) {
updateSelectedItemFocus();
makeSelectionVisible();

View File

@ -214,6 +214,7 @@ class MenuWidgetBase : ListWidget {
protected MenuItem _item;
protected PopupMenu _openedMenu;
protected PopupWidget _openedPopup;
protected int _openedPopupIndex;
protected bool delegate(MenuItem item) _onMenuItemClickListener;
/// menu item click listener
@property bool delegate(MenuItem item) onMenuItemListener() { return _onMenuItemClickListener; }
@ -269,14 +270,17 @@ class MenuWidgetBase : ListWidget {
}
protected void onPopupClosed(PopupWidget p) {
bool undoSelection = _openedPopupIndex == _selectedItemIndex;
_openedPopup = null;
_openedMenu = null;
selectItem(-1);
setHoverItem(-1);
if (undoSelection) {
selectItem(-1);
setHoverItem(-1);
}
window.setFocus(this);
}
protected void openSubmenu(MenuItemWidget itemWidget, bool selectFirstItem) {
protected void openSubmenu(int index, MenuItemWidget itemWidget, bool selectFirstItem) {
if (_openedPopup !is null) {
_openedPopup.close();
}
@ -286,6 +290,7 @@ class MenuWidgetBase : ListWidget {
popup.flags = PopupFlags.CloseOnClickOutside;
_openedPopup = popup;
_openedMenu = popupMenu;
_openedPopupIndex = index;
window.setFocus(popupMenu);
if (selectFirstItem)
_openedMenu.selectItem(0);
@ -293,6 +298,7 @@ class MenuWidgetBase : ListWidget {
/// override to handle change of selection
override protected void selectionChanged(int index, int previouslySelectedItem = -1) {
debug Log.d("menu.selectionChanged ", index, ", ", previouslySelectedItem, " _selectedItemIndex=", _selectedItemIndex);
MenuItemWidget itemWidget = index >= 0 ? cast(MenuItemWidget)_adapter.itemWidget(index) : null;
MenuItemWidget prevWidget = previouslySelectedItem >= 0 ? cast(MenuItemWidget)_adapter.itemWidget(previouslySelectedItem) : null;
if (prevWidget !is null) {
@ -302,7 +308,7 @@ class MenuWidgetBase : ListWidget {
if (itemWidget !is null) {
if (itemWidget.item.isSubmenu()) {
if (_selectOnHover) {
openSubmenu(itemWidget, false);
openSubmenu(index, itemWidget, _orientation == Orientation.Horizontal); // for main menu, select first item
}
} else {
// normal item
@ -311,6 +317,7 @@ class MenuWidgetBase : ListWidget {
}
protected void onMenuItem(MenuItem item) {
debug Log.d("onMenuItem ", item.action.label);
if (_openedPopup !is null) {
_openedPopup.close();
_openedPopup = null;
@ -351,7 +358,7 @@ class MenuWidgetBase : ListWidget {
selectItem(-1);
selectOnHover = false;
} else {
openSubmenu(itemWidget, false);
openSubmenu(index, itemWidget, false);
selectOnHover = true;
}
} else {
@ -366,13 +373,17 @@ class MenuWidgetBase : ListWidget {
return cast(PopupWidget)parent;
}
protected int _menuToggleState;
protected Widget _menuTogglePreviousFocus;
/// list navigation using keys
override bool onKeyEvent(KeyEvent event) {
// TODO:
if (orientation == Orientation.Horizontal) {
if (event.action == KeyAction.KeyDown) {
}
// no special processing
} else {
// for vertical (popup) menu
if (!focused)
return false;
if (event.action == KeyAction.KeyDown) {
if (event.keyCode == KeyCode.LEFT) {
if (_parentMenu !is null) {
@ -392,7 +403,7 @@ class MenuWidgetBase : ListWidget {
} else if (event.keyCode == KeyCode.RIGHT) {
MenuItemWidget thisItem = selectedMenuItemWidget();
if (thisItem !is null && thisItem.item.isSubmenu) {
openSubmenu(thisItem, true);
openSubmenu(_selectedItemIndex, thisItem, true);
return true;
} else if (_parentMenu !is null && _parentMenu.orientation == Orientation.Horizontal) {
_parentMenu.moveSelection(1);
@ -420,8 +431,96 @@ class MainMenu : MenuWidgetBase {
styleId = "MAIN_MENU";
_clickOnButtonDown = true;
}
/// override and return true to track key events even when not focused
@property override bool wantsKeyTracking() {
return true;
}
protected int _menuToggleState;
protected Widget _menuTogglePreviousFocus;
/// return true if main menu is activated (focused or has open submenu)
@property bool activated() {
return focused || _openedPopup !is null;
}
/// bring focus to main menu, if not yet activated
void activate() {
debug Log.d("activating main menu");
if (activated)
return;
window.setFocus(this);
selectItem(0);
}
/// close and remove focus, if activated
void deactivate() {
debug Log.d("deactivating main menu");
if (!activated)
return;
if (_openedPopup !is null)
_openedPopup.close();
selectItem(-1);
setHoverItem(-1);
window.setFocus(_menuTogglePreviousFocus);
}
/// activate or deactivate main menu, return true if it has been activated
bool toggle() {
if (activated) {
// unfocus
deactivate();
return false;
} else {
// focus
activate();
return true;
}
}
/// override to handle focus changes
override protected void handleFocusChange(bool focused) {
if (focused && _openedPopup is null) {
// activating!
_menuTogglePreviousFocus = window.focusedWidget;
}
super.handleFocusChange(focused);
}
/// list navigation using keys
override bool onKeyEvent(KeyEvent event) {
// handle MainMenu activation / deactivation (Alt, Esc...)
bool toggleMenu = false;
bool isAlt = event.keyCode == KeyCode.ALT || event.keyCode == KeyCode.LALT || event.keyCode == KeyCode.RALT;
bool noOtherModifiers = !(event.flags & (KeyFlag.Shift | KeyFlag.Control));
if (event.action == KeyAction.KeyDown && event.keyCode == KeyCode.ESCAPE && event.flags == 0 && activated) {
deactivate();
return true;
}
if (event.action == KeyAction.KeyDown && isAlt && noOtherModifiers) {
_menuToggleState = 1;
} else if (event.action == KeyAction.KeyUp && isAlt && noOtherModifiers) {
if (_menuToggleState == 1)
toggleMenu = true;
_menuToggleState = 0;
} else {
_menuToggleState = 0;
}
if (toggleMenu) {
toggle();
return true;
}
if (!focused)
return false;
return super.onKeyEvent(event);
}
}
/// popup menu widget (vertical layout of items)
class PopupMenu : MenuWidgetBase {

View File

@ -480,6 +480,11 @@ class Widget {
return (window !is null && window.focusedWidget is this && (state & State.Focused));
}
/// override and return true to track key events even when not focused
@property bool wantsKeyTracking() {
return false;
}
protected bool _focusGroup;
/*****************************************
* When focus group is set for some parent widget, focus from one of containing widgets can be moved using keyboard only to one of other widgets containing in it and cannot bypass bounds of focusGroup.