-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This also adds some framework for more complex layouts, that the settings app uses
- Loading branch information
1 parent
a126e72
commit 7e34d3c
Showing
5 changed files
with
279 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
from events.input import BUTTON_TYPES | ||
from . import tokens, utils | ||
|
||
|
||
class Layoutable: | ||
height: int # Available after draw | ||
|
||
def __init__(self): | ||
self.height = 0 | ||
|
||
def draw(self, ctx, focused=False): | ||
return | ||
|
||
async def button_event(self, event): | ||
return False | ||
|
||
|
||
class TextDisplay(Layoutable): | ||
def __init__(self, text, font_size=None, rgb=None): | ||
super().__init__() | ||
self.text = text | ||
if font_size is None: | ||
font_size = tokens.label_font_size | ||
self.font_size = font_size | ||
self.lines = None | ||
if rgb is None: | ||
rgb = tokens.ui_colors["label"] | ||
self.rgb = rgb | ||
|
||
def draw(self, ctx, focused=False): | ||
ctx.save() | ||
if self.lines is None: | ||
self.lines = utils.wrap_text(ctx, self.text) | ||
self.height = len(self.lines) * ctx.font_size | ||
ctx.text_align = ctx.LEFT | ||
if self.rgb: | ||
ctx.rgb(*self.rgb) | ||
for i, line in enumerate(self.lines): | ||
ctx.move_to(0, i * ctx.font_size) | ||
ctx.text(line) | ||
ctx.restore() | ||
|
||
|
||
class ButtonDisplay(Layoutable): | ||
def __init__(self, text, font_size=None, rgb=None): | ||
self.text = text | ||
self.height = 40 | ||
|
||
def draw(self, ctx, focused=False): | ||
ctx.save() | ||
|
||
# Draw button | ||
ctx.translate(30, 5) | ||
ctx.scale(0.75, 0.75) | ||
if focused: | ||
bg = tokens.ui_colors["active_button_background"] | ||
fg = tokens.ui_colors["active_button_text"] | ||
else: | ||
bg = tokens.ui_colors["button_background"] | ||
fg = tokens.ui_colors["active_button_text"] | ||
ctx.rgb(*bg) | ||
ctx.round_rectangle(0, 0, tokens.display_x, 40, 30).fill() | ||
|
||
# Draw text | ||
ctx.rgb(*fg) | ||
ctx.move_to(120, 30) | ||
ctx.text_align = ctx.CENTER | ||
ctx.text(self.text) | ||
|
||
ctx.restore() | ||
|
||
|
||
class DefinitionDisplay(Layoutable): | ||
def __init__(self, label, value): | ||
self.label = label | ||
self.value = value | ||
self.height = 0 | ||
|
||
def draw(self, ctx, focused=False): | ||
ctx.save() | ||
self.height = 0 | ||
|
||
# Draw heading | ||
ctx.font_size = tokens.one_pt * 8 | ||
ctx.text_align = ctx.LEFT | ||
if focused: | ||
ctx.rgb(*tokens.colors["orange"]) | ||
else: | ||
ctx.rgb(*tokens.colors["yellow"]) | ||
|
||
# Draw label | ||
label_lines = utils.wrap_text(ctx, self.label) | ||
for line in label_lines: | ||
ctx.move_to(0, self.height) | ||
ctx.text(line) | ||
self.height += ctx.font_size | ||
|
||
ctx.rgb(*tokens.ui_colors["label"]) | ||
|
||
# Draw value | ||
ctx.font_size = tokens.ten_pt | ||
value_lines = utils.wrap_text(ctx, self.value, width=230) | ||
for line in value_lines: | ||
ctx.move_to(10, self.height) | ||
ctx.text(line) | ||
self.height += ctx.font_size | ||
|
||
ctx.restore() | ||
|
||
|
||
class LinearLayout(Layoutable): | ||
def __init__(self, items): | ||
self.items = items | ||
self.y_offset = 120 | ||
self.scale_factor = 0.9 | ||
super().__init__() | ||
|
||
def draw(self, ctx): | ||
focused_child = self.centred_component() | ||
self.height = 0 | ||
|
||
ctx.save() | ||
# Clip to the screen to be shown | ||
ctx.rectangle(-120, -120, 240, 240).clip() | ||
|
||
# Re-centre so the origin is in the top left | ||
# Use y_offset to move this down | ||
ctx.translate(-120, -120 + self.y_offset) | ||
|
||
# Scale to 90% and centre | ||
ctx.scale(self.scale_factor, self.scale_factor) | ||
ctx.translate(12, 0) | ||
|
||
# Draw each item in turn | ||
for item in self.items: | ||
item.draw(ctx, focused=item == focused_child) | ||
ctx.translate(0, item.height) | ||
self.height += item.height | ||
|
||
ctx.restore() | ||
|
||
def centred_component(self): | ||
cumulative_height = 0 | ||
for item in self.items: | ||
cumulative_height += item.height * self.scale_factor | ||
print(f"Y: {self.y_offset}, cumulative: {cumulative_height}") | ||
if round(cumulative_height) > round(120 - self.y_offset): | ||
return item | ||
return item | ||
|
||
async def button_event(self, event) -> bool: | ||
focused = self.centred_component() | ||
to_jump = min(focused.height * self.scale_factor, 60) | ||
print(f"Y: {self.y_offset}, Jump: {to_jump}") | ||
if BUTTON_TYPES["UP"] in event.button: | ||
self.y_offset += to_jump | ||
if self.y_offset > 120: | ||
self.y_offset = 120 | ||
return True | ||
elif BUTTON_TYPES["DOWN"] in event.button: | ||
self.y_offset -= to_jump | ||
if self.y_offset < 120 - self.height: | ||
self.y_offset = 120 - self.height | ||
return True | ||
else: | ||
return await focused.button_event(event) | ||
|
||
|
||
a = """ | ||
import display, time | ||
import app_components.layout | ||
text = app_components.layout.TextDisplay("Lorem ipsum " + "abcde "*30) | ||
foo = app_components.layout.ButtonDisplay("foo") | ||
bar = app_components.layout.DefinitionDisplay("Wifi", "emfcamp") | ||
layout = app_components.layout.LinearLayout([text, foo, bar]) | ||
ctx=display.get_ctx() | ||
app_components.clear_background(ctx) | ||
ctx.rgb(1,1,1) | ||
layout.draw(ctx) | ||
display.end_frame(ctx) | ||
""" | ||
|
||
""" | ||
def scroll(): | ||
for i in range(20): | ||
ctx=display.get_ctx() | ||
app_components.clear_background(ctx) | ||
ctx.rgb(1,1,1) | ||
layout.draw(ctx) | ||
display.end_frame(ctx) | ||
time.sleep_ms(100) | ||
layout.y_offset -= 10 | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
def fill_line(ctx, text, width_for_line): | ||
extra_text = "" | ||
text_that_fits = text | ||
text_width = ctx.text_width(text_that_fits) | ||
while text_width > width_for_line: | ||
character = text_that_fits[-1] | ||
text_that_fits = text_that_fits[:-1] | ||
extra_text = character + extra_text | ||
text_width = ctx.text_width(text_that_fits) | ||
return text_that_fits, extra_text | ||
|
||
|
||
def wrap_text(ctx, text, width=None): | ||
if width is None: | ||
width = 240 | ||
remaining_text = text | ||
lines = [] | ||
while remaining_text: | ||
line, remaining_text = fill_line(ctx, remaining_text, width) | ||
lines.append(line) | ||
return lines |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import settings | ||
import app | ||
from app_components import layout, tokens | ||
from events.input import BUTTON_TYPES, ButtonDownEvent | ||
from system.eventbus import eventbus | ||
from system.scheduler.events import RequestForegroundPushEvent | ||
|
||
|
||
def string_formatter(value): | ||
if value is None: | ||
return "Default" | ||
else: | ||
return str(value) | ||
|
||
|
||
class SettingsApp(app.App): | ||
def __init__(self): | ||
self.layout = layout.LinearLayout(items=[]) | ||
self.make_layout_children() | ||
eventbus.on_async(ButtonDownEvent, self._button_handler, self) | ||
eventbus.on(RequestForegroundPushEvent, self.make_layout_children, self) | ||
|
||
async def _button_handler(self, event): | ||
layout_handled = await self.layout.button_event(event) | ||
if not layout_handled: | ||
if BUTTON_TYPES["CANCEL"] in event.button: | ||
self.minimise() | ||
|
||
def make_layout_children(self, event=None): | ||
self.layout.items = [] | ||
for id, label, formatter in self.settings_options(): | ||
value = settings.get(id) | ||
self.layout.items.append(layout.DefinitionDisplay(label, formatter(value))) | ||
|
||
def settings_options(self): | ||
return [ | ||
("name", "Name", string_formatter), | ||
("pattern", "LED Pattern", string_formatter), | ||
("wifi_tx_power", "WiFi TX power", string_formatter), | ||
("wifi_connection_timeout", "WiFi connection timeout", string_formatter), | ||
("wifi_ssid", "WiFi SSID", string_formatter), | ||
("wifi_password", "WiFi password", string_formatter), | ||
("wifi_wpa2ent_username", "WPA2 Enterprise Username", string_formatter), | ||
] | ||
|
||
def update(self, delta): | ||
return True | ||
|
||
def draw(self, ctx): | ||
tokens.clear_background(ctx) | ||
self.layout.draw(ctx) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters