Repository: slint-ui/slint (language: rust)

Issue:
Allow disabling combobox value changing with scrolling
<!-- Thanks for opening an issue! 🤗 -->
<!-- IMPORTANT: This issue tracker is for tracking bugs and features request. For questions or help on how to use Slint, please go to the "Discussions" tab -->
<!-- Please mention your platform and the programming language you are using Slint with -->
<!-- For bugs, please give steps on how to reproduce. What is the expected behavior and what do you see instead. -->
<!-- If possible, please include relevant code snippets, in code blocks (```  ```)-->

When a ComboBox is set inside ScrollView it is easy to make the mistake of editing the ComboBox by hovering over the ComboBox and scrolling on the ComboBox instead of an empty part of the window. When ComboBox.width: 100%; this situation is inevitable. 

I tried getting around this with a wrapper TouchArea catching scroll events, but looks like the TouchArea would need to be inside the ComboBox for this to work.

```slint
import { AboutSlint, Button, VerticalBox, ScrollView, ComboBox } from "std-widgets.slint";
export component Demo {
    height: 300px;
    ScrollView 
    {
        VerticalLayout {
            padding: 50px;
            for i in 5 : TouchArea {
                height: 100px;
                ComboBox  {
                    height: 100%;
                    width: 100%;
                    
                    model: ["setting-a", "setting-b", "setting-c"];
                }
                scroll-event(ev) => {
                    EventResult.accept // since this controls whether parent element gets the event, this does nothing for the child combobox
                }
            }
            Text {
                text: "----";
            }
            for i in 5 : ComboBox  {
                height: 100px;
                model: ["setting-a", "setting-b", "setting-c"];
            }
        }
    }
}
```


Discussion / hints:
Thanks for the bug report.
We should do the same as what the platform natively does.
I tried Qt and the wheel does change the combobox even within scroll area.
On windows, it seems that the wheel only change the content of the combobox if it has focus.

I have disabled it in Qt Creator - https://codereview.qt-project.org/c/qt-creator/qt-creator/+/429103
Qt allows users to disable it thorugh style QStyle::SH_ComboBox_AllowWheelScrolling. I'm wondering if I can disable it through a property or an environment variable.
This is causing a particular horrible issue with the Slint live-preview. You scroll the panel of visual properties and as the mouse moves over the combo boxes for unit types they get change from e.g. px to rem and lots of code is altered. Windows, Mac and I believe even linux don't have this by default regardless of if a combo box is in a scrollview or anywhere. 
One fix that could be done is to only allow this to work if the combobox has focus.


Fix patch:
diff --git a/internal/compiler/widgets/common/combobox-base.slint b/internal/compiler/widgets/common/combobox-base.slint
index 97088865a34..91f33558fbb 100644
--- a/internal/compiler/widgets/common/combobox-base.slint
+++ b/internal/compiler/widgets/common/combobox-base.slint
@@ -73,7 +73,8 @@ export component ComboBoxBase {
         root.update-current-value();
     }
 
-    private property <length> scroll-delta: 2px;
+    /// Minimum scroll delta so that the scroll wheel changes the value.
+    in property <length> scroll-delta: 2px;
 
     forward-focus: i-focus-scope;
 
@@ -109,6 +110,9 @@ export component ComboBoxBase {
             }
 
             scroll-event(event) => {
+                if (!root.has-focus) {
+                    return reject;
+                }
                 if (event.delta-y < -root.scroll-delta) {
                     root.move-selection-down();
                     return accept;
diff --git a/internal/compiler/widgets/cupertino/combobox.slint b/internal/compiler/widgets/cupertino/combobox.slint
index 79a75738b27..9f4ee549701 100644
--- a/internal/compiler/widgets/cupertino/combobox.slint
+++ b/internal/compiler/widgets/cupertino/combobox.slint
@@ -47,6 +47,9 @@ export component ComboBox {
         width: 100%;
         height: 100%;
 
+        // Mac doesn't react on mouse wheel on the ComboBox.
+        scroll-delta: 1000000px;
+
         show-popup => {
             popup.show();
         }


Test patch:
diff --git a/tests/cases/widgets/combobox.slint b/tests/cases/widgets/combobox.slint
index ca1450a4753..241c0e1e65c 100644
--- a/tests/cases/widgets/combobox.slint
+++ b/tests/cases/widgets/combobox.slint
@@ -21,6 +21,10 @@ export component TestCase inherits Window {
         }
     }
 
+    public function unfocus() {
+        box.clear-focus();
+    }
+
     in-out property current-index <=> box.current-index;
     in-out property current-value <=> box.current-value;
     in-out property model <=> box.model;
@@ -32,9 +36,7 @@ export component TestCase inherits Window {
 
 ```rust
 use std::rc::Rc;
-use slint::platform::Key;
-use slint::SharedString;
-use slint::VecModel;
+use slint::{platform::WindowEvent, platform::Key, VecModel, SharedString, LogicalPosition};
 use i_slint_backend_testing::mock_elapsed_time;
 
 let instance = TestCase::new().unwrap();
@@ -167,6 +169,40 @@ assert_eq!(instance.get_output(), "");
 assert_eq!(instance.get_has_focus(), true);
 assert_eq!(combobox.accessible_expanded(), Some(false));
 
+// Scroll with the wheel
+let wheel_change_value = !module_path!().contains("cupertino");
+instance.set_output(Default::default());
+instance.window().dispatch_event(WindowEvent::PointerScrolled { position: LogicalPosition::new(100.0, 100.0), delta_x: 0.0, delta_y: 20.0 });
+if wheel_change_value {
+    assert_eq!(instance.get_current_value(), "Aaa");
+    assert_eq!(instance.get_current_index(), 0);
+    assert_eq!(instance.get_output(), "selected(Aaa,0)\n");
+} else {
+    assert_eq!(instance.get_current_value(), "Bbb");
+    assert_eq!(instance.get_current_index(), 1);
+    assert_eq!(instance.get_output(), "");
+}
+instance.window().dispatch_event(WindowEvent::PointerScrolled { position: LogicalPosition::new(100.0, 100.0), delta_x: 0.0, delta_y: -5.0 });
+assert_eq!(instance.get_has_focus(), true);
+assert_eq!(instance.get_current_value(), "Bbb");
+assert_eq!(instance.get_current_index(), 1);
+if wheel_change_value {
+    assert_eq!(instance.get_output(), "selected(Aaa,0)\nselected(Bbb,1)\n");
+}
+// When not having focus, do nothing
+instance.invoke_unfocus();
+instance.set_output(Default::default());
+assert_eq!(instance.get_has_focus(), false);
+instance.window().dispatch_event(WindowEvent::PointerScrolled { position: LogicalPosition::new(100.0, 100.0), delta_x: 0.0, delta_y: 20.0 });
+assert_eq!(instance.get_current_value(), "Bbb");
+assert_eq!(instance.get_current_index(), 1);
+instance.window().dispatch_event(WindowEvent::PointerScrolled { position: LogicalPosition::new(100.0, 100.0), delta_x: 0.0, delta_y: -5.0 });
+assert_eq!(instance.get_has_focus(), false);
+assert_eq!(instance.get_current_value(), "Bbb");
+assert_eq!(instance.get_current_index(), 1);
+assert_eq!(instance.get_output(), "");
+slint_testing::send_keyboard_string_sequence(&instance, "\t");
+assert_eq!(instance.get_has_focus(), true);
 
 // Set current-index to -1
 instance.set_current_index(-1);