Keyboard and Mouse

Warning

This page is not fully done, and the way functui handles keyboard and mouse navigation will be changed soon!

Important

This page assumes that you have already a way to get a InputEvent. If that’s not the case, check out I/O Overview.

Functui has the functui.nav module which provides the NavState class and multiple nodes to allow interactivity.

Keyboard Navigation

To facilitate keyboard navigation, you need to create a tree of InteractibleID s. Note that this tree is created separatly from the renderable ui tree. To create a simple container that can be vertically navigated through (by pressing up and down) use an InteractibleID as a container and create children with the child() method.

To detect if an interactible is active you can use the is_active() method after the update method was called.

To detect if an interactible is selected (was active and then enter was pressed) you can use is_selected() method.

from functui.nav import InteractibleID, NavState, NavAction, ROOT_VERTICAL

selectable_1 = ROOT_VERTICAL.child(0)
selectable_2 = ROOT_VERTICAL.child(1)
selectable_3 = ROOT_VERTICAL.child(2)
nav_tree = [selectable_1, selectable_2, selectable_3]

nav = NavState()

# at first nothing is active, so navigation in any direction
# will activate the first item in tree.
nav = nav.update(nav_tree=nav_tree, action=NavAction.NAV_UP)

# use .is_active method to test if an interactible is active
assert nav.is_active(selectable_1)

# use .is_selected method to test if an interactible is selected()
nav = nav.update(nav_tree=nav_tree, action=NavAction.SELECT_VIE_KEYBOARD)
assert nav.is_active(selectable_2)

nav = nav.update(nav_tree=nav_tree, action=NavAction.NAV_DOWN)
assert nav.is_active(selectable_2)

You can also nest containers and specify their navigation direction.

from functui.nav import InteractibleID, NavState, NavAction, ROOT_VERTICAL, Direction

root = ROOT_VERTICAL

# directiot=Direction.HORIZONTAL means navigating by
# NavAction.NAV_LEFT and NavAction.NAV_RIGHT
inner_container = root.child(0, direction=Direction.HORIZONTAL)
inner_child_1 = inner_container.child(0)
inner_child_2 = inner_container.child(1)
outer_child_1 = root.child(1)

# the above create a tree that looks something like this
# x-------------------------------x
# |x-----------------------------x|
# || inner_child_1 inner_child_2 ||
# |x-----------------------------x|
# | outer_child_1                 |
# x-------------------------------x

nav_tree = [inner_child_1, inner_child_2, outer_child_1]

nav = NavState()

# just activate keyboard navigation.
nav = nav.update(nav_tree=nav_tree, action=NavAction.NAV_DOWN)
assert nav.is_active(inner_child_1)

nav = nav.update(nav_tree=nav_tree, action=NavAction.NAV_RIGHT)
assert nav.is_active(inner_child_2)

nav = nav.update(nav_tree=nav_tree, action=NavAction.NAV_DOWN)
assert nav.is_active(outer_child_1)

Mouse Detection

Mouse detection can be performed with an interaction_area() wrapper node.

To detect if mouse is hovering over an intractable use the is_hover() method.

Unlike keyboard navigation, selection is split into two stages. is_held_down() method returns True while left click is held down. When left click is relased is_selected() returns true. This behaviour is useful for implementing buttons that get highlighted when you hold left click and have ability to be canceled if you move your mouse away.