From 37ba8a6a4d2f7b2093b7f6cc65e32cd36e36adac Mon Sep 17 00:00:00 2001 From: Marcus Minhorst Date: Sat, 12 Jul 2025 13:40:51 -0400 Subject: [PATCH] Refactored. Scrolling no longer always moves cursor --- .../zscript/engine/ui/menu/optionmenu.zs | 198 +++++++++++------- 1 file changed, 118 insertions(+), 80 deletions(-) diff --git a/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs b/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs index 07ce9b54c..f5856b1a8 100644 --- a/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs @@ -97,6 +97,8 @@ class OptionMenu : Menu bool CanScrollUp; bool CanScrollDown; int VisBottom; + int LastRow; + int MaxItems; OptionMenuItem mFocusControl; //============================================================================= @@ -223,18 +225,12 @@ class OptionMenu : Menu { if (ev.type == UIEvent.Type_WheelUp) { - if (MenuMoveCursor(-2) != 0) - { - MenuSound ("menu/cursor"); - } + MenuScrollViewport(-2, true); return true; } else if (ev.type == UIEvent.Type_WheelDown) { - if (MenuMoveCursor(2) != 0) - { - MenuSound ("menu/cursor"); - } + MenuScrollViewport(2, true); return true; } else if (ev.type == UIEvent.Type_Char) @@ -274,16 +270,14 @@ class OptionMenu : Menu int MenuMoveCursor(int items) { - // trivial case - if (items == 0) + if (items == 0) // trivial case { return 0; } int startedAt = mDesc.mSelectedItem; - // trivial case - if (startedAt == -1) + if (startedAt == -1) // trivial case { if (items < 0) { @@ -297,27 +291,24 @@ class OptionMenu : Menu return mDesc.mSelectedItem - startedAt; } - if (items < -1) + if (items < -1) // extended case up { - // extended case up do { MenuMoveCursor(-1); items++; } while (startedAt > mDesc.mSelectedItem && items < 0) } - else if (items > 1) + else if (items > 1) // extended case down { - // extended case down do { MenuMoveCursor(1); items--; } while (startedAt <= mDesc.mSelectedItem && items > 0) } - else if (items == -1) + else if (items == -1) // base case up { - // base case up do { mDesc.mSelectedItem--; @@ -341,15 +332,12 @@ class OptionMenu : Menu { int y = mDesc.mPosition; if (y <= 0) y = DrawCaption(mDesc.mTitle, -y, false); - int lastrow = screen.GetHeight() - OptionHeight() * CleanYfac_1; - int rowheight = OptionMenuSettings.mLinespacing * CleanYfac_1 + 1; - - int maxitems = (lastrow - y) / rowheight + 1; - if (maxitems < RemainingVisibleItems(0)) + int maxItemsInternal = MaxItems; + if (maxItemsInternal < RemainingVisibleItems(0)) { - maxItems -= 2; + maxItemsInternal -= 2; } - if (maxitems <= 0) maxitems = 1; + if (maxItemsInternal <= 0) maxItemsInternal = 1; int newTopIndex = 0; int visibleItemsOnPage = 0; @@ -358,7 +346,7 @@ class OptionMenu : Menu if (mDesc.mItems[i].Visible()) { visibleItemsOnPage++; - if (visibleItemsOnPage >= maxitems) + if (visibleItemsOnPage >= maxItemsInternal) { newTopIndex = i; break; @@ -397,9 +385,8 @@ class OptionMenu : Menu } } } - else if (items == 1) + else if (items == 1) // base case down { - // base case down do { mDesc.mSelectedItem++; @@ -446,6 +433,99 @@ class OptionMenu : Menu return mDesc.mSelectedItem - startedAt; } + //============================================================================= + // + // Moves the viewport by the specified number of lines + // Keeps cursor in view if cursor is true + // Does not wrap + // Returns number of lines moved + // + //============================================================================= + + int MenuScrollViewport(int lines, bool cursor) + { + if (lines == 0) // trivial case + { + return 0; + } + + int startedAtScroll = mDesc.mScrollPos; + int startedAt = mDesc.mSelectedItem; + + if (lines > 0 && !CanScrollDown) // trivial case + { + return 0; + } + + if (lines < 0 && startedAtScroll <= 0) // trivial case + { + return 0; + } + + if (lines < 0) // base case up + { + mDesc.mScrollPos += lines; + + // backtrack if we overshot + if (mDesc.mScrollPos < 0) + { + mDesc.mScrollPos = 0; + } + + // ensure cursor is visible (if possible) + int lastItem = LastVisibleItem(); + int lastSelectable = -1; + int visible = 0; + for (int i = mDesc.mScrollPos; visible <= MaxItems && i < lastItem; i++) + { + if (!mDesc.mItems[i].Visible()) continue; + visible++; + if (!mDesc.mItems[i].Selectable()) continue; + lastSelectable = i; + } + if (lastSelectable != -1 && mDesc.mSelectedItem > lastSelectable) + { + mDesc.mSelectedItem = lastSelectable; + } + } + else if (lines > 0) // base case down + { + mDesc.mScrollPos += lines; + + // backtrack if we overshot + int visible = RemainingVisibleItems(mDesc.mScrollPos); + if (visible < MaxItems) + { + mDesc.mScrollPos = MAX(0, LastVisibleItem() - MaxItems); + visible = RemainingVisibleItems(mDesc.mScrollPos); + while (visible < MaxItems && mDesc.mScrollPos > 0) + { + if (mDesc.mItems[mDesc.mScrollPos].Visible()) + { + visible++; + } + mDesc.mScrollPos--; + } + } + + // ensure cursor is visible (if possible) + if (cursor && mDesc.mSelectedItem < mDesc.mScrollPos + mDesc.mScrollTop) + { + int temp = mDesc.mScrollPos + mDesc.mScrollTop; + for (int i = 0, v = 0; v < visible; i++) + { + if (!mDesc.mItems[temp + i].Visible()) continue; + v++; + if (!mDesc.mItems[temp + i].Selectable()) continue; + mDesc.mSelectedItem = temp + i; + break; + } + } + } + + return mDesc.mSelectedItem - startedAt; + } + //============================================================================= // // @@ -467,58 +547,11 @@ class OptionMenu : Menu break; case MKEY_PageUp: - if (mDesc.mScrollPos > 0) - { - mDesc.mScrollPos -= VisBottom - mDesc.mScrollPos - mDesc.mScrollTop; - if (mDesc.mScrollPos < 0) - { - mDesc.mScrollPos = 0; - } - if (mDesc.mSelectedItem != -1) - { - mDesc.mSelectedItem = mDesc.mScrollTop + mDesc.mScrollPos + 1; - while (!(mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mItems[mDesc.mSelectedItem].Visible())) - { - if (++mDesc.mSelectedItem >= RemainingVisibleItems(0)) - { - mDesc.mSelectedItem = 0; - } - } - if (mDesc.mScrollPos > mDesc.mSelectedItem) - { - mDesc.mScrollPos = mDesc.mSelectedItem; - } - } - } + MenuScrollViewport(-MaxItems, true); break; case MKEY_PageDown: - if (CanScrollDown) - { - int pagesize = VisBottom - mDesc.mScrollPos - mDesc.mScrollTop; - if (pagesize > 0) mDesc.mScrollPos += pagesize; - else mDesc.mScrollPos++; - - if (mDesc.mScrollPos + mDesc.mScrollTop + pagesize > mDesc.mItems.Size()) - { - mDesc.mScrollPos = mDesc.mItems.Size() - mDesc.mScrollTop - pagesize; - } - if (mDesc.mSelectedItem != -1) - { - mDesc.mSelectedItem = mDesc.mScrollTop + mDesc.mScrollPos; - while (!(mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mItems[mDesc.mSelectedItem].Visible())) - { - if (++mDesc.mSelectedItem >= mDesc.mItems.Size()) - { - mDesc.mSelectedItem = 0; - } - } - if (mDesc.mScrollPos > mDesc.mSelectedItem) - { - mDesc.mScrollPos = mDesc.mSelectedItem; - } - } - } + MenuScrollViewport(MaxItems, true); break; case MKEY_Enter: @@ -526,7 +559,10 @@ class OptionMenu : Menu { return true; } - // fall through to default + else + { + // fall through to default + } default: if (mDesc.mSelectedItem >= 0 && mDesc.mItems[mDesc.mSelectedItem].MenuEvent(mkey, fromcontroller)) return true; @@ -667,11 +703,13 @@ class OptionMenu : Menu int indent = GetIndent(); int ytop = y + mDesc.mScrollTop * 8 * CleanYfac_1; - int lastrow = screen.GetHeight() - OptionHeight() * CleanYfac_1; + LastRow = screen.GetHeight() - OptionHeight() * CleanYfac_1; + int rowheight = OptionMenuSettings.mLinespacing * CleanYfac_1 + 1; + MaxItems = (LastRow - y) / rowheight + 1; int i; int lastDrawnItemIndex = -1; - for (i = 0; i < mDesc.mItems.Size() && y <= lastrow; i++) + for (i = 0; i < mDesc.mItems.Size() && y <= LastRow; i++) { // Don't scroll the uppermost items if (i == mDesc.mScrollTop)