Skip to content
Snippets Groups Projects
Commit ca3acce0 authored by q3k's avatar q3k
Browse files

py: implement about screen

parent e27387cf
No related branches found
No related tags found
1 merge request!87py: implement about screen
Pipeline #6362 passed
from ctx import Context
from st3m import InputState, Responder
from st3m.ui.view import BaseView, ViewTransitionSwipeRight
from st3m.utils import tau
from st3m.goose import List, Optional
import math
class Screen(Responder):
"""
A screen with some text. Just the text part, without background.
"""
START_Y = -50
FONT = "Camp Font 3"
FONT_SIZE = 15
def __init__(self, text: List[str]) -> None:
self.text = text
def draw(self, ctx: Context) -> None:
ctx.font = self.FONT
ctx.font_size = self.FONT_SIZE
y = self.START_Y
for line in self.text:
ctx.move_to(0, y)
ctx.text(line)
y += self.FONT_SIZE
def think(self, ins: InputState, delta_ms: int) -> None:
pass
class HeroScreen(Screen):
"""
A screen with large text in the middle.
"""
START_Y = 0
FONT = "Camp Font 2"
FONT_SIZE = 30
class IntroScreen(Screen):
"""
Title card which shows on About app startup.
"""
START_Y = 25
def __init__(self) -> None:
super().__init__(["Chaos", "Communication", "Camp 2023"])
def draw(self, ctx: Context) -> None:
ctx.text_align = ctx.MIDDLE
ctx.font = "Camp Font 2"
ctx.font_size = 50
ctx.move_to(0, -33)
ctx.text("flow3r")
ctx.move_to(0, -3)
ctx.text("badge")
super().draw(ctx)
class About(BaseView):
"""
A pseudo-app (applet?) which displays an about screen for the badge.
"""
def __init__(self) -> None:
self.ts = 0.0
self.screens: List[Screen] = [
IntroScreen(),
HeroScreen(
[
"flow3r.garden",
]
),
Screen(
[
"This is Free",
"Software based on",
"code by numerous",
"third parties.",
"",
"Visit",
"flow3r.garden/license",
"for more info.",
]
),
]
self.screen_ix = 0
self.screen_ix_anim = 0.0
super().__init__()
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
self.ts += delta_ms / 1000
if self.input.left_shoulder.middle.pressed:
if self.vm is not None:
self.vm.pop(ViewTransitionSwipeRight())
# Change target screen intent.
if self.input.left_shoulder.left.pressed and self._can_left():
self.screen_ix -= 1
if self.input.left_shoulder.right.pressed and self._can_right():
self.screen_ix += 1
# Calculate animation/transitions.
diff = self.screen_ix - self.screen_ix_anim
if abs(diff) < 0.01:
self.screen_ix_anim = self.screen_ix
else:
if diff > 0:
diff = min(diff, 10 * (delta_ms / 1000))
else:
diff = max(diff, -10 * (delta_ms / 1000))
self.screen_ix_anim += diff
def _can_left(self, ix: Optional[int] = None) -> bool:
"""
Returns true if the given screen index (or the current screen index) can
be decreased.
"""
if ix is None:
ix = self.screen_ix
return ix > 0
def _can_right(self, ix: Optional[int] = None) -> bool:
"""
Returns true if the given screen index (or the current screen index) can
be increased.
"""
if ix is None:
ix = self.screen_ix
return ix < len(self.screens) - 1
def draw(self, ctx: Context) -> None:
# Background
ctx.rectangle(-120, -120, 240, 240)
ctx.rgb(117 / 255, 255 / 255, 226 / 255)
ctx.fill()
ctx.arc(0, 350, 300, 0, tau, 0)
ctx.rgb(61 / 255, 165 / 255, 30 / 255)
ctx.fill()
ctx.save()
y = -40 + math.cos(self.ts * 2 + 10) * 10
ctx.translate(0, y)
for i in range(5):
a = i * tau / 5 + self.ts
size = 30 + math.cos(self.ts) * 5
ctx.save()
ctx.rotate(a)
ctx.translate(30, 0)
ctx.arc(0, 0, size, 0, tau, 0)
ctx.gray(1)
ctx.fill()
ctx.restore()
ctx.arc(0, 0, 20, 0, tau, 0)
ctx.rgb(255 / 255, 247 / 255, 46 / 255)
ctx.fill()
ctx.restore()
# Prepare list of screens to draw. We draw at most two screens if we're
# in a transition, otherwies just one.
#
# List of (screen index, screen object, draw offset) tuples.
draw = []
diff = self.screen_ix - self.screen_ix_anim
if diff > 0.01 or diff < -0.01:
ix = math.floor(self.screen_ix_anim)
draw = [
(ix, self.screens[ix], self.screen_ix_anim - ix),
(ix + 1, self.screens[ix + 1], (self.screen_ix_anim - ix) - 1),
]
else:
draw = [
(self.screen_ix, self.screens[self.screen_ix], 0),
]
# Draw currently visible screens.
for ix, screen, offs in draw:
ctx.save()
ctx.translate(-240 * offs, 0)
# Opaque circle
ctx.start_group()
ctx.global_alpha = 0.5
ctx.rgb(0, 0, 0)
ctx.arc(0, 0, 90, 0, tau, 0)
ctx.fill()
# Draw arrows.
if self._can_left(ix):
ctx.move_to(-105, 20)
ctx.font = "Material Icons"
ctx.font_size = 30
ctx.text_align = ctx.MIDDLE
ctx.text("\ue5c4")
if self._can_right(ix):
ctx.move_to(105, 20)
ctx.font = "Material Icons"
ctx.font_size = 30
ctx.text_align = ctx.MIDDLE
ctx.text("\ue5c8")
ctx.end_group()
# Text
ctx.start_group()
ctx.global_alpha = 1.0
ctx.gray(1)
ctx.text_align = ctx.MIDDLE
screen.draw(ctx)
ctx.end_group()
ctx.restore()
...@@ -6,11 +6,13 @@ from st3m.ui.menu import ( ...@@ -6,11 +6,13 @@ from st3m.ui.menu import (
MenuItemForeground, MenuItemForeground,
MenuItemNoop, MenuItemNoop,
MenuItemAction, MenuItemAction,
MenuItemLaunchPersistentView,
) )
from st3m.ui.elements import overlays from st3m.ui.elements import overlays
from st3m.ui.view import View, ViewManager, ViewTransitionBlend from st3m.ui.view import View, ViewManager, ViewTransitionBlend
from st3m.ui.elements.menus import SimpleMenu, SunMenu from st3m.ui.elements.menus import SimpleMenu, SunMenu
from st3m.application import discover_bundles, BundleMetadata from st3m.application import discover_bundles, BundleMetadata
from st3m.about import About
from st3m import settings, logging, processors from st3m import settings, logging, processors
import captouch, audio, leds, gc import captouch, audio, leds, gc
...@@ -124,7 +126,7 @@ def run_main() -> None: ...@@ -124,7 +126,7 @@ def run_main() -> None:
MenuItemBack(), MenuItemBack(),
MenuItemForeground("Settings", menu_settings), MenuItemForeground("Settings", menu_settings),
MenuItemNoop("Disk Mode"), MenuItemNoop("Disk Mode"),
MenuItemNoop("About"), MenuItemLaunchPersistentView("About", About),
MenuItemAction("Yeet Local Changes", yeet_local_changes), MenuItemAction("Yeet Local Changes", yeet_local_changes),
MenuItemAction("Reboot", lambda: machine.reset()), MenuItemAction("Reboot", lambda: machine.reset()),
], ],
......
...@@ -88,6 +88,30 @@ class MenuItemAction(MenuItem): ...@@ -88,6 +88,30 @@ class MenuItemAction(MenuItem):
return self._label return self._label
class MenuItemLaunchPersistentView(MenuItem):
"""
A MenuItem which lazily loads a View class on first run.
"""
def __init__(self, label: str, cons: Callable[[], View]) -> None:
self._label = label
self._cons = cons
self._instance: Optional[View] = None
def press(self, vm: Optional[ViewManager]) -> None:
if self._instance is None:
self._instance = self._cons()
if vm is not None:
vm.push(self._instance, ViewTransitionSwipeLeft())
else:
log.warning(
f"Could not foreground {self._instance} as no ViewManager is present"
)
def label(self) -> str:
return self._label
class MenuItemNoop(MenuItem): class MenuItemNoop(MenuItem):
""" """
A MenuItem which does nothing. A MenuItem which does nothing.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment