From 179d0f24d6da794e71f46f91ee66ea6a7c0041f0 Mon Sep 17 00:00:00 2001
From: iggy <iggy@muc.ccc.de>
Date: Wed, 7 Jun 2023 02:36:39 +0200
Subject: [PATCH] py: menu_tinysynth and menu_settings use the new audio api;
 controls; icons and general ui styling

---
 python_payload/control.py        | 50 ++++++++++++++++---
 python_payload/demo_menu.py      | 12 ++---
 python_payload/menu.py           |  5 +-
 python_payload/menu_settings.py  | 81 +++++++++++++++++++++++++++++-
 python_payload/menu_tinysynth.py |  6 ++-
 python_payload/ui.py             | 85 +++++++++++++++++++++++---------
 6 files changed, 197 insertions(+), 42 deletions(-)

diff --git a/python_payload/control.py b/python_payload/control.py
index c3660aa77c..3f6b5ae6ab 100644
--- a/python_payload/control.py
+++ b/python_payload/control.py
@@ -1,26 +1,43 @@
 import ui
 
 class Control():
-    def __init__(self,name, default=0, on_set=None):
+    def __init__(self,name, default=0, on_set=None, on_get=None, on_mod=None):
         #TODO inheritance from Control()
         self.name=name
         self.on_set = on_set
-        self._value = default
+        self.on_get = on_get
+        self.on_mod = on_mod
+
+        if not self.on_get:
+            self._value = default
         self.ui = ui.IconValue(label=self.name,size=60, value=self.get_value())
+        self.ui.get_value = self.get_normal_value
 
     def draw(self):
+        self.ui.value = self.get_value()
+        print("draw: value", self.get_value())
         self.ui.draw()
 
-    def get_value(self):
+    def get_normal_value(self):
+        v = self.get_value()
+        n = min(1,max(0,v))
+        return n
+
+    def get_value(self,update=True):
+        if update and self.on_get:
+            self._value = self.on_get()
         return self._value
 
     def set_value(self, value, do_trigger=True):
         self._value = value
-        self.ui.value = value
         if do_trigger:
             if self.on_set:
                 self.on_set(value)
     
+    def mod_value(self,delta):
+        self._value = self.on_mod(delta)
+
+    
     def enter(self):
         pass
     
@@ -33,6 +50,10 @@ class Control():
 class ControlSwitch(Control):
     def enter(self):
         self.set_value(not self.get_value())
+        self.draw()
+
+    def scroll(self,delta):
+        self.enter()
 
 class ControlFloat(Control):
     def __init__(self, min=0.0,max=1.0,step=0.1, *args, **kwargs):
@@ -41,15 +62,28 @@ class ControlFloat(Control):
         self.step=step
         super().__init__(*args, **kwargs)
 
+    def get_normal_value(self):
+        v = self.get_value()
+        n = (v-self.min)/(self.max-self.min)
+        #print("normal:",v,n)
+        return n
+
 class ControlKnob(ControlFloat):
     def enter(self):
         #repeat action with current value
         self.set_value(self.get_value())
+        self.draw()
 
     def scroll(self,delta):
-        v = self.get_value()
-        v_new = max(self.min,min(self.max,v+delta*self.step))
-        self.set_value(v_new)
+        if self.on_mod:
+            #print("control: on mod")
+            self.on_mod(delta)
+
+        elif self.on_set:
+            v = self.get_value()
+            v_new = max(self.min,min(self.max,v+delta*self.step))
+            self.set_value(v_new)
+        self.draw()
 
 class ControlSlide(ControlFloat):
     def __init__(self, do_reset=True, *args, **kwargs):
@@ -67,4 +101,4 @@ class ControlSlide(ControlFloat):
         if z<0: #Release
             if self.do_reset:
                 self.set_value(self._saved_value)
-        
\ No newline at end of file
+        self.draw()
\ No newline at end of file
diff --git a/python_payload/demo_menu.py b/python_payload/demo_menu.py
index b72d86be4d..84974dd652 100644
--- a/python_payload/demo_menu.py
+++ b/python_payload/demo_menu.py
@@ -5,7 +5,7 @@ import control
 import audio
 import application
 
-#import demo_worms,demo_sparabo,cap_touch_demo, melodic_demo, harmonic_demo
+import demo_worms,demo_sparabo,cap_touch_demo, melodic_demo, harmonic_demo
 import menu_settings,menu_tinysynth
 
 import time
@@ -19,11 +19,11 @@ menu_apps = menu.Menu("apps")
 menu_music = menu.Menu("music")
 
 
-#for app_module in [demo_sparabo,melodic_demo,harmonic_demo]:
-#    menu_music.add(menu.MenuItemApp(app_module.app))
+for app_module in [demo_sparabo,melodic_demo,harmonic_demo]:
+    menu_music.add(menu.MenuItemApp(app_module.app))
 
-#for app_module in [demo_worms,cap_touch_demo,]:
-#    menu_apps.add(menu.MenuItemApp(app_module.app))
+for app_module in [demo_worms,cap_touch_demo,]:
+    menu_apps.add(menu.MenuItemApp(app_module.app))
 
 testmenu = menu.Menu("test")
 
@@ -40,11 +40,11 @@ testmenu.add(item_add)
 
 
 menu_badge.add(menu.MenuItemSubmenu(testmenu))
-menu_badge.add(menu.MenuItemSubmenu(menu_settings.get_menu()))
 
 menu_main.add(menu.MenuItemSubmenu(menu_badge))
 menu_main.add(menu.MenuItemSubmenu(menu_apps))
 menu_main.add(menu.MenuItemSubmenu(menu_music))
+menu_main.add(menu.MenuItemSubmenu(menu_settings.get_menu()))
 
 
 menu_music.add(menu.MenuItemSubmenu(menu_tinysynth.get_menu()))
diff --git a/python_payload/menu.py b/python_payload/menu.py
index a5aafda483..ab88d6ca60 100644
--- a/python_payload/menu.py
+++ b/python_payload/menu.py
@@ -127,7 +127,8 @@ class MenuItemSubmenu(MenuItem):
         
 class MenuItemBack(MenuItem):
     def __init__(self):
-        super().__init__(name="<-")
+        super().__init__(name="")
+        self.ui = ui.IconLabel(label="back")
     
     def enter(self,data={}):
         menu_back()
@@ -252,7 +253,7 @@ def render():
     if active_menu is None:
         return
     
-    ui.the_ctx.rectangle(-120,-120,240,240).rgb(*ui.GO_GREEN).fill()
+    ui.the_ctx.rectangle(-120,-120,240,240).rgb(*ui.BLACK).fill()
     active_menu.draw()
     #hardware.display_update()
 
diff --git a/python_payload/menu_settings.py b/python_payload/menu_settings.py
index c99e3c80c3..aa74f8ddac 100644
--- a/python_payload/menu_settings.py
+++ b/python_payload/menu_settings.py
@@ -44,8 +44,85 @@ def get_menu():
     item_input_overlay = menu.MenuItemControl("input overlay", control_debug_input)
     m.add(item_input_overlay)
 
-    c = control.ControlKnob(name="Volume",default=0.5,on_set=set_volume)
-    m.add(menu.MenuItemControl("volume",c))
+    
+
+    m_audio = menu.Menu("audio")
+    m_speaker = menu.Menu("speaker")
+    m_head = menu.Menu("headphones")
+
+    vol = control.ControlKnob(name="vol",
+        on_mod=audio.adjust_volume_dB,
+        on_get=audio.get_volume_relative
+    )
+    m_audio.add(menu.MenuItemControl("volume",vol))
+    m_head.add(menu.MenuItemControl("vol head",control.ControlKnob(
+        name="vol",
+        on_mod=audio.headphones_adjust_volume_dB,
+        on_get=audio.headphones_get_volume_relative
+    )))
+
+    m_speaker.add(menu.MenuItemControl("vol speaker",control.ControlKnob(
+        name="vol",
+        on_mod=audio.speaker_adjust_volume_dB,
+        on_get=audio.speaker_get_volume_relative
+    )))
+
+    m_audio.add(menu.MenuItemControl("mute",control.ControlSwitch(
+        name="mute",
+        on_set=audio.set_mute,
+        on_get=audio.get_mute
+    )))
+
+    m_head.add(menu.MenuItemControl("mute head",control.ControlSwitch(
+        name="mute",
+        on_set=audio.headphones_set_mute,
+        on_get=audio.headphones_get_mute
+    )))
+
+    m_head.add(menu.MenuItemControl("detected headphones",control.ControlSwitch(
+        name="connected?",
+        on_get=audio.headphones_are_connected
+    )))
+
+
+    m_speaker.add(menu.MenuItemControl("mute speaker",control.ControlSwitch(
+        name="mute",
+        on_set=audio.speaker_set_mute,
+        on_get=audio.speaker_get_mute
+    )))
+
+    m_speaker.add(menu.MenuItemControl("min vol speaker",control.ControlKnob(
+        name="min",
+        min = -40, max =14, step=1,
+        on_set=audio.speaker_set_minimum_volume_dB,
+        on_get=audio.speaker_get_minimum_volume_dB
+    )))
+    
+    m_speaker.add(menu.MenuItemControl("max vol speaker",control.ControlKnob(
+        name="max",
+        min = -40, max =14, step=1,
+        on_set=audio.speaker_set_maximum_volume_dB,
+        on_get=audio.speaker_get_maximum_volume_dB
+    )))
+
+    m_head.add(menu.MenuItemControl("min vol headphones",control.ControlKnob(
+        name="min",
+        min = -40, max =14, step=1,
+        on_set=audio.headphones_set_minimum_volume_dB,
+        on_get=audio.headphones_get_minimum_volume_dB
+    )))
+    
+    m_head.add(menu.MenuItemControl("max vol headphones",control.ControlKnob(
+        name="max",
+        min = -40, max =14, step=1,
+        on_set=audio.headphones_set_maximum_volume_dB,
+        on_get=audio.headphones_get_maximum_volume_dB
+    )))
+
+    m.add(menu.MenuItemSubmenu(m_audio))
+    m_audio.add(menu.MenuItemSubmenu(m_speaker))
+    m_audio.add(menu.MenuItemSubmenu(m_head))
+
 
     return m
 
diff --git a/python_payload/menu_tinysynth.py b/python_payload/menu_tinysynth.py
index 3f2d95b418..f186c5ab8a 100644
--- a/python_payload/menu_tinysynth.py
+++ b/python_payload/menu_tinysynth.py
@@ -42,7 +42,11 @@ def get_menu():
     pitch = control.ControlSlide(name="pitch",on_set=set_frequency,default=0)
     m.add(menu.MenuItemControl("pitch",pitch))
 
-    vol = control.ControlKnob(name="vol",on_set=set_volume,default=0.0)
+    vol = control.ControlKnob(name="vol",
+        #on_set=set_volume
+        on_mod=audio.adjust_volume_dB,
+        on_get=audio.get_volume_relative
+    )
     m.add(menu.MenuItemControl("volume",vol))
 
     play = control.ControlSwitch(name="play",on_set=set_play,default=False)
diff --git a/python_payload/ui.py b/python_payload/ui.py
index 36616f7895..541e688817 100644
--- a/python_payload/ui.py
+++ b/python_payload/ui.py
@@ -95,30 +95,48 @@ class Icon(UIElement):
         super().__init__()
 
     def _draw(self,pos):
-        #print("ui.Icon._draw()")
-        (x,y) = pos
-
         self.ctx.text_align = self.ctx.CENTER
         self.ctx.text_baseline = self.ctx.MIDDLE
-        self.ctx.font_size = 30
+        self.ctx.font_size=self.size/3
 
-        if self.has_highlight:
-            hs = self.size+5;
-            self.ctx.move_to(x,y-hs/2).rgb(1,1,1).round_rectangle(
-                x-hs/2,
-                y-hs/2,
-                hs,hs,hs//2
-            ).fill()
+        (x,y)=pos
+        hs = 5
         
+        if self.has_highlight:
+            self.ctx.rgb(*GO_GREEN).arc(x,y, self.size/2+hs,-math.pi,math.pi,True).fill()
         self.ctx.move_to(x,y).rgb(*self.bg).arc(x,y,self.size/2,-math.pi,math.pi,True).fill()
-        #self.ctx.move_to(x,y).rgb(self.bg_r,self.bg_g,self.bg_b).circle(x,y,self.size/2).fill()
-        
-        self.ctx.rgb(1,1,1).move_to(x,y).text(self.label)
+
+        y += self.size/3
+        width = max(self.size-10, self.ctx.text_width(self.label))+10
+        height = self.size/3+8
+        if self.has_highlight:
+            self.ctx.rgb(*BLACK).move_to(x,y-height/2).round_rectangle(x-width/2,y-height/2,width,height,width//2).fill()
+            self.ctx.rgb(*GO_GREEN).move_to(x,y).text(self.label)
+        else:
+            self.ctx.rgb(*PUSH_RED).move_to(x,y-height/2).round_rectangle(x-width/2,y-height/2,width,height,width//2).fill()
+            self.ctx.rgb(*BLACK).move_to(x,y).text(self.label)
+
+class IconLabel(Icon):
+    def _draw(self,pos):
+        self.ctx.text_align = self.ctx.CENTER
+        self.ctx.text_baseline = self.ctx.MIDDLE
+        self.ctx.font_size=self.size/2
+
+        (x,y)=pos
+        hs = 5
+        width = self.ctx.text_width(self.label)+10
+        height = self.size/2
+        if self.has_highlight:
+            self.ctx.rgb(*GO_GREEN).move_to(x,y-height/2).round_rectangle(x-width/2,y-height/2,width,height,width//2).fill()
+            self.ctx.rgb(*BLACK).move_to(x,y).text(self.label)
+        else:
+            self.ctx.rgb(*PUSH_RED).move_to(x,y-height/2).round_rectangle(x-width/2,y-height/2,width,height,width//2).fill()
+            self.ctx.rgb(*BLACK).move_to(x,y).text(self.label)
 
 class IconFlower(Icon):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        self.petal_count= 0 #random.randint(4,8)
+        self.petal_count= random.randint(3,5)
         self.petal_color = (random.random(),random.random(),random.random())
         self.phi_offset = random.random()
         self.size_offset = random.randint(0,20)
@@ -127,12 +145,14 @@ class IconFlower(Icon):
     def _draw(self,pos):
         self.ctx.text_align = self.ctx.CENTER
         self.ctx.text_baseline = self.ctx.MIDDLE
+        self.ctx.font_size=self.size/3
+
         (x,y)=pos
         petal_size=0
         if self.petal_count:
             petal_size=2.3*self.size/self.petal_count+self.size_offset
-        self.ctx.font_size=self.size/3
-        hs = 3
+
+        hs = 5
             
         for i in range(self.petal_count):
             phi = math.pi*2 / self.petal_count * i + self.phi_offset
@@ -140,14 +160,22 @@ class IconFlower(Icon):
             (x_,y_) = xy_from_polar(r, phi)
             size_rnd = random.randint(-3,3)
             if self.has_highlight:
-                self.ctx.move_to(x+x_,y+y_).rgb(1,1,1).arc(x+x_,y+y_, petal_size/2+hs+size_rnd,-math.pi,math.pi,True).fill()
+                self.ctx.move_to(x+x_,y+y_).rgb(*GO_GREEN).arc(x+x_,y+y_, petal_size/2+hs+size_rnd,-math.pi,math.pi,True).fill()
             self.ctx.move_to(x+x_,y+y_).rgb(*self.petal_color).arc(x+x_,y+y_, petal_size/2+size_rnd,-math.pi,math.pi,True).fill()
         
-        #if self.has_highlight:
-        #    self.ctx.rgb(1,1,1).arc(x,y, self.size/2+hs,-math.pi,math.pi,True).fill()
+        if self.has_highlight:
+            self.ctx.rgb(*GO_GREEN).arc(x,y, self.size/2+hs,-math.pi,math.pi,True).fill()
         self.ctx.move_to(x,y).rgb(*self.bg).arc(x,y,self.size/2,-math.pi,math.pi,True).fill()
 
-        self.ctx.rgb(*WHITE).move_to(x,y).text(self.label)
+        y += self.size/3
+        width = max(self.size, self.ctx.text_width(self.label)+10)
+        height = self.size/3+8
+        if self.has_highlight:
+            self.ctx.rgb(*BLACK).move_to(x,y-height/2).round_rectangle(x-width/2,y-height/2,width,height,width//2).fill()
+            self.ctx.rgb(*GO_GREEN).move_to(x,y).text(self.label)
+        else:
+            self.ctx.rgb(*PUSH_RED).move_to(x,y-height/2).round_rectangle(x-width/2,y-height/2,width,height,width//2).fill()
+            self.ctx.rgb(*BLACK).move_to(x,y).text(self.label)
 
 
 
@@ -155,15 +183,25 @@ class IconValue(Icon):
     def __init__(self, value=0, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.value = value
+        self.get_value = None
     
     def _draw(self,pos):
         (x,y) = pos
 
+        v = self.value
+        if self.get_value:
+            v = self.get_value()
+            self.value = v
+
+        self.ctx.text_align = self.ctx.CENTER
+        self.ctx.text_baseline = self.ctx.MIDDLE
+        self.ctx.font_size=self.size/3
+
         if self.has_highlight:
-            self.ctx.move_to(x,y).rgb(*GO_GREEN).arc(x,y,self.size/2+5,-pi,pi,True).fill()
+            self.ctx.move_to(x,y).rgb(*WHITE).arc(x,y,self.size/2+5,-pi,pi,True).fill()
 
         self.ctx.move_to(x,y).rgb(*PUSH_RED).arc(x,y,self.size/2,-pi,pi,True).fill()
-        self.ctx.move_to(x,y).rgb(*GO_GREEN).arc(x,y,self.size/2-5,2*pi*self.value,0,1).fill()
+        self.ctx.move_to(x,y).rgb(*GO_GREEN).arc(x,y,self.size/2-5,v*2*pi,0,1).fill()
         self.ctx.rgb(0,0,0).move_to(x,y).text(self.label)
 
 
@@ -194,4 +232,5 @@ class GroupRing(UIElement):
 
     def _draw(self,pos):
         if self.element_center:
+            self.element_center.has_highlight = False
             self.element_center._draw(pos)
-- 
GitLab