diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 2b1dab4a..56d4a55f 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -276,6 +276,7 @@ class Window { return _focusedWidget; } + /// dispatch key event to widgets which have wantsKeyTracking == true protected bool dispatchKeyEvent(Widget root, KeyEvent event) { if (root.visibility != Visibility.Visible) return false; @@ -313,9 +314,12 @@ class Window { return res; } Widget focus = focusedWidget; - if (focus !is null) { + while (focus) { if (focus.onKeyEvent(event)) return true; // processed by focused widget + if (focus.focusGroup) + break; + focus = focus.parent; } if (_mainWidget) { if (dispatchKeyEvent(_mainWidget, event)) diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index 2be382c1..98743cd8 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -30,8 +30,10 @@ interface TabHandler { void onTabChanged(string newActiveTabId, string previousTabId); } + /// tab item metadata class TabItem { + private static long _lastAccessCounter; private string _iconRes; private string _id; private UIString _label; @@ -45,7 +47,7 @@ class TabItem { _id = id; _label = labelRes; _iconRes = iconRes; - _lastAccessTs = std.datetime.Clock.currStdTime; + _lastAccessTs = _lastAccessCounter++; } @property string iconId() const { return _iconRes; } @property string id() const { return _id; } @@ -54,7 +56,9 @@ class TabItem { @property TabItem id(string id) { _id = id; return this; } @property long lastAccessTs() { return _lastAccessTs; } @property void lastAccessTs(long ts) { _lastAccessTs = ts; } - void updateAccessTs() { _lastAccessTs = std.datetime.Clock.currStdTime; } + void updateAccessTs() { + _lastAccessTs = _lastAccessCounter++; //std.datetime.Clock.currStdTime; + } } /// tab item widget - to show tab header @@ -123,6 +127,10 @@ class TabItemList { return null; return _list[index]; } + /// get item by index + TabItem opIndex(int index) { + return get(index); + } /// get item by id TabItem get(string id) { int idx = indexById(id); @@ -130,6 +138,10 @@ class TabItemList { return null; return _list[idx]; } + /// get item by id + TabItem opIndex(string id) { + return get(id); + } @property int length() const { return _len; } /// append new item TabItemList add(TabItem item) { @@ -215,6 +227,7 @@ class TabControl : WidgetGroupDefaultDrawing { static bool accessTimeComparator(TabItemWidget a, TabItemWidget b) { return (a.tabItem.lastAccessTs > b.tabItem.lastAccessTs); } + protected TabItemWidget[] sortedItems() { _sortedItems.length = _items.length; for (int i = 0; i < _items.length; i++) @@ -222,6 +235,27 @@ class TabControl : WidgetGroupDefaultDrawing { std.algorithm.sort!(accessTimeComparator)(_sortedItems); return _sortedItems; } + + /// find next or previous tab index, based on access time + int getNextItemIndex(int direction) { + if (_items.length == 0) + return -1; + if (_items.length == 1) + return 0; + TabItemWidget[] items = sortedItems(); + for (int i = 0; i < items.length; i++) { + if (items[i].id == _selectedTabId) { + int next = i + direction; + if (next < 0) + next = cast(int)(items.length - 1); + if (next >= items.length) + next = 0; + return _items.indexById(items[next].id); + } + } + return -1; + } + /// remove tab TabControl removeTab(string id) { int index = _items.indexById(id); @@ -261,7 +295,7 @@ class TabControl : WidgetGroupDefaultDrawing { string id = source.id; int index = tabIndex(id); if (index >= 0) { - selectTab(index); + selectTab(index, true); } return true; } @@ -341,7 +375,13 @@ class TabControl : WidgetGroupDefaultDrawing { protected string _selectedTabId; - void selectTab(int index) { + void updateAccessTs() { + int index = _items.indexById(_selectedTabId); + if (index >= 0) + _items[index].updateAccessTs(); + } + + void selectTab(int index, bool updateAccess) { if (_children.get(index + 1).compareId(_selectedTabId)) return; // already selected string previousSelectedTab = _selectedTabId; @@ -349,6 +389,8 @@ class TabControl : WidgetGroupDefaultDrawing { if (index == i - 1) { _children.get(i).state = State.Selected; _selectedTabId = _children.get(i).id; + if (updateAccess) + updateAccessTs(); } else { _children.get(i).state = State.Normal; } @@ -356,6 +398,7 @@ class TabControl : WidgetGroupDefaultDrawing { if (onTabChangedListener.assigned) onTabChangedListener(_selectedTabId, previousSelectedTab); } + } /// container for widgets controlled by TabControl @@ -411,7 +454,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 + //widget.focusGroup = true; // doesn't allow move focus outside of tab content addChild(widget); return this; } @@ -425,10 +468,10 @@ class TabHost : FrameLayout, TabHandler { return this; } /// select tab - void selectTab(string ID) { + void selectTab(string ID, bool updateAccess) { int index = _tabControl.tabIndex(ID); if (index != -1) { - _tabControl.selectTab(index); + _tabControl.selectTab(index, updateAccess); } } // /// request relayout of widget and its children @@ -463,6 +506,7 @@ class TabWidget : VerticalLayout, TabHandler { styleId = STYLE_TAB_WIDGET; addChild(_tabControl); addChild(_tabHost); + focusGroup = true; } /// signal of tab change (e.g. by clicking on tab header) @@ -494,8 +538,13 @@ class TabWidget : VerticalLayout, TabHandler { } /// select tab - void selectTab(string ID) { - _tabHost.selectTab(ID); + void selectTab(string ID, bool updateAccess = true) { + _tabHost.selectTab(ID, updateAccess); + } + + /// select tab + void selectTab(int index, bool updateAccess = true) { + _tabControl.selectTab(index, updateAccess); } /// returns tab item by id (null if index out of range) @@ -510,4 +559,30 @@ class TabWidget : VerticalLayout, TabHandler { int tabIndex(string id) { return _tabControl.tabIndex(id); } + + private bool _tabNavigationInProgress; + + /// process key event, return true if event is processed. + override bool onKeyEvent(KeyEvent event) { + if (_tabNavigationInProgress) { + if (event.action == KeyAction.KeyDown || event.action == KeyAction.KeyUp) { + if (!(event.flags & KeyFlag.Control)) { + _tabNavigationInProgress = false; + _tabControl.updateAccessTs(); + } + } + } + if (event.action == KeyAction.KeyDown) { + if (event.keyCode == KeyCode.TAB && (event.flags & KeyFlag.Control)) { + // support Ctrl+Tab and Ctrl+Shift+Tab for navigation + _tabNavigationInProgress = true; + int direction = (event.flags & KeyFlag.Shift) ? - 1 : 1; + int index = _tabControl.getNextItemIndex(direction); + if (index >= 0) + selectTab(index, false); + return true; + } + } + return super.onKeyEvent(event); + } }