Bug #11071
COMBO-BOX (SIMPLE): Incorrect Mouse Wheel Scrolling Step
100%
History
#1 Updated by Vladimir Tsichevski 9 months ago
Run the following 4GL example:
DEFINE VARIABLE cb AS CHARACTER VIEW-AS COMBO-BOX SIMPLE LIST-ITEMS "item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9" SIZE 20 BY 3. DEFINE FRAME fr cb WITH CENTERED SIZE 80 BY 20 SIDE-LABELS. ENABLE ALL WITH FRAME fr. WAIT-FOR GO OF FRAME fr.
This creates and enables a SIMPLE combo-box.
- Open the drop-down list.
- Rotate the mouse wheel while the mouse pointer is over the drop-down list area.
- OE: The list scrolls by one line per wheel notch.
- FWD: The list scrolls by three lines per wheel notch.
Note: The scrolling step is independent of the combo-box vertical size.
Note: Tested only with Swing client.
#2 Updated by Razvan-Nicolae Chichirau 27 days ago
- Status changed from New to WIP
- Assignee set to Razvan-Nicolae Chichirau
#3 Updated by Razvan-Nicolae Chichirau 26 days ago
- % Done changed from 0 to 100
- Status changed from WIP to Review
A simple combo-box should scroll 1 item per notch. Tested on multiple OE + Windows versions.
Hynek: Please check 11071a/rev. 16726.
#4 Updated by Hynek Cihlar 26 days ago
Razvan-Nicolae Chichirau wrote:
A simple combo-box should scroll 1 item per notch. Tested on multiple OE + Windows versions.
Razvan, isn't this dependent on OS settings. In the system you can set how many lines/pages to (wheel) scroll.
#5 Updated by Razvan-Nicolae Chichirau 25 days ago
Only the drop-downs of a DROP-DOWN / DROP-DOWN-LIST combo-box are affected by this Windows setting. For a SIMPLE combo-box, it will always be 1 per each notch regardless of this setting.
#6 Updated by Hynek Cihlar 18 days ago
- Status changed from Review to WIP
- % Done changed from 100 to 90
Code review 11071a revisions 16725..16726
The points below look legit.
- [MAJOR] functional
ScrollPaneGuiImpl.scrollVerticallyTo: The newif (newPos vBar.getPosition()) return;guard permanently swallows wheel-up in the SIMPLE combo-box whenevervBar's cached position has drifted below the list's real top row, and there is a live user path that creates exactly that drift. Trigger, with the issue's own reproducer (9 items,SIZE 20 BY 3⇒visibleRows 2): typeitem2into the combo's entry field.EntryFieldGuiImpl.processKeyEvent(SE_VALUE_CHANGED) →ComboBoxGuiImpl.entryFieldChanged(:1481) →ScrollableSelectionListGuiImpl.setTopRow(1)(:548-552), which first callsensureRowVisible(1, true)→DefaultList.setCurrentRow(1, true): therecalculateScrollYPosition()returnsrowTop 0both before and after, sopos newPosandpostScrollEventis not called — no listener is notified andvBar.positionstays 0.setTopRowthen assignsrowTop = fixTopRow(1) = 1directly, again with no notification. Derived state:rowTop 1,vBar.getPosition() 0. Nothing resyncs the bar upward from the widget —adjustScrollLayoutImpl(:1016-1158) snapshotsvBar.getPosition()andadjustScrollBar(:1263-1266) only clamps down (if (bar.getPosition() > max);0 > 7is false), after which :1141-1149 nullsnewYPositionbecause the bar did not move, sogetScrollWidget().scroll(...)is never called even thoughdraw()runs it on every repaint;ScrollableSelectionListGuiImpl.draw(:266-270) only self-corrects whenrowTop + rowsVisible > modelSize(1 + 2 > 9is false). One wheel notch up then computesnewPos = -1 * 1 + 0 = -1→ clamped to 0 → equalsvBar.getPosition()→ return. NoScrollEventis posted,item1can never be reached by wheeling up, and every further wheel-up is swallowed identically. Before this change the unconditionalScrollEvent(w, null, 0)reachedScrollableSelectionListGuiImpl.scroll(null, 0), which setrowTop = 0and repainted — i.e. the old post also acted as the de-facto resync. Fix by comparing against the scrolled widget's actual offset (getTopRow()) rather thanvBar.getPosition(), or by makingScrollableSelectionListGuiImpl.setTopRownotify its scroll listeners after assigningrowTop. Note the mirror-image drift (bar above the widget, e.g. typingitem9) does self-heal via the clamp-down inadjustScrollBar, so only the bar-below-widget direction is affected; no drift source was found for the other users of the shared helper (frames, editors, drop-downs).
- [MINOR] functional
ComboBoxGuiImpl.mouseWheelMoved: On the web driver the new step is one item per wheel message, not per physical notch, so high-resolution input over-scrolls a SIMPLE combo relative to the pre-change behavior.e.getWheelRotation()is±1by construction on the web driver —GuiWebDriversplits the JSrotationintodirection = (wheelParameters[0] > 0) ? 1 : -1andscrollAmount = Math.abs(wheelParameters[0])(:1806-1807) and passesdirectionas the AWTwheelRotation(:1841-1843) — so the gesture magnitude survives only ingetScrollAmount(), whichscrollBySteps()discards. Trigger: web GUI client, focused SIMPLE combo-box, macOS trackpad / Windows precision touchpad / high-resolution wheel, where Chrome sendsdeltaMode 0with many smalldeltaYvalues per gesture; each event now scrolls a full list item, whereas the oldscrollStepsFromPixels(e.getScrollAmount())path (CoordinatesConversion:738-748,(int) Math.round(heightFromNative(pixels))) returned 0 steps for deltas below about half a text row (~7px at a typicalpixelsPerRowof 15-21) and so ignored them.MouseEvt.mergeWith()sums rotations as well as scroll amounts (:632-633), so queue coalescing provides no damping. Kept at MINOR rather than MAJOR because the effect is limited to viewport over-scroll —scrollBySteps()only posts aScrollEvent, the selection is untouched, and the excursion is clamped tovBar.getMax()and trivially reversible — and because it is not a new anomaly:SliderGuiImpl(:470),ScrollableListGuiImpl(:399),TreeBodyGuiImpl(:578),EditorGuiImpl(:1441),SelectionListBodyGuiImpl(:552) andComboBoxGuiImpl's own closed-drop-down branch (:656-687) all already move one unit per web wheel event, so this diff extends an existing codebase-wide web convention to the SIMPLE combo rather than regressing it in isolation. Optional, non-blocking follow-up: reuse the accepted mechanism fromBrowseGuiImpl.mouseWheelMoved(:1523-1537), which accumulatesMath.abs(e.getScrollAmount()) / 100.0againstThinClient.getMouseScrollThreshold()behind agd.isWeb()guard — ideally hoisted into shared code so all rotation-only web widgets benefit at once. A classic discrete notch (deltaY 100, or FirefoxdeltaMode 1) is unaffected and in fact improved by this diff (was ~6 rows / 3 rows, now 1 item), which is exactly what the issue asks for.
- [MINOR] functional
ScrollPaneGuiImpl.mouseWheelMoved: ThehBar null && hBar.isVisible()→hBar != null && hBar.isVisible()correction is right and removes a real crash —&&evaluates its right operand only when the left is true, i.e. exactly whenhBaris null, so the old guard was an unconditional NPE forBars.VERTICAL-only panes (DropDownGuiImpl:222,ComboBoxGuiImpl:2148,MessageAreaGuiImpl:157) and the body was genuinely dead. But it brings that previously-dead statement to life, and the statement mixes units.scrollStepisw.getVisibleDimension().height, which is native pixels for every pane that can actually have a visiblehBar—NativeScrollContainer(:110-120,dimensionToNative(pane.calcInitialViewportSize())), used by the frame scroll container andWindowWorkSpace— whereashBar.height()returns character rows as adouble(ScrollBarGuiImpl.initsetssetHeight(cc.heightFromNative(theme.getScrollBarGaugeWidth())), andScrollBarGuiImpl.height():269 only subtracts the 3D offset). The decisive corroboration isadjustScrollLayoutImpl(:1066-1070), which performs the analogous correction asmaxV += screen().coordinates().heightToNative(barSize(Orientation.HORIZONTAL))— it must convert to pixels;mouseWheelMoveddoes not. Per JLS 15.26.2 the compoundscrollStep -= hBar.height()is(int)(scrollStep - hBar.height()), so a ~0.8-0.9 row value truncates the correction to ~1px: a 200px viewport becomes 199 instead of the intended 184, and the page scroll skips roughly one text row hidden behind the horizontal bar. Trigger: Windows wheel setting "One screen at a time" (SwingWHEEL_BLOCK_SCROLL) or a browser sendingdeltaMode == 2(GuiWebDriver:1808-1811), wheeling with focus on an ordinary widget inside a SCROLLABLE frame whose content overflows both ways so both bars are visible —UiUtils.findMouseWheelSourcefalls through toFrameGuiImpl.getMouseWheelTarget()→frameScroll(created withBars.BOTH,FrameGuiImpl:2127). UsebarSizeNative(Orientation.HORIZONTAL)(:987-1011, returnshBar.physicalDimension().heightin pixels) orscreen().coordinates().heightToNative(hBar.height()).
#7 Updated by Razvan-Nicolae Chichirau 1 day ago
- % Done changed from 90 to 100
- Status changed from WIP to Review
Hynek: Please check 11071a/rev. 16727.
All items were valid. For the second one, I removed the cumulative mouse wheel scrolling from browse and moved it at the driver-level for WEB clients. Tested on a laptop trackpad and it seems OK.