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++)
listAdapter.widgets.add((new TextWidget()).text("List item "d ~ to!dstring(i)).styleId("LIST_ITEM"));
list.ownAdapter = listAdapter;
listAdapter.resetItemState(0, State.Enabled);
listAdapter.resetItemState(5, State.Enabled);
listAdapter.resetItemState(7, State.Enabled);
listAdapter.resetItemState(12, State.Enabled);
assert(list.itemEnabled(5) == false);
assert(list.itemEnabled(6) == true);
list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
list.selectItem(0);
tabs.addTab(list, "Long List"d);
}

View File

@ -240,6 +240,7 @@ enum State : uint {
Checked = 64,
Activated = 128,
WindowFocused = 256,
Default = 512, // widget is default for form (e.g. default button will be focused on show)
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
bool showChild(string ID, Visibility otherChildrenVisibility = Visibility.Invisible) {
bool showChild(string ID, Visibility otherChildrenVisibility = Visibility.Invisible, bool updateFocus = false) {
bool found = false;
Widget foundWidget = null;
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.compareId(ID)) {
item.visibility = Visibility.Visible;
foundWidget = item;
found = true;
} else {
item.visibility = otherChildrenVisibility;
}
}
if (foundWidget !is null && updateFocus)
foundWidget.setFocus();
return found;
}
}

View File

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

View File

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

View File

@ -323,14 +323,49 @@ class Widget {
@property bool 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() {
if (window is null)
return null;
if (!_focusable)
if (!visible)
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);
}
/// 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