From a710eb5216208fed489db11ac8895f9022339371 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Wed, 16 Apr 2014 16:07:59 +0400 Subject: [PATCH] list and focus handling fixes --- examples/example1/main.d | 2 ++ src/dlangui/core/types.d | 1 + src/dlangui/widgets/layouts.d | 6 ++++- src/dlangui/widgets/lists.d | 41 ++++++++++++++++++++++++++++++----- src/dlangui/widgets/tabs.d | 2 +- src/dlangui/widgets/widget.d | 39 +++++++++++++++++++++++++++++++-- 6 files changed, 82 insertions(+), 9 deletions(-) diff --git a/examples/example1/main.d b/examples/example1/main.d index a7e4edec..939111ce 100644 --- a/examples/example1/main.d +++ b/examples/example1/main.d @@ -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); } diff --git a/src/dlangui/core/types.d b/src/dlangui/core/types.d index ed0ef399..a4ea524b 100644 --- a/src/dlangui/core/types.d +++ b/src/dlangui/core/types.d @@ -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 } diff --git a/src/dlangui/widgets/layouts.d b/src/dlangui/widgets/layouts.d index bc8ba354..913dc4c5 100644 --- a/src/dlangui/widgets/layouts.d +++ b/src/dlangui/widgets/layouts.d @@ -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; } } diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index 6dc507c0..4b8ea5ba 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -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; diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index 5909abec..472b9875 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -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); diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 0a4c3755..19163e67 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -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