wasp: launcher: Experimental launcher implementation
It is not really the launcher itself that is immature. Rather that the framework and UI concepts to move between applications isn't complete yet.
This commit is contained in:
parent
59bb70fa64
commit
8ed80eeeba
13 changed files with 129 additions and 8 deletions
BIN
res/app_icon.png
Normal file
BIN
res/app_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7 KiB |
BIN
res/clock_icon.png
Normal file
BIN
res/clock_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 965 B |
BIN
res/down_arrow.png
Normal file
BIN
res/down_arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 599 B |
BIN
res/settings_icon.png
Normal file
BIN
res/settings_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
res/torch_icon.png
Normal file
BIN
res/torch_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 820 B |
BIN
res/up_arrow.png
Normal file
BIN
res/up_arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 593 B |
|
@ -3,6 +3,7 @@
|
|||
|
||||
import wasp
|
||||
|
||||
import icons
|
||||
import fonts.clock as digits
|
||||
|
||||
DIGITS = (
|
||||
|
@ -25,6 +26,8 @@ class ClockApp():
|
|||
|
||||
Shows a time (as HH:MM) together with a battery meter and the date.
|
||||
"""
|
||||
NAME = 'Clock'
|
||||
ICON = icons.clock
|
||||
|
||||
def __init__(self):
|
||||
self.meter = wasp.widgets.BatteryMeter()
|
||||
|
|
|
@ -3,11 +3,15 @@
|
|||
|
||||
import wasp
|
||||
|
||||
import icons
|
||||
|
||||
class FlashlightApp(object):
|
||||
"""Trivial flashlight application.
|
||||
|
||||
Shows a pure white screen with the backlight set to maximum.
|
||||
"""
|
||||
NAME = 'Torch'
|
||||
ICON = icons.torch
|
||||
|
||||
def foreground(self):
|
||||
"""Activate the application."""
|
||||
|
|
80
wasp/apps/launcher.py
Normal file
80
wasp/apps/launcher.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
# Copyright (C) 2020 Daniel Thompson
|
||||
|
||||
import wasp
|
||||
import icons
|
||||
|
||||
class LauncherApp():
|
||||
"""An application launcher application.
|
||||
"""
|
||||
NAME = 'Launcher'
|
||||
ICON = icons.app
|
||||
|
||||
def foreground(self):
|
||||
"""Activate the application."""
|
||||
self._page = 0
|
||||
self._draw()
|
||||
wasp.system.request_event(wasp.EventMask.TOUCH |
|
||||
wasp.EventMask.SWIPE_UPDOWN)
|
||||
|
||||
def swipe(self, event):
|
||||
i = self._page
|
||||
n = self._num_pages
|
||||
if event[0] == wasp.EventType.UP:
|
||||
i += 1
|
||||
if i >= n:
|
||||
i -= 1
|
||||
wasp.watch.vibrator.pulse()
|
||||
return
|
||||
else:
|
||||
i -= 1
|
||||
if i < 0:
|
||||
wasp.system.switch(wasp.system.applications[0])
|
||||
return
|
||||
|
||||
self._page = i
|
||||
wasp.watch.display.mute(True)
|
||||
self._draw()
|
||||
wasp.watch.display.mute(False)
|
||||
|
||||
def touch(self, event):
|
||||
page = self._get_page(self._page)
|
||||
x = event[1]
|
||||
y = event[2]
|
||||
app = page[2 * (y // 120) + (x // 120)]
|
||||
if app:
|
||||
wasp.system.switch(app)
|
||||
else:
|
||||
wasp.watch.vibrator.pulse()
|
||||
|
||||
@property
|
||||
def _num_pages(self):
|
||||
"""Work out what the highest possible pages it."""
|
||||
num_apps = len(wasp.system.applications)
|
||||
return (num_apps + 3) // 4
|
||||
|
||||
def _get_page(self, i):
|
||||
apps = wasp.system.applications
|
||||
page = apps[4*i: 4*(i+1)]
|
||||
while len(page) < 4:
|
||||
page.append(None)
|
||||
return page
|
||||
|
||||
def _draw(self):
|
||||
"""Redraw the display from scratch."""
|
||||
def draw_app(app, x, y):
|
||||
if not app:
|
||||
return
|
||||
draw.set_color(0xffff)
|
||||
draw.rleblit(app.ICON, (x+13, y+12))
|
||||
draw.set_color(0xbdb6)
|
||||
draw.string(app.NAME, x, y+120-30, 120)
|
||||
|
||||
draw = wasp.watch.drawable
|
||||
page = self._get_page(self._page)
|
||||
|
||||
draw.fill()
|
||||
draw_app(page[0], 0, 0)
|
||||
draw_app(page[1], 120, 0)
|
||||
draw_app(page[2], 0, 120)
|
||||
draw_app(page[3], 120, 120)
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
import machine
|
||||
import wasp
|
||||
import icons
|
||||
|
||||
class TestApp():
|
||||
"""Simple test application.
|
||||
"""
|
||||
NAME = 'Self Test'
|
||||
ICON = icons.app
|
||||
|
||||
def __init__(self):
|
||||
self.tests = ('Touch', 'String', 'Button', 'Crash')
|
||||
|
@ -57,6 +60,7 @@ class TestApp():
|
|||
def benchmark_string(self):
|
||||
draw = wasp.watch.drawable
|
||||
draw.fill(0, 0, 30, 240, 240-30)
|
||||
self.scroll.draw()
|
||||
t = machine.Timer(id=1, period=8000000)
|
||||
t.start()
|
||||
draw.string("The quick brown", 12, 24+24)
|
||||
|
|
|
@ -5,8 +5,9 @@ freeze('.', 'watch.py', opt=3)
|
|||
freeze('../..',
|
||||
(
|
||||
'apps/clock.py',
|
||||
'apps/testapp.py',
|
||||
'apps/flashlight.py',
|
||||
'apps/launcher.py',
|
||||
'apps/testapp.py',
|
||||
'boot.py',
|
||||
'demo.py',
|
||||
'draw565.py',
|
||||
|
|
|
@ -4,6 +4,15 @@
|
|||
# 1-bit RLE, generated from res/battery.png, 189 bytes
|
||||
battery = (36, 48, b'\x97\x0e\x14\x12\x11\x14\x10\x14\x0c\x08\x0c\x08\x08\x08\x0c\x08\x08\x08\x0c\x08\x08\x08\x0c\x08\x08\x04\x14\x04\x08\x04\x14\x04\x08\x04\x0c\x04\x04\x04\x08\x04\x0b\x05\x04\x04\x08\x04\n\x06\x04\x04\x08\x04\t\x07\x04\x04\x08\x04\x08\x07\x05\x04\x08\x04\x07\x07\x06\x04\x08\x04\x06\x07\x07\x04\x08\x04\x05\x07\x08\x04\x08\x04\x04\x0e\x02\x04\x08\x04\x03\x0f\x02\x04\x08\x04\x02\x10\x02\x04\x08\x04\x02\x10\x02\x04\x08\x04\x02\x0f\x03\x04\x08\x04\x02\x0e\x04\x04\x08\x04\x08\x07\x05\x04\x08\x04\x07\x07\x06\x04\x08\x04\x06\x07\x07\x04\x08\x04\x05\x07\x08\x04\x08\x04\x04\x07\t\x04\x08\x04\x04\x06\n\x04\x08\x04\x04\x05\x0b\x04\x08\x04\x04\x04\x0c\x04\x08\x04\x14\x04\x08\x04\x14\x04\x08\x04\x14\x04\x08\x04\x14\x04\x08\x1c\x08\x1c\x08\x1c\x08\x1c\x98')
|
||||
|
||||
# 1-bit RLE, generated from res/app_icon.png, 441 bytes
|
||||
app = (96, 64, b'\x1e$<$<$;&\x97,20/2-4,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03\x0c\x03\x10\x03\x0c\x03,\x03\n\x07\x0c\x07\n\x03,\x03\t\x03\x02\x04\n\x04\x02\x03\t\x03,\x03\x08\x02\x07\x02\x08\x02\x07\x02\x08\x03,\x03\x07\x02\t\x02\x06\x02\t\x02\x07\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x05\x03\x0b\x02\x04\x02\x0b\x03\x05\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x01\x07\x03,\x03\x07\x02\n\x02\x04\x02\n\x02\x07\x03+\x04\x08\x02\t\x02\x04\x02\t\x02\x08\x03*\x05\t\x0c\x04\x0c\t\x03*\x05\n\x0b\x04\x0b\n\x03*\x05.\x03*\x05.\x03*\x05.\x03*\x05.\x03*\x05\n\x0b\x04\x0b\n\x03+\x04\t\x0c\x04\x0c\t\x03,\x03\x08\x02\t\x02\x04\x02\t\x02\x08\x03,\x03\x07\x02\n\x02\x04\x02\n\x02\x07\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x01\x07\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x05\x03\x0b\x02\x04\x02\x0b\x03\x05\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x07\x02\t\x02\x06\x02\t\x02\x07\x03,\x03\x08\x02\x07\x02\x08\x02\x07\x02\x08\x03,\x03\t\x03\x02\x04\n\x04\x02\x03\t\x03,\x03\n\x06\x0e\x06\n\x03,\x03\x0c\x03\x10\x03\x0c\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,4-2/02,\x97&;$<$<$\x1e')
|
||||
|
||||
# 1-bit RLE, generated from res/clock_icon.png, 301 bytes
|
||||
clock = (96, 64, b'\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xcd\x06\r\x06!\x05\x0b\x08\x0c\x08\x0b\n\x1d\t\x07\x0c\n\x08\n\x0c\x1b\x0b\x06\x0e\x08\x03\x02\x03\n\x04\x05\x04\x1a\x04\x03\x04\x06\x02\x08\x04\r\x03\t\x04\x07\x03\x19\x04\x05\x04\x10\x04\x0c\x03\t\x03\t\x02\x19\x03\x07\x03\x11\x03\x0c\x03\t\x03\t\x03\n\x04\t\x04\x07\x04\x10\x03\x0c\x03\t\x03\t\x03\n\x04\t\x03\t\x03\x10\x03\x0c\x03\t\x03\t\x03\n\x04\t\x03\t\x03\x10\x03\x0c\x03\t\x04\x07\x04\n\x04\t\x03\t\x03\x0f\x04\x0c\x03\n\x04\x05\x05\n\x04\t\x03\x03\x02\x04\x03\x0e\x04\r\x03\n\n\x01\x03\x17\x03\x02\x04\x03\x03\r\x05\r\x03\x0b\t\x01\x03\x17\x03\x02\x03\x04\x03\x0c\x05\x0e\x03\r\x05\x03\x03\x17\x03\t\x03\x0b\x05\x0f\x03\x15\x03\x17\x03\t\x03\n\x05\x10\x03\x14\x04\x17\x03\t\x03\t\x05\x11\x03\x14\x03\x18\x04\x07\x04\x08\x04\x13\x03\x14\x03\x19\x03\x07\x03\x08\x04\x14\x03\x13\x04\x0b\x04\n\x03\x06\x04\x07\x04\x15\x03\x0b\x01\x05\x05\x0c\x04\x0b\x04\x03\x04\x07\x03\x12\r\x06\n\r\x04\x0b\x0b\x06\x0f\x07\r\x06\t\x0e\x04\x0c\t\x07\x0f\x07\r\x07\x06\x10\x04\x0e\x05\t\x0f\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xaa')
|
||||
|
||||
# 1-bit RLE, generated from res/torch_icon.png, 283 bytes
|
||||
torch = (96, 64, b'\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00e\x06W\nT\x04\x06\x02S\x03\x07\x02S\x02\n\x01\x0b\x029\x05\x08\x02\t\x02\x08\x03:\x07\x06\x02\x0b\x01\x06\x02$(\n\x02\x03\x03%(\x0c\x01+\x02%\x01\x0b\x02+\x02%\x01\x0c\x01+\x02\x05\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x05\x01\x0b\x02+\x02\x04\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x04\x01\x0c\x01+\x02%\x01\x0b\x02\x03\n\x1e\x02\x05\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x05\x01\x0c\x01+\x02\x04\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x04\x01\x0b\x02+\x02%\x01\x0c\x01+\x02%\x01\x0b\x02+(\x0c\x01,(\n\x02\x03\x03L\x02\x0b\x01\x06\x02K\x02\t\x02\x08\x03H\x02\n\x01\x0b\x02G\x03\x07\x02U\x04\x06\x02V\nY\x06\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xe2')
|
||||
|
||||
# 1-bit RLE, generated from res/up_arrow.png, 16 bytes
|
||||
up_arrow = (16, 9, b'\x07\x02\r\x04\x0b\x06\t\x08\x07\n\x05\x0c\x03\x0e\x01 ')
|
||||
|
||||
|
|
34
wasp/wasp.py
34
wasp/wasp.py
|
@ -15,6 +15,7 @@ import widgets
|
|||
|
||||
from apps.clock import ClockApp
|
||||
from apps.flashlight import FlashlightApp
|
||||
from apps.launcher import LauncherApp
|
||||
from apps.testapp import TestApp
|
||||
|
||||
class EventType():
|
||||
|
@ -86,6 +87,8 @@ class Manager():
|
|||
self.applications = []
|
||||
self.blank_after = 15
|
||||
self.charging = True
|
||||
self.launcher = LauncherApp()
|
||||
|
||||
self._brightness = 2
|
||||
self._button = PinHandler(watch.button)
|
||||
|
||||
|
@ -142,23 +145,40 @@ class Manager():
|
|||
quick application ring. Applications on the ring are not permitted
|
||||
to subscribe to :py:data`EventMask.SWIPE_LEFTRIGHT` events.
|
||||
|
||||
Swipe up is used to bring up the launcher. Clock applications are not
|
||||
permitted to subscribe to :py:data`EventMask.SWIPE_UPDOWN` events since
|
||||
they should expect to be the default application (and is important that
|
||||
we can trigger the launcher from the default application).
|
||||
|
||||
:param int direction: The direction of the navigation
|
||||
"""
|
||||
app_list = self.applications
|
||||
|
||||
if direction == EventType.LEFT:
|
||||
i = app_list.index(self.app) + 1
|
||||
if i >= len(app_list):
|
||||
if self.app in app_list:
|
||||
i = app_list.index(self.app) + 1
|
||||
if i >= len(app_list):
|
||||
i = 0
|
||||
else:
|
||||
i = 0
|
||||
self.switch(app_list[i])
|
||||
elif direction == EventType.RIGHT:
|
||||
i = app_list.index(self.app) - 1
|
||||
if i < 0:
|
||||
i = len(app_list)-1
|
||||
if self.app in app_list:
|
||||
i = app_list.index(self.app) - 1
|
||||
if i < 0:
|
||||
i = len(app_list)-1
|
||||
else:
|
||||
i = 0
|
||||
self.switch(app_list[i])
|
||||
elif direction == EventType.UP:
|
||||
self.switch(self.launcher)
|
||||
elif direction == EventType.DOWN:
|
||||
if self.app != app_list[0]:
|
||||
self.switch(app_list[0])
|
||||
else:
|
||||
watch.vibrator.pulse()
|
||||
elif direction == EventType.HOME:
|
||||
i = app_list.index(self.app)
|
||||
if i != 0:
|
||||
if self.app != app_list[0]:
|
||||
self.switch(app_list[0])
|
||||
else:
|
||||
self.sleep()
|
||||
|
|
Loading…
Reference in a new issue