Skip to content

Commit ab7183a

Browse files
Connect4: add directional controls
1 parent 2a0e788 commit ab7183a

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

CLAUDE.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,54 @@ def handle_result(self, result):
447447
- `mpos.ui.focus_direction`: Keyboard/joystick navigation helpers
448448
- `mpos.ui.anim`: Animation utilities
449449

450+
### Keyboard and Focus Navigation
451+
452+
MicroPythonOS supports keyboard/joystick navigation through LVGL's focus group system. This allows users to navigate apps using arrow keys and select items with Enter.
453+
454+
**Basic focus handling pattern**:
455+
```python
456+
def onCreate(self):
457+
# Get the default focus group
458+
focusgroup = lv.group_get_default()
459+
if not focusgroup:
460+
print("WARNING: could not get default focusgroup")
461+
462+
# Create a clickable object
463+
button = lv.button(screen)
464+
465+
# Add focus/defocus event handlers
466+
button.add_event_cb(lambda e, b=button: self.focus_handler(b), lv.EVENT.FOCUSED, None)
467+
button.add_event_cb(lambda e, b=button: self.defocus_handler(b), lv.EVENT.DEFOCUSED, None)
468+
469+
# Add to focus group (enables keyboard navigation)
470+
if focusgroup:
471+
focusgroup.add_obj(button)
472+
473+
def focus_handler(self, obj):
474+
"""Called when object receives focus"""
475+
obj.set_style_border_color(lv.theme_get_color_primary(None), lv.PART.MAIN)
476+
obj.set_style_border_width(2, lv.PART.MAIN)
477+
obj.scroll_to_view(True) # Scroll into view if needed
478+
479+
def defocus_handler(self, obj):
480+
"""Called when object loses focus"""
481+
obj.set_style_border_width(0, lv.PART.MAIN)
482+
```
483+
484+
**Key principles**:
485+
- Get the default focus group with `lv.group_get_default()`
486+
- Add objects to the focus group to make them keyboard-navigable
487+
- Use `lv.EVENT.FOCUSED` to highlight focused elements (usually with a border)
488+
- Use `lv.EVENT.DEFOCUSED` to remove highlighting
489+
- Use theme color for consistency: `lv.theme_get_color_primary(None)`
490+
- Call `scroll_to_view(True)` to auto-scroll focused items into view
491+
- The focus group automatically handles arrow key navigation between objects
492+
493+
**Example apps with focus handling**:
494+
- **Launcher** (`builtin/apps/com.micropythonos.launcher/assets/launcher.py`): App icons are focusable
495+
- **Settings** (`builtin/apps/com.micropythonos.settings/assets/settings_app.py`): Settings items are focusable
496+
- **Connect 4** (`apps/com.micropythonos.connect4/assets/connect4.py`): Game columns are focusable
497+
450498
**Other utilities**:
451499
- `mpos.apps.good_stack_size()`: Returns appropriate thread stack size for platform (16KB ESP32, 24KB desktop)
452500
- `mpos.wifi`: WiFi management utilities

internal_filesystem/apps/com.micropythonos.connect4/assets/connect4.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ def onCreate(self):
129129
self.pieces.append(piece_row)
130130

131131
# Create column buttons (invisible clickable areas)
132+
focusgroup = lv.group_get_default()
133+
if not focusgroup:
134+
print("WARNING: could not get default focusgroup")
135+
132136
for col in range(self.COLS):
133137
btn = lv.obj(self.screen)
134138
btn.set_size(self.CELL_SIZE, self.ROWS * self.CELL_SIZE)
@@ -138,13 +142,28 @@ def onCreate(self):
138142
btn.set_style_border_width(0, 0)
139143
btn.add_flag(lv.obj.FLAG.CLICKABLE)
140144
btn.add_event_cb(lambda e, c=col: self.on_column_click(c), lv.EVENT.CLICKED, None)
145+
btn.add_event_cb(lambda e, b=btn: self.focus_column(b), lv.EVENT.FOCUSED, None)
146+
btn.add_event_cb(lambda e, b=btn: self.defocus_column(b), lv.EVENT.DEFOCUSED, None)
147+
148+
if focusgroup:
149+
focusgroup.add_obj(btn)
150+
141151
self.column_buttons.append(btn)
142152

143153
self.setContentView(self.screen)
144154

145155
def onResume(self, screen):
146156
self.last_time = time.ticks_ms()
147157

158+
def focus_column(self, column_btn):
159+
"""Highlight column when focused"""
160+
column_btn.set_style_border_color(lv.theme_get_color_primary(None), lv.PART.MAIN)
161+
column_btn.set_style_border_width(3, lv.PART.MAIN)
162+
163+
def defocus_column(self, column_btn):
164+
"""Remove highlight when unfocused"""
165+
column_btn.set_style_border_width(0, lv.PART.MAIN)
166+
148167
def cycle_difficulty(self, event):
149168
if self.animating:
150169
return

0 commit comments

Comments
 (0)