list and focus handling fixes

This commit is contained in:
Vadim Lopatin 2014-04-16 16:07:59 +04:00
parent dd687b4433
commit a710eb5216
6 changed files with 82 additions and 9 deletions

View File

@ -139,12 +139,14 @@ extern (C) int UIAppMain(string[] args) {
for (int i = 0; i < 1000; i++) for (int i = 0; i < 1000; i++)
listAdapter.widgets.add((new TextWidget()).text("List item "d ~ to!dstring(i)).styleId("LIST_ITEM")); listAdapter.widgets.add((new TextWidget()).text("List item "d ~ to!dstring(i)).styleId("LIST_ITEM"));
list.ownAdapter = listAdapter; list.ownAdapter = listAdapter;
listAdapter.resetItemState(0, State.Enabled);
listAdapter.resetItemState(5, State.Enabled); listAdapter.resetItemState(5, State.Enabled);
listAdapter.resetItemState(7, State.Enabled); listAdapter.resetItemState(7, State.Enabled);
listAdapter.resetItemState(12, State.Enabled); listAdapter.resetItemState(12, State.Enabled);
assert(list.itemEnabled(5) == false); assert(list.itemEnabled(5) == false);
assert(list.itemEnabled(6) == true); assert(list.itemEnabled(6) == true);
list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
list.selectItem(0);
tabs.addTab(list, "Long List"d); tabs.addTab(list, "Long List"d);
} }

View File

@ -240,6 +240,7 @@ enum State : uint {
Checked = 64, Checked = 64,
Activated = 128, Activated = 128,
WindowFocused = 256, WindowFocused = 256,
Default = 512, // widget is default for form (e.g. default button will be focused on show)
Parent = 0x10000, // use parent's state Parent = 0x10000, // use parent's state
} }

View File

@ -363,17 +363,21 @@ class FrameLayout : WidgetGroup {
} }
/// make one of children (with specified ID) visible, for the rest, set visibility to otherChildrenVisibility /// make one of children (with specified ID) visible, for the rest, set visibility to otherChildrenVisibility
bool showChild(string ID, Visibility otherChildrenVisibility = Visibility.Invisible) { bool showChild(string ID, Visibility otherChildrenVisibility = Visibility.Invisible, bool updateFocus = false) {
bool found = false; bool found = false;
Widget foundWidget = null;
for (int i = 0; i < _children.count; i++) { for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i); Widget item = _children.get(i);
if (item.compareId(ID)) { if (item.compareId(ID)) {
item.visibility = Visibility.Visible; item.visibility = Visibility.Visible;
foundWidget = item;
found = true; found = true;
} else { } else {
item.visibility = otherChildrenVisibility; item.visibility = otherChildrenVisibility;
} }
} }
if (foundWidget !is null && updateFocus)
foundWidget.setFocus();
return found; return found;
} }
} }

View File

@ -224,13 +224,19 @@ class ListWidget : WidgetGroup, OnScrollHandler {
void makeSelectionVisible() { void makeSelectionVisible() {
if (_selectedItemIndex < 0) if (_selectedItemIndex < 0)
return; // no selection return; // no selection
if (needLayout) {
_makeSelectionVisibleOnNextLayout = true;
return;
}
makeItemVisible(_selectedItemIndex); makeItemVisible(_selectedItemIndex);
} }
protected bool _makeSelectionVisibleOnNextLayout;
/// ensure item is visible /// ensure item is visible
void makeItemVisible(int itemIndex) { void makeItemVisible(int itemIndex) {
if (itemIndex < 0 || itemIndex >= itemCount) if (itemIndex < 0 || itemIndex >= itemCount)
return; // no selection return; // no selection
Rect viewrc = Rect(0, 0, _clientRc.width, _clientRc.height); Rect viewrc = Rect(0, 0, _clientRc.width, _clientRc.height);
Rect scrolledrc = itemRect(itemIndex); Rect scrolledrc = itemRect(itemIndex);
if (scrolledrc.isInsideOf(viewrc)) // completely visible if (scrolledrc.isInsideOf(viewrc)) // completely visible
@ -284,7 +290,23 @@ class ListWidget : WidgetGroup, OnScrollHandler {
return true; return true;
} }
protected bool selectItem(int index) { bool selectItem(int index, int disabledItemsSkipDirection) {
if (index == -1 || disabledItemsSkipDirection == 0)
return selectItem(index);
int maxAttempts = itemCount;
for (int i = 0; i < maxAttempts; i++) {
if (selectItem(index))
return true;
index += disabledItemsSkipDirection > 0 ? 1 : -1;
if (index < 0)
index = itemCount - 1;
if (index >= itemCount)
index = 0;
}
return false;
}
bool selectItem(int index) {
if (_selectedItemIndex == index) { if (_selectedItemIndex == index) {
updateSelectedItemFocus(); updateSelectedItemFocus();
makeSelectionVisible(); makeSelectionVisible();
@ -530,6 +552,11 @@ class ListWidget : WidgetGroup, OnScrollHandler {
// calc item rectangles // calc item rectangles
updateItemPositions(); updateItemPositions();
if (_makeSelectionVisibleOnNextLayout) {
makeSelectionVisible();
_makeSelectionVisibleOnNextLayout = false;
}
} }
/// Draw widget at its position to buffer /// Draw widget at its position to buffer
@ -593,13 +620,17 @@ class ListWidget : WidgetGroup, OnScrollHandler {
} }
if (event.action == KeyAction.KeyDown) { if (event.action == KeyAction.KeyDown) {
if (event.keyCode == KeyCode.HOME) { if (event.keyCode == KeyCode.HOME) {
// select first item on HOME key // select first enabled item on HOME key
selectItem(0); selectItem(0, 1);
return true; return true;
} else if (event.keyCode == KeyCode.END) { } else if (event.keyCode == KeyCode.END) {
// select last item on END key // select last enabled item on END key
selectItem(itemCount - 1); selectItem(itemCount - 1, -1);
return true; return true;
} else if (event.keyCode == KeyCode.PAGEDOWN) {
// TODO
} else if (event.keyCode == KeyCode.PAGEUP) {
// TODO
} }
} }
return false; return false;

View File

@ -370,7 +370,7 @@ class TabHost : FrameLayout, TabHandler {
protected override void onTabChanged(string newActiveTabId, string previousTabId) { protected override void onTabChanged(string newActiveTabId, string previousTabId) {
if (newActiveTabId !is null) { if (newActiveTabId !is null) {
showChild(newActiveTabId); showChild(newActiveTabId, Visibility.Invisible, true);
} }
if (_onTabChanged !is null) if (_onTabChanged !is null)
_onTabChanged(newActiveTabId, previousTabId); _onTabChanged(newActiveTabId, previousTabId);

View File

@ -323,14 +323,49 @@ class Widget {
@property bool focused() { @property bool focused() {
return (window !is null && window.focusedWidget is this && (state & State.Focused)); return (window !is null && window.focusedWidget is this && (state & State.Focused));
} }
/// sets focus to this widget, returns previously focused widget /// returns true if this widget and all its parents are visible
@property bool visible() {
if (visibility != Visibility.Visible)
return false;
if (parent is null)
return true;
return parent.visible;
}
/// returns true if widget is focusable and visible
@property bool canFocus() {
return focusable && visible;
}
/// sets focus to this widget or suitable focusable child, returns previously focused widget
Widget setFocus() { Widget setFocus() {
if (window is null) if (window is null)
return null; return null;
if (!_focusable) if (!visible)
return window.focusedWidget; return window.focusedWidget;
if (!canFocus) {
Widget w = findFocusableChild(true);
if (!w)
w = findFocusableChild(false);
if (w)
return window.setFocus(w);
// try to find focusable child
return window.focusedWidget;
}
return window.setFocus(this); return window.setFocus(this);
} }
/// searches children for first focusable item, returns null if not found
Widget findFocusableChild(bool defaultOnly) {
for(int i = 0; i < childCount; i++) {
Widget w = child(i);
if (w.canFocus && (!defaultOnly || (w.state & State.Default) != 0))
return w;
w = w.findFocusableChild(defaultOnly);
if (w !is null)
return w;
}
if (canFocus)
return this;
return null;
}
// ======================================================= // =======================================================
// Events // Events