kivy/kivy — pull request #1909 by matham (2014-02-21 08:36:42+00:00)
Title: initial focus behavior.

This adds an initial FocusBehavior. It's not complete, and is missing docs. I want to first see if this is in the right direction.

The main thing that needs to be explained is the reason for allowing the user to supply a keyboard using the keyboard property. The reason is imagine having multiple keyboards in the systemandmulti mode. Then as the user tabs through widgets, at each tab, a keyboard is requested and released. This is fairly slow. If the user supplies the keyboard, we don't have to request and release, we just bind and unbind which is very fast. This doesn't seem to be an issue when the keyboard mode is single keyboard.

You can see the large speed difference by running the example code below with and without commenting out Clock.schedule_once(self.late_start, 5). By leaving it in, and clicking on a button and then holding down tab you'll see it's a lot faster than if its commented out.

from kivy.config import Config
Config.set('kivy', 'keyboard_mode', 'systemandmulti')

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.behaviors import FocusBehavior
from kivy.core.window import Window
from kivy.clock import Clock

class FocusButton(FocusBehavior, Button):

def on_focused(self, instance, value, *largs):
self.background_color = [1, 0, 0, 1] if value else [1, 1, 1, 1]

def keyboard_on_key_down(self, window, keycode, text, modifiers):
if super(FocusButton, self).keyboard_on_key_down(window, keycode,
text, modifiers):
return True
self.text = keycode[1]
return True

class FocusApp(App):

def build(self):
root = BoxLayout()
self.grid1 = grid1 = GridLayout(cols=4)
self.grid2 = grid2 = GridLayout(cols=4)
root.add_widget(grid1)
root.add_widget(grid2)
for i in range(40):
grid1.add_widget(FocusButton(text=str(i)))
for i in range(40):
grid2.add_widget(FocusButton(text=str(i)))
FocusBehavior.autopopulate_focus(grid1)
FocusBehavior.autopopulate_focus(grid2)
Clock.schedule_once(self.late_start, 5)
return root

def late_start(self, *l):
keyboard1 = Window.request_keyboard(None, self.grid1.children[-1])
keyboard2 = Window.request_keyboard(None, self.grid2.children[-1])
for c in self.grid1.children:
c.keyboard = keyboard1
for c in self.grid2.children:
c.keyboard = keyboard2

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