community icon indicating copy to clipboard operation
community copied to clipboard

Horizontal Scroll with Modifier Key for Mouse

Open AliSot2000 opened this issue 3 years ago โ€ข 8 comments

Hi

I ran into the issue that I needed to scroll horizontally and I didn't feel like using the multi-touch emulation. Many applications have a modifier key designated to switch from a vertical scrolling direction to horizontal scrolling direction.

I added said capability into kivy. (I've two implementations, one is commented out. One is faster and relies on default behavior, the other is slower but more verbose.) Since this is my first time working with the project I'm not very familiar with both the way you do the docs and how you like to have the config (more verbose vs faster).

I'd appreciate it if a maintainer could glance at the code I wrote and help me figure out how it should be structured to integrate as good as possible with the existing project.

AliSot2000

PS: I've written an issue already concerning this but I've had no response since so I decided to open this pull request.

Maintainer merge checklist

  • [ ] Title is descriptive/clear for inclusion in release notes.
  • [ ] Applied a Component: xxx label.
  • [ ] Applied the api-deprecation or api-break label.
  • [ ] Applied the release-highlight label to be highlighted in release notes.
  • [ ] Added to the milestone version it was merged into.
  • [ ] Unittests are included in PR.
  • [ ] Properly documented, including versionadded, versionchanged as needed.

AliSot2000 avatar Dec 01 '22 01:12 AliSot2000

Thanks for opening your first pull request here! ๐Ÿ’– Please check out our contributing guidelines.

welcome[bot] avatar Dec 01 '22 01:12 welcome[bot]

FYI https://github.com/Android-for-Python/gestures4kivy#pan

RobertFlatt avatar Dec 11 '22 05:12 RobertFlatt

FYI https://github.com/Android-for-Python/gestures4kivy#pan

I tried that first.

I ran into issues with nested boxes which scroll. (I do not remember what they were since itโ€™s been some time that I worked on the project)

AliSot2000 avatar Dec 11 '22 12:12 AliSot2000

@RobertFlatt

Hi, I've gotten around to testing again. I've tried a lot of things but I was unable to use gestures4kivy to implement nested ScrollViews. The outer ScrollView would always capture the event and not propagate it to the nested one, thereby making it useless. From my googling and understanding of events in kivy, this seems to be the default 'way' to handle it.

I don't see me rewriting the gestures4kivy too if I've already implemented a solution with this pull request.

For your convenience, I've an example of nested ScrollViews which you can use to verify the behaviour yourself. Example

AliSot2000 avatar Mar 02 '23 23:03 AliSot2000

From the gestures4kivy docs:

CommonGestures callback methods detect gestures; they do not implement behaviors.

I'm not really the right person to ask, I know only a little about the ScrollView widget, which is enough to know I don't understand. Since I'm not a kv jockey either, your example is a mystery to me. So I tried something more basic.

I tried a simpler Python example, based on https://kivy.org/doc/stable/api-kivy.uix.scrollview.html and a model of a slot machine, imagine each number is a different fruit๐ŸŽ๐ŸŒ๐Ÿ

from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from kivy.app import runTouchApp

def buttons():
    layout = GridLayout(cols=1, spacing=10, size_hint_y=None)
    # Make sure the height is such that there is something to scroll.
    layout.bind(minimum_height=layout.setter('height'))
    for i in range(20):
        btn = Button(text=str(i), size_hint_y=None, height=40)
        layout.add_widget(btn)
    return layout

def child_scroll():
    child = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))
    child.add_widget(buttons())
    return child

layout2 = GridLayout(cols=3, spacing=10, size_hint_y=None)    
layout2.add_widget(child_scroll())
layout2.add_widget(child_scroll())
layout2.add_widget(child_scroll())

root = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))
root.add_widget(layout2)

runTouchApp(root)

Each of the three columns scrolls independently.

I can see that one might also want to have the three columns move together (though a slot machine presumably does not allow this)

But I don't understand how to disambiguate one control signal (button, gesture, big handle on the side of the slot machine) to do two different functions (independent scroll, and synchronized scroll) in the same screen area.

My lack of understanding is about UI design, not a about code, and unrelated to how the control signal is generated.

RobertFlatt avatar Mar 03 '23 01:03 RobertFlatt

I did a little more experimenting. As far as I can tell, and I may be wrong:

  • Touch events are not propagated to the parent ScrollView.
  • Wheel Touch events are not propagated to the parent or to child ScrollView

Which I assume is what it says here https://kivy.org/doc/stable/api-kivy.uix.scrollview.html#scrolling-behavior

Without an event g4k is not going to respond.

But you can steal the touch events from ScrollView by messing with the method resolution order. But I don't know how portable or reliable this is. If HScrollView is the parent, and ScrollView is the child

class HScrollView(CommonGestures, ScrollView):

    def cgb_pan(self, touch, focus_x, focus_y, delta_x, velocity):
        print('pan')
        # do your thing here

RobertFlatt avatar Mar 03 '23 03:03 RobertFlatt

So my application has a ScrollView which scrolls horizontally. This Scroll View contains another GridLayout which has itself multiple children which scroll too.

Using the default ScrollView works. The problem is that I cannot scroll horizontally bc I don't have a horizontal mouse button and I don't like to use the touch emulation.

I want to emulate the behavior you get when you comment all on_scroll_start, on_scroll_stop, on_scroll_move, cgb_scroll, cgb_pan in the example.

Now I tried to implement that with the gestures4kivy like in said example. But you should discover that the scrolling is only sent to the parent and not the child ScrollViews. Compare that to the behavior I got before where on a horizontal scroll first the inner ScrollView scrolled until it reached its limit, then the outer ScrollView scrolls until your cursor is on top of another ScrollView. Then the inner one scrolls again, until the limit is reached...

It is this behavior I'm trying to replicated with gestures4kivy.

AliSot2000 avatar Mar 03 '23 04:03 AliSot2000

g4k gestures have the scope of the widget that inherits CommonGestures.

You can of course use the callbacks to call methods in child widgets if you want to.

There is no additional magic behavior for any co-inherited Widget.

I really don't understand enough about interacting ScrollViews to comprehend the mechanism behind the behavior you describe.

Have fun figuring it out.

RobertFlatt avatar Mar 03 '23 06:03 RobertFlatt