Scrolling and focus with arrow keys

廉价感情. 提交于 2019-12-08 13:22:37

问题


I'm trying have a grid layout, that I can have on item foucsed, and I can navigate using the arrow keys (up/down and left/right). the idea is for a leanback expireance application (i.e. android tv, or a home media center), with no touch or mouse.

I'm trying to reuse the FocusBehavior and CompoundSelectionBehavior for that I have something that is almost there, but I can't figure out how do I shift the selection to the next row, left/right keep on the first row that I've click with the mouse, and doesn't move.

from kivy.uix.behaviors.compoundselection import CompoundSelectionBehavior
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.app import App


class SelectableBoxLayout(FocusBehavior, CompoundSelectionBehavior, BoxLayout):

    def keyboard_on_key_down(self, window, keycode, text, modifiers):
        """Based on FocusBehavior that provides automatic keyboard
        access, key presses will be used to select children.
        """
        print(keycode, text, modifiers)
        print(self.orientation)
        if super(SelectableBoxLayout, self).keyboard_on_key_down(
            window, keycode, text, modifiers):
            return True

        if self.orientation == 'horizontal' and keycode[1] in ['up', 'down']:
            self.clear_selection()
            return self.parent.keyboard_on_key_down(window, keycode, text, modifiers)

        if self.select_with_key_down(window, keycode, text, modifiers):
            return True
        return False

    def keyboard_on_key_up(self, window, keycode):
        """Based on FocusBehavior that provides automatic keyboard
        access, key release will be used to select children.
        """
        if super(SelectableBoxLayout, self).keyboard_on_key_up(window, keycode):
            return True
        if self.orientation == 'horizontal' and keycode[1] in ['up', 'down']:
            self.clear_selection()
            return self.parent.keyboard_on_key_up(window, keycode)
        if self.select_with_key_up(window, keycode):
            return True
        return False

    def add_widget(self, widget):
        """ Override the adding of widgets so we can bind and catch their
        *on_touch_down* events. """
        widget.bind(on_touch_down=self.button_touch_down,
                    on_touch_up=self.button_touch_up)
        return super(SelectableBoxLayout, self).add_widget(widget)

    def button_touch_down(self, button, touch):
        """ Use collision detection to select buttons when the touch occurs
        within their area. """
        if button.collide_point(*touch.pos):
            self.select_with_touch(button, touch)

    def button_touch_up(self, button, touch):
        """ Use collision detection to de-select buttons when the touch
        occurs outside their area and *touch_multiselect* is not True. """
        if not (button.collide_point(*touch.pos) or
                self.touch_multiselect):
            self.deselect_node(button)

    def select_node(self, node):
        node.background_color = (1, 0, 0, 1)
        return super(SelectableBoxLayout, self).select_node(node)

    def deselect_node(self, node):
        node.background_color = (1, 1, 1, 1)
        super(SelectableBoxLayout, self).deselect_node(node)

    def on_selected_nodes(self, gird, nodes):
        print("Selected nodes = {0}".format(nodes))

        if self.orientation == 'vertical':
            if nodes:
                row = nodes[0]
                row.clear_selection()
                node_src, idx_src = row._resolve_last_node()
                text = 'left'
                node, idx = row.goto_node(text, node_src, idx_src)
                row.select_node(node)



class TestApp(App):
    def build(self):
        grid = SelectableBoxLayout(orientation='vertical', touch_multiselect=False,
                              multiselect=False)
        for i in range(0, 6):
            row = SelectableBoxLayout(orientation='horizontal', touch_multiselect=False,
                                       multiselect=False)
            for j in range(0,5):
                b = Button(text="Button {0}".format(j))
                row.add_widget(b)
            grid.add_widget(row)
        row.get_focus_next().focus = True
        return grid


TestApp().run()

回答1:


Took me a while debugging, but here's a working example:

from kivy.uix.behaviors.compoundselection import CompoundSelectionBehavior
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.app import App


class SelectableBoxLayout(FocusBehavior, CompoundSelectionBehavior, BoxLayout):

    def keyboard_on_key_down(self, window, keycode, text, modifiers):
        """Based on FocusBehavior that provides automatic keyboard
        access, key presses will be used to select children.
        """
        print(keycode, text, modifiers)
        print(self.orientation)
        if super(SelectableBoxLayout, self).keyboard_on_key_down(
            window, keycode, text, modifiers):
            return True

        if self.orientation == 'horizontal' and keycode[1] in ['up', 'down']:
            self.clear_selection()
            return self.parent.keyboard_on_key_down(window, keycode, text, modifiers)
        if self.orientation == 'vertical' and keycode[1] in ['up', 'down']:
            direction = 'focus_next' if keycode[1] == 'down' else 'focus_previous'

            if self.selected_nodes:
                next_row = self.selected_nodes[0]._get_focus_next(direction)
            else:
                next_row = self.children[-1]
            self.clear_selection()
            self.select_node(next_row)
            if next_row:
                next_row.focus = True
                next = next_row.children[-1]
                if next and not isinstance(next, SelectableBoxLayout):
                    print("moving to {0}".format(next))
                    next.focus = True
                    next_row.clear_selection()
                    next_row.select_node(next)
            return True
        if self.select_with_key_down(window, keycode, text, modifiers):
            return True
        return False

    def keyboard_on_key_up(self, window, keycode):
        """Based on FocusBehavior that provides automatic keyboard
        access, key release will be used to select children.
        """
        if super(SelectableBoxLayout, self).keyboard_on_key_up(window, keycode):
            return True
        if self.orientation == 'horizontal' and keycode[1] in ['up', 'down']:
            return self.parent.keyboard_on_key_up(window, keycode)
        if self.select_with_key_up(window, keycode):
            return True
        return False

    def add_widget(self, widget, index=0):
        """ Override the adding of widgets so we can bind and catch their
        *on_touch_down* events. """
        widget.bind(on_touch_down=self.button_touch_down,
                    on_touch_up=self.button_touch_up)
        return super(SelectableBoxLayout, self).add_widget(widget, index)

    def button_touch_down(self, button, touch):
        """ Use collision detection to select buttons when the touch occurs
        within their area. """
        if button.collide_point(*touch.pos):
            self.select_with_touch(button, touch)

    def button_touch_up(self, button, touch):
        """ Use collision detection to de-select buttons when the touch
        occurs outside their area and *touch_multiselect* is not True. """
        if not (button.collide_point(*touch.pos) or
                self.touch_multiselect):
            self.deselect_node(button)

    def select_node(self, node):
        node.background_color = (1, 0, 0, 1)
        print("select: {}".format(getattr(node, 'text', 'none')))
        return super(SelectableBoxLayout, self).select_node(node)

    def deselect_node(self, node):
        node.background_color = (1, 1, 1, 1)
        print("deselect: {}".format(getattr(node, 'text', 'none')))
        super(SelectableBoxLayout, self).deselect_node(node)

    def on_selected_nodes(self, grid, nodes):
        pass

class TestingappApp(App):
    """Basic kivy app

    Edit testingapp.kv to get started.
    """
    def build(self):
        grid = SelectableBoxLayout(orientation='vertical', touch_multiselect=False,
                              multiselect=False)
        for i in range(0, 6):
            row = SelectableBoxLayout(orientation='horizontal', touch_multiselect=False,
                                       multiselect=False)
            for j in range(0,5):
                b = Button(text="Event A\n TT {}{}".format(i, j))
                row.add_widget(b)
            grid.add_widget(row)
        row.get_focus_next().focus = True
        return grid


来源:https://stackoverflow.com/questions/47212144/scrolling-and-focus-with-arrow-keys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!