mirror of https://gitlab.com/nakst/essence
scroll stability wrapped list view
This commit is contained in:
parent
81f3ee6109
commit
73f9545be1
|
@ -93,6 +93,21 @@ struct EsListView : EsElement {
|
||||||
EsListViewIndex anchorItemGroup;
|
EsListViewIndex anchorItemGroup;
|
||||||
EsListViewIndex anchorItemIndex;
|
EsListViewIndex anchorItemIndex;
|
||||||
|
|
||||||
|
bool hasScrollItem; // Used to preserve the scroll position when resizing a wrapped list view.
|
||||||
|
bool useScrollItem;
|
||||||
|
int64_t scrollItemOffset;
|
||||||
|
EsListViewIndex scrollItemGroup;
|
||||||
|
EsListViewIndex scrollItemIndex;
|
||||||
|
|
||||||
|
#define ENSURE_VISIBLE_ALIGN_TOP (1 << 0)
|
||||||
|
#define ENSURE_VISIBLE_ALIGN_CENTER (1 << 1)
|
||||||
|
#define ENSURE_VISIBLE_ALIGN_FOR_SCROLL_ITEM (1 << 2)
|
||||||
|
EsListViewIndex ensureVisibleGroupIndex;
|
||||||
|
EsListViewIndex ensureVisibleIndex;
|
||||||
|
uint8_t ensureVisibleFlags;
|
||||||
|
bool ensureVisibleQueued;
|
||||||
|
bool populateQueued;
|
||||||
|
|
||||||
// Valid only during Z-order messages.
|
// Valid only during Z-order messages.
|
||||||
Array<EsElement *> zOrderItems;
|
Array<EsElement *> zOrderItems;
|
||||||
|
|
||||||
|
@ -122,12 +137,6 @@ struct EsListView : EsElement {
|
||||||
|
|
||||||
int maximumItemsPerBand;
|
int maximumItemsPerBand;
|
||||||
|
|
||||||
EsListViewIndex ensureVisibleGroupIndex;
|
|
||||||
EsListViewIndex ensureVisibleIndex;
|
|
||||||
uint8_t ensureVisibleAlign;
|
|
||||||
bool ensureVisibleQueued;
|
|
||||||
bool populateQueued;
|
|
||||||
|
|
||||||
// Fixed item storage:
|
// Fixed item storage:
|
||||||
Array<ListViewFixedItem> fixedItems;
|
Array<ListViewFixedItem> fixedItems;
|
||||||
Array<EsListViewIndex> fixedItemIndices; // For sorting. Converts the actual list index into an index for fixedItems.
|
Array<EsListViewIndex> fixedItemIndices; // For sorting. Converts the actual list index into an index for fixedItems.
|
||||||
|
@ -338,10 +347,10 @@ struct EsListView : EsElement {
|
||||||
*_itemSize = itemSize;
|
*_itemSize = itemSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureItemVisible(EsListViewIndex groupIndex, EsListViewIndex index, uint8_t align) {
|
void EnsureItemVisible(EsListViewIndex groupIndex, EsListViewIndex index, uint8_t visibleFlags) {
|
||||||
ensureVisibleGroupIndex = groupIndex;
|
ensureVisibleGroupIndex = groupIndex;
|
||||||
ensureVisibleIndex = index;
|
ensureVisibleIndex = index;
|
||||||
ensureVisibleAlign = align;
|
ensureVisibleFlags = visibleFlags;
|
||||||
|
|
||||||
if (!ensureVisibleQueued) {
|
if (!ensureVisibleQueued) {
|
||||||
UpdateAction action = {};
|
UpdateAction action = {};
|
||||||
|
@ -352,7 +361,7 @@ struct EsListView : EsElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _EnsureItemVisible(EsListViewIndex groupIndex, EsListViewIndex index, uint8_t align) {
|
void _EnsureItemVisible(EsListViewIndex groupIndex, EsListViewIndex index, uint8_t visibleFlags) {
|
||||||
EsRectangle contentBounds = GetListBounds();
|
EsRectangle contentBounds = GetListBounds();
|
||||||
|
|
||||||
int64_t startInset = flags & ES_LIST_VIEW_HORIZONTAL ? style->insets.l : style->insets.t,
|
int64_t startInset = flags & ES_LIST_VIEW_HORIZONTAL ? style->insets.l : style->insets.t,
|
||||||
|
@ -362,17 +371,28 @@ struct EsListView : EsElement {
|
||||||
int64_t position, itemSize;
|
int64_t position, itemSize;
|
||||||
GetItemPosition(groupIndex, index, &position, &itemSize);
|
GetItemPosition(groupIndex, index, &position, &itemSize);
|
||||||
|
|
||||||
|
if (visibleFlags & ENSURE_VISIBLE_ALIGN_FOR_SCROLL_ITEM) {
|
||||||
|
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
||||||
|
scroll.SetX(scroll.position[0] + position - scrollItemOffset);
|
||||||
|
} else {
|
||||||
|
scroll.SetY(scroll.position[1] + position - scrollItemOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
useScrollItem = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (position >= 0 && position + itemSize <= contentSize - endInset) {
|
if (position >= 0 && position + itemSize <= contentSize - endInset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (align == 1) {
|
if (visibleFlags & ENSURE_VISIBLE_ALIGN_TOP) {
|
||||||
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
||||||
scroll.SetX(scroll.position[0] + position - startInset);
|
scroll.SetX(scroll.position[0] + position - startInset);
|
||||||
} else {
|
} else {
|
||||||
scroll.SetY(scroll.position[1] + position - startInset);
|
scroll.SetY(scroll.position[1] + position - startInset);
|
||||||
}
|
}
|
||||||
} else if (align == 2) {
|
} else if (visibleFlags & ENSURE_VISIBLE_ALIGN_CENTER) {
|
||||||
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
||||||
scroll.SetX(scroll.position[0] + position + itemSize / 2 - contentSize / 2);
|
scroll.SetX(scroll.position[0] + position + itemSize / 2 - contentSize / 2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -759,6 +779,13 @@ struct EsListView : EsElement {
|
||||||
ListViewItem *item = FindVisibleItem(inlineTextboxGroup, inlineTextboxIndex);
|
ListViewItem *item = FindVisibleItem(inlineTextboxGroup, inlineTextboxIndex);
|
||||||
if (item) MoveInlineTextbox(item);
|
if (item) MoveInlineTextbox(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visibleItems.Length() && (!useScrollItem || !hasScrollItem)) {
|
||||||
|
scrollItemGroup = visibleItems.First().group;
|
||||||
|
scrollItemIndex = visibleItems.First().index;
|
||||||
|
scrollItemOffset = visibleItems.First().element->offsetY;
|
||||||
|
hasScrollItem = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Populate() {
|
void Populate() {
|
||||||
|
@ -771,7 +798,7 @@ struct EsListView : EsElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wrap(bool autoScroll) {
|
void Wrap(bool sizeChanged) {
|
||||||
if (~flags & ES_LIST_VIEW_TILED) return;
|
if (~flags & ES_LIST_VIEW_TILED) return;
|
||||||
|
|
||||||
totalSize = 0;
|
totalSize = 0;
|
||||||
|
@ -804,8 +831,12 @@ struct EsListView : EsElement {
|
||||||
|
|
||||||
scroll.Refresh();
|
scroll.Refresh();
|
||||||
|
|
||||||
if (visibleItems.Length() && autoScroll) {
|
if (sizeChanged) {
|
||||||
EnsureItemVisible(visibleItems[0].group, visibleItems[0].index, true);
|
useScrollItem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useScrollItem && hasScrollItem) {
|
||||||
|
EnsureItemVisible(scrollItemGroup, scrollItemIndex, ENSURE_VISIBLE_ALIGN_FOR_SCROLL_ITEM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,6 +860,7 @@ struct EsListView : EsElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useScrollItem = false;
|
||||||
scroll.Refresh();
|
scroll.Refresh();
|
||||||
EsElementUpdateContentSize(this);
|
EsElementUpdateContentSize(this);
|
||||||
}
|
}
|
||||||
|
@ -1422,7 +1454,7 @@ struct EsListView : EsElement {
|
||||||
|
|
||||||
focusedItemIndex = m.getContent.index;
|
focusedItemIndex = m.getContent.index;
|
||||||
focusedItemGroup = m.getContent.group;
|
focusedItemGroup = m.getContent.group;
|
||||||
EnsureItemVisible(focusedItemGroup, focusedItemIndex, false);
|
EnsureItemVisible(focusedItemGroup, focusedItemIndex, ES_FLAGS_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
ListViewItem *newFocus = FindVisibleItem(focusedItemGroup, focusedItemIndex);
|
ListViewItem *newFocus = FindVisibleItem(focusedItemGroup, focusedItemIndex);
|
||||||
|
@ -1548,7 +1580,7 @@ struct EsListView : EsElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keepSearchBuffer) ClearSearchBuffer();
|
if (!keepSearchBuffer) ClearSearchBuffer();
|
||||||
EnsureItemVisible(focusedItemGroup, focusedItemIndex, isPrevious || isHome || isPageUp || isPreviousBand);
|
EnsureItemVisible(focusedItemGroup, focusedItemIndex, (isPrevious || isHome || isPageUp || isPreviousBand) ? ENSURE_VISIBLE_ALIGN_TOP : ES_FLAGS_DEFAULT);
|
||||||
Select(focusedItemGroup, focusedItemIndex, shift, ctrl, ctrl && !shift);
|
Select(focusedItemGroup, focusedItemIndex, shift, ctrl, ctrl && !shift);
|
||||||
return true;
|
return true;
|
||||||
} else if (isSpace && ctrl && !shift && hasFocusedItem) {
|
} else if (isSpace && ctrl && !shift && hasFocusedItem) {
|
||||||
|
@ -1690,10 +1722,11 @@ struct EsListView : EsElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useScrollItem = false;
|
||||||
Populate();
|
Populate();
|
||||||
Repaint(true);
|
Repaint(true);
|
||||||
|
|
||||||
if (columnHeader) {
|
if (columnHeader && message->type == ES_MSG_SCROLL_X) {
|
||||||
EsElementRelayout(columnHeader);
|
EsElementRelayout(columnHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2043,7 +2076,7 @@ void ListViewEnsureVisibleActionCallback(EsElement *element, EsGeneric) {
|
||||||
EsListView *view = (EsListView *) element;
|
EsListView *view = (EsListView *) element;
|
||||||
EsAssert(view->ensureVisibleQueued);
|
EsAssert(view->ensureVisibleQueued);
|
||||||
view->ensureVisibleQueued = false;
|
view->ensureVisibleQueued = false;
|
||||||
view->_EnsureItemVisible(view->ensureVisibleGroupIndex, view->ensureVisibleIndex, view->ensureVisibleAlign);
|
view->_EnsureItemVisible(view->ensureVisibleGroupIndex, view->ensureVisibleIndex, view->ensureVisibleFlags);
|
||||||
EsAssert(!view->ensureVisibleQueued);
|
EsAssert(!view->ensureVisibleQueued);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2501,6 +2534,9 @@ void EsListViewContentChanged(EsListView *view) {
|
||||||
view->scroll.SetX(0);
|
view->scroll.SetX(0);
|
||||||
view->scroll.SetY(0);
|
view->scroll.SetY(0);
|
||||||
|
|
||||||
|
view->hasScrollItem = false;
|
||||||
|
view->useScrollItem = false;
|
||||||
|
|
||||||
EsListViewInvalidateAll(view);
|
EsListViewInvalidateAll(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2523,7 +2559,7 @@ void EsListViewFocusItem(EsListView *view, EsListViewIndex group, EsListViewInde
|
||||||
newFocus->element->MaybeRefreshStyle();
|
newFocus->element->MaybeRefreshStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
view->EnsureItemVisible(group, index, false);
|
view->EnsureItemVisible(group, index, ES_FLAGS_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EsListViewGetFocusedItem(EsListView *view, EsListViewIndex *group, EsListViewIndex *index) {
|
bool EsListViewGetFocusedItem(EsListView *view, EsListViewIndex *group, EsListViewIndex *index) {
|
||||||
|
@ -2753,7 +2789,7 @@ bool EsListViewFixedItemSelect(EsListView *view, EsGeneric data) {
|
||||||
|
|
||||||
// TODO Maybe you should have to separately call EsListViewFocusItem to get this behaviour?
|
// TODO Maybe you should have to separately call EsListViewFocusItem to get this behaviour?
|
||||||
EsListViewFocusItem(view, 0, index);
|
EsListViewFocusItem(view, 0, index);
|
||||||
view->EnsureItemVisible(0, index, 2 /* center */);
|
view->EnsureItemVisible(0, index, ENSURE_VISIBLE_ALIGN_CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
|
@ -2849,7 +2885,7 @@ void EsListViewFixedItemSortAll(EsListView *view) {
|
||||||
if (view->fixedItemIndices[i] == previousSelectionIndex) {
|
if (view->fixedItemIndices[i] == previousSelectionIndex) {
|
||||||
EsListViewSelect(view, 0, i);
|
EsListViewSelect(view, 0, i);
|
||||||
EsListViewFocusItem(view, 0, i);
|
EsListViewFocusItem(view, 0, i);
|
||||||
view->EnsureItemVisible(0, i, 2 /* center */);
|
view->EnsureItemVisible(0, i, ENSURE_VISIBLE_ALIGN_CENTER);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2893,7 +2929,7 @@ EsTextbox *EsListViewCreateInlineTextbox(EsListView *view, EsListViewIndex group
|
||||||
|
|
||||||
view->inlineTextboxGroup = group;
|
view->inlineTextboxGroup = group;
|
||||||
view->inlineTextboxIndex = index;
|
view->inlineTextboxIndex = index;
|
||||||
view->EnsureItemVisible(group, index, true);
|
view->EnsureItemVisible(group, index, ENSURE_VISIBLE_ALIGN_TOP);
|
||||||
|
|
||||||
uint64_t textboxFlags = ES_CELL_FILL | ES_TEXTBOX_EDIT_BASED | ES_TEXTBOX_ALLOW_TABS;
|
uint64_t textboxFlags = ES_CELL_FILL | ES_TEXTBOX_EDIT_BASED | ES_TEXTBOX_ALLOW_TABS;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue