99 lines
3.6 KiB
Python
99 lines
3.6 KiB
Python
from kivy.app import App
|
|
from kivy.uix.gridlayout import GridLayout
|
|
from kivy.uix.boxlayout import BoxLayout
|
|
from kivy.uix.label import Label
|
|
from kivy.uix.behaviors import FocusBehavior
|
|
from kivy.graphics import Color, Rectangle
|
|
|
|
|
|
class FocusWithColor(FocusBehavior):
|
|
''' Class that when focused, changes its background color to red.
|
|
'''
|
|
|
|
_color = None
|
|
_rect = None
|
|
|
|
def __init__(self, **kwargs):
|
|
super(FocusWithColor, self).__init__(**kwargs)
|
|
with self.canvas:
|
|
self._color = Color(1, 1, 1, .2)
|
|
self._rect = Rectangle(size=self.size, pos=self.pos)
|
|
self.bind(size=self._update_rect, pos=self._update_rect)
|
|
|
|
def _update_rect(self, instance, value):
|
|
self._rect.pos = instance.pos
|
|
self._rect.size = instance.size
|
|
|
|
def on_focused(self, instance, value, *largs):
|
|
self._color.rgba = [1, 0, 0, .2] if value else [1, 1, 1, .2]
|
|
|
|
|
|
class FocusLabel(FocusWithColor, Label):
|
|
'''A label, which in addition to turn red when focused, it also sets the
|
|
keyboard input to the text of the label.
|
|
'''
|
|
|
|
def keyboard_on_key_down(self, window, keycode, text, modifiers):
|
|
'''We call super before doing anything else to enable tab cycling
|
|
by FocusBehavior. If we wanted to use tab for ourselves, we could just
|
|
not call it, or call it if we didn't need tab.
|
|
'''
|
|
if super(FocusLabel, self).keyboard_on_key_down(window, keycode,
|
|
text, modifiers):
|
|
return True
|
|
self.text = keycode[1]
|
|
return True
|
|
|
|
|
|
class FocusGridLayout(FocusWithColor, GridLayout):
|
|
pass
|
|
|
|
|
|
class FocusBoxLayout(FocusWithColor, BoxLayout):
|
|
pass
|
|
|
|
|
|
class FocusApp(App):
|
|
|
|
def build(self):
|
|
root = FocusBoxLayout(padding=[10, 10], spacing=10)
|
|
self.grid1 = grid1 = FocusGridLayout(cols=4, padding=[10, 10],
|
|
spacing=10)
|
|
self.grid2 = grid2 = FocusGridLayout(cols=4, padding=[10, 10],
|
|
spacing=10)
|
|
root.add_widget(FocusLabel(text='Left', size_hint_x=0.4))
|
|
root.add_widget(grid1)
|
|
root.add_widget(grid2)
|
|
root.add_widget(FocusLabel(text='Right', size_hint_x=0.4))
|
|
|
|
for i in range(40):
|
|
grid1.add_widget(FocusLabel(text='l' + str(i)))
|
|
for i in range(40):
|
|
grid2.add_widget(FocusLabel(text='r' + str(i)))
|
|
|
|
# make elements 29, 9 un-focusable. The widgets are displayed in
|
|
# reverse order, so 9 = 39 - 10
|
|
grid2.children[30].text = grid1.children[14].text =\
|
|
grid2.children[15].text = grid1.children[34].text = 'Skip me'
|
|
grid2.children[15].is_focusable = False
|
|
grid2.children[30].is_focusable = False
|
|
# similarly, make 39 - 14 = 25, and 5 un-focusable
|
|
grid1.children[14].is_focusable = False
|
|
grid1.children[34].is_focusable = False
|
|
# don't move focus passed this element
|
|
grid2.children[35].focus_next = StopIteration
|
|
grid2.children[35].text = 'Stop forward'
|
|
|
|
# exchange the links between the sides so that it'll skip to the other
|
|
# side in the middle. Remember that children are displayed reversed
|
|
# in layouts.
|
|
grid1.children[10].focus_next = grid2.children[9]
|
|
grid2.children[10].focus_next = grid1.children[9]
|
|
grid1.children[10].text = '-->'
|
|
grid2.children[10].text = '<--'
|
|
return root
|
|
|
|
|
|
if __name__ == '__main__':
|
|
FocusApp().run()
|