Tab control: support navigation between tabs with Ctrl+Tab / Ctrl+Shift+Tab

This commit is contained in:
Vadim Lopatin 2015-01-15 14:11:20 +03:00
parent de9bdf2965
commit 4c1615ffa6
2 changed files with 89 additions and 10 deletions

View File

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

View File

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