summaryrefslogtreecommitdiff
path: root/wasp
diff options
context:
space:
mode:
authorDaniel Thompson <daniel@redfelineninja.org.uk>2020-04-06 21:03:05 (GMT)
committerDaniel Thompson <daniel@redfelineninja.org.uk>2020-04-06 21:04:21 (GMT)
commit8ed80eeebab06dba5839a7f08703494a9917348a (patch)
tree42ae928cd966ca56ca23345cad6653fbca5dc727 /wasp
parent59bb70fa649d1994d367e69fb6272f36a6176825 (diff)
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.
Diffstat (limited to 'wasp')
-rw-r--r--wasp/apps/clock.py3
-rw-r--r--wasp/apps/flashlight.py4
-rw-r--r--wasp/apps/launcher.py80
-rw-r--r--wasp/apps/testapp.py4
-rw-r--r--wasp/boards/pinetime/manifest.py3
-rw-r--r--wasp/icons.py9
-rw-r--r--wasp/wasp.py34
7 files changed, 129 insertions, 8 deletions
diff --git a/wasp/apps/clock.py b/wasp/apps/clock.py
index 4feba63..4236d9a 100644
--- a/wasp/apps/clock.py
+++ b/wasp/apps/clock.py
@@ -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()
diff --git a/wasp/apps/flashlight.py b/wasp/apps/flashlight.py
index 13e3443..c4702a0 100644
--- a/wasp/apps/flashlight.py
+++ b/wasp/apps/flashlight.py
@@ -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."""
diff --git a/wasp/apps/launcher.py b/wasp/apps/launcher.py
new file mode 100644
index 0000000..274ea9c
--- /dev/null
+++ b/wasp/apps/launcher.py
@@ -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)
diff --git a/wasp/apps/testapp.py b/wasp/apps/testapp.py
index 8c15848..478249f 100644
--- a/wasp/apps/testapp.py
+++ b/wasp/apps/testapp.py
@@ -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)
diff --git a/wasp/boards/pinetime/manifest.py b/wasp/boards/pinetime/manifest.py
index 191ee3d..30d54e2 100644
--- a/wasp/boards/pinetime/manifest.py
+++ b/wasp/boards/pinetime/manifest.py
@@ -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',
diff --git a/wasp/icons.py b/wasp/icons.py
index c3089c4..76b5ee7 100644
--- a/wasp/icons.py
+++ b/wasp/icons.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 ')
diff --git a/wasp/wasp.py b/wasp/wasp.py
index a18c466..6016bd5 100644
--- a/wasp/wasp.py
+++ b/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()