ForyszeP
ForyszeP

Reputation: 163

Kivy Recycleview. Change label background color on motion event

Is is possible to change background color of without touch events? Please refer to below code. self.collidepoint() method always returns False. I know that even if it will return True it won't work because I should clear RV.data and build it again with new bcolor which seems to be pretty slow way.

CODE

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.core.window import Window
Builder.load_string('''
<SelectableLabel>:
    # Draw a background to indicate selection
    bcolor:  root.bcolor
    canvas.before:
        Color:
            rgba: (0, 0, 0, 1) if self.selected else self.bcolor
        Rectangle:
            pos: self.pos
            size: self.size
<RV>:
    viewclass: 'SelectableLabel'
    SelectableRecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
        multiselect: True
        touch_multiselect: True
''')


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                                 RecycleBoxLayout):
    ''' Adds selection and focus behaviour to the view. '''


class SelectableLabel(RecycleDataViewBehavior, Label):
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)
    bcolor = (0,0,0,1)

    def __init__(self, **kwargs):
        super(SelectableLabel, self).__init__(**kwargs)

        Window.bind(mouse_pos=self.light_up)

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        return super(SelectableLabel, self).refresh_view_attrs(
            rv, index, data)

    def light_up(self, window, mouse_pos):

        print('MOTION ', mouse_pos, self.collide_point(mouse_pos[0], mouse_pos[1]))
        if self.collide_point(mouse_pos[0], mouse_pos[1]):
            self.bcolor = (1,1,1,1)

        else:
            self.bcolor = (0, 0, 0, 1)

class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'text': str(x)} for x in range(100)]


class TestApp(App):
    def build(self):
        return RV()
        Window.bind(on_motion=SelectableLabel.on_motion())

if __name__ == '__main__':
    TestApp().run()

Upvotes: 0

Views: 566

Answers (1)

John Anderson
John Anderson

Reputation: 39082

There are two problems with your code. I have posted a version of your code with what I think are corrections for those problems.

The first is that bcolor in SelectableLabel needs to be a ListProperty (the kv bindings do not work otherwise).

The second is that in your light_up() method, you must convert the window coordinates into coordinates appropriate for passing to collide_point(). To do this, I save a reference to the RV instance in each SelectableLabel (as self.root). And use the to_local() to do the coordinate transformation.

Also, the Window.bind() in the build() method is unnecessary.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty, ListProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.core.window import Window

Builder.load_string('''
<SelectableLabel>:
    # Draw a background to indicate selection
    bcolor:  root.bcolor
    canvas.before:
        Color:
            rgba: (0, 0, 0, 1) if self.selected else self.bcolor
        Rectangle:
            pos: self.pos
            size: self.size
<RV>:
    viewclass: 'SelectableLabel'
    SelectableRecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
        multiselect: True
        touch_multiselect: True
''')


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                                 RecycleBoxLayout):
    ''' Adds selection and focus behaviour to the view. '''


class SelectableLabel(RecycleDataViewBehavior, Label):
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)
    bcolor = ListProperty([0,0,0,1])

    def __init__(self, **kwargs):
        super(SelectableLabel, self).__init__(**kwargs)
        self.root = App.get_running_app().root
        Window.bind(mouse_pos=self.light_up)

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        return super(SelectableLabel, self).refresh_view_attrs(
            rv, index, data)

    def light_up(self, window, mouse_pos):
        pos_in_parent = self.root.to_local(*mouse_pos)
        print('MOTION ', pos_in_parent, self.collide_point(pos_in_parent[0], pos_in_parent[1]))
        if self.collide_point(pos_in_parent[0], pos_in_parent[1]):
            self.bcolor = (1,1,1,1)
        else:
            self.bcolor = (0, 0, 0, 1)


class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'text': str(x)} for x in range(100)]


class TestApp(App):
    def build(self):
        return RV()

if __name__ == '__main__':
    TestApp().run()

Upvotes: 1

Related Questions