diff --git a/examples/example1/main.d b/examples/example1/main.d index 24ad9f60..a7e4edec 100644 --- a/examples/example1/main.d +++ b/examples/example1/main.d @@ -139,6 +139,11 @@ 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(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); tabs.addTab(list, "Long List"d); } diff --git a/src/dlangui/core/types.d b/src/dlangui/core/types.d index eb1b64ed..ed0ef399 100644 --- a/src/dlangui/core/types.d +++ b/src/dlangui/core/types.d @@ -230,7 +230,7 @@ wstring fromWStringz(const(wchar[]) s) { /// widget state flags - bits enum State : uint { /// state not specified / normal - Normal = 0, + Normal = 4, // Normal is Enabled Pressed = 1, Focused = 2, Enabled = 4, diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index 87ce0d12..6dc507c0 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -92,6 +92,8 @@ class ListWidget : WidgetGroup, OnScrollHandler { /// returns rectangle for item (not scrolled, first item starts at 0,0) Rect itemRectNoScroll(int index) { + if (index < 0 || index >= _itemRects.length) + return Rect.init; Rect res; res = _itemRects[index]; return res; @@ -99,6 +101,8 @@ class ListWidget : WidgetGroup, OnScrollHandler { /// returns rectangle for item (scrolled) Rect itemRect(int index) { + if (index < 0 || index >= _itemRects.length) + return Rect.init; Rect res = itemRectNoScroll(index); if (_orientation == Orientation.Horizontal) { res.left -= _scrollPosition; @@ -154,6 +158,13 @@ class ListWidget : WidgetGroup, OnScrollHandler { return null; } + /// returns true if item with corresponding index is enabled + bool itemEnabled(int index) { + if (_adapter !is null && index >= 0 && index < itemCount) + return (_adapter.itemState(index) & State.Enabled) != 0; + return false; + } + void onAdapterChanged() { requestLayout(); } @@ -243,32 +254,44 @@ class ListWidget : WidgetGroup, OnScrollHandler { } /// move selection - void moveSelection(int direction, bool wrapAround = true) { + bool moveSelection(int direction, bool wrapAround = true) { if (itemCount <= 0) - return; - if (_selectedItemIndex < 0) { - // no previous selection - if (direction > 0) - selectItem(wrapAround ? 0 : itemCount - 1); - else - selectItem(wrapAround ? itemCount - 1 : 0); - return; + return false; + int maxAttempts = itemCount - 1; + int index = _selectedItemIndex; + for (int i = 0; i < maxAttempts; i++) { + int newIndex = 0; + if (index < 0) { + // no previous selection + if (direction > 0) + newIndex = wrapAround ? 0 : itemCount - 1; + else + newIndex = wrapAround ? itemCount - 1 : 0; + } else { + // step + newIndex = index + direction; + } + if (newIndex < 0) + newIndex = wrapAround ? itemCount - 1 : 0; + else if (newIndex >= itemCount) + newIndex = wrapAround ? 0 : itemCount - 1; + if (newIndex != index) { + if (selectItem(newIndex)) + return true; + index = newIndex; + } } - int newIndex = _selectedItemIndex + direction; - if (newIndex < 0) - newIndex = wrapAround ? itemCount - 1 : 0; - else if (newIndex >= itemCount) - newIndex = wrapAround ? 0 : itemCount - 1; - if (newIndex != _selectedItemIndex) - selectItem(newIndex); + return true; } - protected void selectItem(int index) { + protected bool selectItem(int index) { if (_selectedItemIndex == index) { updateSelectedItemFocus(); makeSelectionVisible(); - return; + return true; } + if (index != -1 && !itemEnabled(index)) + return false; if (_selectedItemIndex != -1) { _adapter.resetItemState(_selectedItemIndex, State.Selected | State.Focused); invalidate(); @@ -279,6 +302,7 @@ class ListWidget : WidgetGroup, OnScrollHandler { _adapter.setItemState(_selectedItemIndex, State.Selected | (state & State.Focused)); invalidate(); } + return true; } ~this() { @@ -567,6 +591,17 @@ class ListWidget : WidgetGroup, OnScrollHandler { moveSelection(navigationDelta); return true; } + if (event.action == KeyAction.KeyDown) { + if (event.keyCode == KeyCode.HOME) { + // select first item on HOME key + selectItem(0); + return true; + } else if (event.keyCode == KeyCode.END) { + // select last item on END key + selectItem(itemCount - 1); + return true; + } + } return false; //if (_selectedItemIndex != -1 && event.action == KeyAction.KeyUp && (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN)) { // itemClicked(_selectedItemIndex); @@ -624,19 +659,23 @@ class ListWidget : WidgetGroup, OnScrollHandler { itemrc.bottom += rc.top - scrollOffset.y; if (itemrc.isPointInside(Point(event.x, event.y))) { if ((event.flags & (MouseFlag.LButton || MouseFlag.RButton)) || _selectOnHover) { - if (_selectedItemIndex != i) { + if (_selectedItemIndex != i && itemEnabled(i)) { int prevSelection = _selectedItemIndex; selectItem(i); setHoverItem(-1); selectionChanged(_selectedItemIndex, prevSelection); } - } else - setHoverItem(i); + } else { + if (itemEnabled(i)) + setHoverItem(i); + } if ((event.button == MouseFlag.LButton || event.button == MouseFlag.RButton)) { if ((_clickOnButtonDown && event.action == MouseAction.ButtonDown) || (!_clickOnButtonDown && event.action == MouseAction.ButtonUp)) { - itemClicked(i); - if (_clickOnButtonDown) - event.doNotTrackButtonDown = true; + if (itemEnabled(i)) { + itemClicked(i); + if (_clickOnButtonDown) + event.doNotTrackButtonDown = true; + } } } return true; diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index bd1883f8..5de0d27b 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -479,7 +479,7 @@ class Style { /// find substyle based on widget state (e.g. focused, pressed, ...) const(Style) forState(uint state) const { - if (state == 0) + if (state == State.Normal) return this; //Log.d("forState ", state, " styleId=", _id, " substates=", _substates.length); if (parentStyle !is null && _substates.length == 0 && parentStyle._substates.length > 0) //id is null && @@ -676,6 +676,7 @@ Theme createDefaultTheme() { Style listItem = res.createSubstyle("LIST_ITEM"); listItem.createState(State.Selected, State.Selected).backgroundColor(0xC04040FF).textColor(0x000000); + listItem.createState(State.Enabled, 0).textColor(0x80000000); // half transparent text for disabled item return res; } diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index cf3dce6e..0a4c3755 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -97,7 +97,7 @@ class Widget { /// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used). protected @property const (Style) style(uint stateFlags) const { const (Style) normalStyle = style(); - if (!stateFlags) // state is normal + if (stateFlags == State.Normal) // state is normal return normalStyle; const (Style) stateStyle = normalStyle.forState(stateFlags); if (stateStyle !is normalStyle)