1
0
Fork 0

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.
pull/24/head
Daniel Thompson 2020-04-06 22:03:05 +01:00
parent 59bb70fa64
commit 8ed80eeeba
13 changed files with 129 additions and 8 deletions

BIN
res/app_icon.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
res/clock_icon.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

BIN
res/down_arrow.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
res/torch_icon.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

BIN
res/up_arrow.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

View File

@ -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()

View File

@ -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."""

View 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)

View File

@ -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)

View File

@ -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',

View File

@ -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 ')

View File

@ -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()