From 41aca2c44fa5ffc42df778da79ad21aa3ebeb7fd Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Fri, 25 Apr 2014 14:20:04 +0400 Subject: [PATCH] focus movement using Tab/Shift+Tab) --- src/dlangui/widgets/editors.d | 3 ++ src/dlangui/widgets/tabs.d | 1 + src/dlangui/widgets/widget.d | 99 ++++++++++++++++++++++++++++++----- 3 files changed, 90 insertions(+), 13 deletions(-) diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 8f0e25ff..fd87fa46 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -857,6 +857,9 @@ class EditWidgetBase : WidgetGroup, EditableContentListener { new Action(EditorActions.DocumentEnd, KeyCode.END, KeyFlag.Control), new Action(EditorActions.SelectDocumentEnd, KeyCode.END, KeyFlag.Control | KeyFlag.Shift), + new Action(EditorActions.ScrollLineUp, KeyCode.UP, KeyFlag.Control), + new Action(EditorActions.ScrollLineDown, KeyCode.DOWN, KeyFlag.Control), + new Action(EditorActions.InsertNewLine, KeyCode.RETURN, 0), new Action(EditorActions.InsertNewLine, KeyCode.RETURN, KeyFlag.Shift), new Action(EditorActions.PrependNewLine, KeyCode.RETURN, KeyFlag.Control), diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index 79c6e2e3..fa7dbbbf 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -415,6 +415,7 @@ class TabHost : FrameLayout, TabHandler { assert(widget.id !is null, "ID for tab host page is mandatory"); assert(_children.indexOf(id) == -1, "duplicate ID for tab host page"); _tabControl.addTab(widget.id, label, iconId, enableCloseButton); + widget.focusGroup = true; // doesn't allow move focus outside of tab content addChild(widget); return this; } diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index e3361b0e..30649248 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -482,41 +482,114 @@ class Widget { return p; } - private void findFocusableChildren(ref Widget[] results, Widget widgetToExclude) { + private static class TabOrderInfo { + Widget widget; + uint tabOrder; + uint childOrder; + Rect rect; + this(Widget widget, Rect rect) { + this.widget = widget; + this.tabOrder = widget.thisOrParentTabOrder(); + this.rect = widget.pos; + } + override int opCmp(Object obj) { + TabOrderInfo v = cast(TabOrderInfo)obj; + if (tabOrder != 0 && v.tabOrder !=0) { + if (tabOrder < v.tabOrder) + return -1; + if (tabOrder > v.tabOrder) + return 1; + } + // place items with tabOrder 0 after items with tabOrder non-0 + if (tabOrder != 0) + return -1; + if (v.tabOrder != 0) + return 1; + if (childOrder < v.childOrder) + return -1; + if (childOrder > v.childOrder) + return 1; + return 0; + } + } + + private void findFocusableChildren(ref TabOrderInfo[] results, Rect clipRect) { if (visibility != Visibility.Visible) return; - if (widgetToExclude is this) - return; // doesn't include + Rect rc = _pos; + applyMargins(rc); + applyPadding(rc); + if (!rc.intersects(clipRect)) + return; // out of clip rectangle if (focusable) { - results ~= this; + TabOrderInfo item = new TabOrderInfo(this, rc); + results ~= item; return; } + rc.intersect(clipRect); for (int i = 0; i < childCount(); i++) { - child(i).findFocusableChildren(results, widgetToExclude); + child(i).findFocusableChildren(results, rc); } } /// find all focusables belonging to the same focusGroup as this widget (does not include current widget). /// usually to be called for focused widget to get possible alternatives to navigate to - private Widget[] findFocusables() { - Widget[] result; + private TabOrderInfo[] findFocusables() { + TabOrderInfo[] result; Widget group = focusGroupWidget(); - group.findFocusableChildren(result, this); + group.findFocusableChildren(result, group.pos); + for (ushort i = 0; i < result.length; i++) + result[i].childOrder = i + 1; + sort(result); return result; } + protected ushort _tabOrder; + /// tab order - hint for focus movement using Tab/Shift+Tab + @property ushort tabOrder() { return _tabOrder; } + @property Widget tabOrder(ushort tabOrder) { _tabOrder = tabOrder; return this; } + private int thisOrParentTabOrder() { + if (_tabOrder) + return _tabOrder; + if (!parent) + return 0; + return parent.thisOrParentTabOrder; + } + /// call on focused widget, to find best private Widget findNextFocusWidget(FocusMovement direction) { if (direction == FocusMovement.None) return this; - Widget[] focusables = findFocusables(); + TabOrderInfo[] focusables = findFocusables(); if (!focusables.length) return null; + int myIndex = -1; + for (int i = 0; i < focusables.length; i++) { + if (focusables[i].widget is this) { + myIndex = i; + break; + } + } + if (myIndex == -1) + return null; // not found myself if (focusables.length == 1) - return focusables[0]; // single option - use it - Rect currentRect = pos; - // TODO: - return focusables[0]; + return focusables[0].widget; // single option - use it + if (direction == FocusMovement.Next) { + // move forward + int index = myIndex + 1; + if (index >= focusables.length) + index = 0; + return focusables[index].widget; + } else if (direction == FocusMovement.Previous) { + // move back + int index = myIndex - 1; + if (index < 0) + index = cast(int)focusables.length - 1; + return focusables[index].widget; + } else { + // Left, Right, Up, Down + return focusables[0].widget; + } } bool handleMoveFocusUsingKeys(KeyEvent event) {