From 3c9121b4afec2719f0a33630b4842ecc5243f966 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 14 May 2022 10:03:57 -0600 Subject: [PATCH] Improve scroll visualization --- .../ProcessQueue/VirtualFlowControl.cs | 75 +++++++++++-------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs index e9054919..2e0b34b6 100644 --- a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs +++ b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs @@ -38,28 +38,31 @@ namespace LibationWinForms.ProcessQueue private int _virtualControlCount; - //https://stackoverflow.com/a/2882878/3335599 - int ScrollValue => Math.Max(Math.Min(VirtualHeight, vScrollBar1.Value), 0); + int ScrollValue => Math.Max(vScrollBar1.Value, 0); /// - /// The virtual height of all virtual controls within this + /// Virtual height of all virtual controls within this /// - private int VirtualHeight => _virtualControlCount * VirtualControlHeight - vScrollBar1.Height + 2 * TopMargin; - + private int VirtualHeight => (VirtualControlCount + 1 /*Allow extra blank space after last control*/) * VirtualControlHeight - DisplayHeight + 2 * TopMargin; /// - /// Item index of the first virtual + /// Index of the first virtual /// private int FirstVisibleVirtualIndex => ScrollValue / VirtualControlHeight; - /// /// The display height of this /// private int DisplayHeight => DisplayRectangle.Height; - /// /// The total height, inclusing margins, of the repeated /// - private readonly int VirtualControlHeight; //90 + private readonly int VirtualControlHeight; + /// + /// Amount the control moves with a small scroll change + /// + private int SmallChangeValue => VirtualControlHeight * SMALL_SCROLL_CHANGE_MULTIPLE; + /// + /// Margin between the top and the top of the Panle and the bottom and the bottom of the panel + /// private readonly int TopMargin; private const int WM_MOUSEWHEEL = 522; @@ -67,23 +70,22 @@ namespace LibationWinForms.ProcessQueue /// Total number of actual controls added to the panel. 23 is sufficient up to a 4k monitor height. /// private const int NUM_ACTUAL_CONTROLS = 23; - private const int SCROLL_SMALL_CHANGE = 120; - private const int SCROLL_LARGE_CHANGE = 3 * SCROLL_SMALL_CHANGE; + /// + /// Multiple of that is moved for each small scroll change + /// + private const int SMALL_SCROLL_CHANGE_MULTIPLE = 1; private readonly VScrollBar vScrollBar1; private readonly List BookControls = new(); public VirtualFlowControl() { - InitializeComponent(); vScrollBar1 = new VScrollBar { Minimum = 0, Value = 0, - SmallChange = SCROLL_SMALL_CHANGE, - LargeChange = SCROLL_LARGE_CHANGE, Dock = DockStyle.Right }; panel1.Width -= vScrollBar1.Width + panel1.Margin.Right; @@ -99,11 +101,12 @@ namespace LibationWinForms.ProcessQueue vScrollBar1.Scroll += (_, s) => SetScrollPosition(s.NewValue); var control = InitControl(0); - BookControls.Add(control); - panel1.Controls.Add(control); VirtualControlHeight = control.Height + control.Margin.Top + control.Margin.Bottom; TopMargin = control.Margin.Top; + BookControls.Add(control); + panel1.Controls.Add(control); + if (DesignMode) return; @@ -114,8 +117,7 @@ namespace LibationWinForms.ProcessQueue panel1.Controls.Add(control); } - vScrollBar1.SmallChange = VirtualControlHeight; - vScrollBar1.LargeChange = 3 * VirtualControlHeight; + vScrollBar1.SmallChange = SMALL_SCROLL_CHANGE_MULTIPLE * VirtualControlHeight; panel1.Height += 2 * VirtualControlHeight; } @@ -167,13 +169,18 @@ namespace LibationWinForms.ProcessQueue else { vScrollBar1.Enabled = true; + + //Large scroll change is almost one full window height + vScrollBar1.LargeChange = Math.Max(DisplayHeight / VirtualControlHeight - 2, SMALL_SCROLL_CHANGE_MULTIPLE) * VirtualControlHeight; + //https://stackoverflow.com/a/2882878/3335599 vScrollBar1.Maximum = VirtualHeight + vScrollBar1.LargeChange - 1; } } /// - /// Calculated the virtual controls that are in view at the currrent scroll position and windows size, then fires for the visible controls + /// Calculated the virtual controls that are in view at the currrent scroll position and windows size, + /// positions to simulate scroll activity, then fires for the visible controls /// private void DoVirtualScroll() { @@ -182,19 +189,18 @@ namespace LibationWinForms.ProcessQueue int position = ScrollValue % VirtualControlHeight; panel1.Location = new Point(0, -position); - int visibleHeight = ScrollValue - firstVisible * VirtualControlHeight + DisplayHeight; + int numVisible = DisplayHeight / VirtualControlHeight; - int count = visibleHeight / VirtualControlHeight; + if (DisplayHeight % VirtualControlHeight != 0) + numVisible++; - if (visibleHeight % VirtualControlHeight != 0) - count++; + numVisible = Math.Min(numVisible, VirtualControlCount); + numVisible = Math.Min(numVisible, VirtualControlCount - firstVisible); - count = Math.Min(count, VirtualControlCount); - - RequestData?.Invoke(firstVisible, count, BookControls); + RequestData?.Invoke(firstVisible, numVisible, BookControls); for (int i = 0; i < BookControls.Count; i++) - BookControls[i].Visible = i < count; + BookControls[i].Visible = i < numVisible; } /// @@ -202,10 +208,11 @@ namespace LibationWinForms.ProcessQueue /// private void SetScrollPosition(int value) { - int newPos = (int)Math.Round((double)value / VirtualControlHeight) * VirtualControlHeight; + int newPos = (int)Math.Round((double)value / SmallChangeValue) * SmallChangeValue; if (vScrollBar1.Value != newPos) { - vScrollBar1.Value = Math.Min(newPos, vScrollBar1.Maximum); + //https://stackoverflow.com/a/2882878/3335599 + vScrollBar1.Value = Math.Min(newPos, vScrollBar1.Maximum - vScrollBar1.LargeChange +1); DoVirtualScroll(); } } @@ -218,12 +225,16 @@ namespace LibationWinForms.ProcessQueue //https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel int wheelDelta = -(short)(((ulong)m.WParam) >> 16 & ushort.MaxValue); + int numSmallPositionMoves = Math.Abs(wheelDelta) / 120; + + int scrollDelta = Math.Sign(wheelDelta) * numSmallPositionMoves * SmallChangeValue; + int newScrollPosition; - if (wheelDelta > 0) - newScrollPosition = Math.Min(vScrollBar1.Value + wheelDelta, vScrollBar1.Maximum); + if (scrollDelta > 0) + newScrollPosition = Math.Min(vScrollBar1.Value + scrollDelta, vScrollBar1.Maximum); else - newScrollPosition = Math.Max(vScrollBar1.Value + wheelDelta, vScrollBar1.Minimum); + newScrollPosition = Math.Max(vScrollBar1.Value + scrollDelta, vScrollBar1.Minimum); SetScrollPosition(newScrollPosition); }