From 6b0c186d5b20061a4e9c58d701b7f2c0055027bc Mon Sep 17 00:00:00 2001
From: moon2 <moon2protonmail@protonmail.com>
Date: Thu, 23 May 2024 14:23:05 +0200
Subject: [PATCH] tiny bugfixes, partial redraw for mixer + help

---
 python_payload/st3m/ui/elements/overlays.py |  34 +++--
 python_payload/st3m/ui/help.py              |  65 ++++----
 python_payload/st3m/ui/mixer.py             | 159 +++++++++++---------
 3 files changed, 151 insertions(+), 107 deletions(-)

diff --git a/python_payload/st3m/ui/elements/overlays.py b/python_payload/st3m/ui/elements/overlays.py
index b8efd73ae0..f277c4ee78 100644
--- a/python_payload/st3m/ui/elements/overlays.py
+++ b/python_payload/st3m/ui/elements/overlays.py
@@ -92,7 +92,7 @@ class Region:
         )
 
     def copy(self, rect):
-        self.set(rect.x0, rect.x1, rect.y0, rect.y1)
+        self.set(rect.x0, rect.y0, rect.x1, rect.y1)
 
     def __eq__(self, rect):
         return (
@@ -160,6 +160,8 @@ class Compositor(Responder):
             if kind == OverlayKind.SystemMenu:
                 if not self._system_menu.active:
                     continue
+            elif self._system_menu.sub and kind < OverlayKind.SystemMenu:
+                continue
             elif not self.enabled.get(kind, False):
                 continue
             if kind == OverlayKind.Indicators and not self.main.wants_icons():
@@ -266,6 +268,11 @@ class Compositor(Responder):
                     or redraw
                     or self._enabled[i] not in self._last_enabled
                 )
+            # catching partial redraws here
+            if self._system_menu.sub:
+                if not self._clip_rect.is_empty():
+                    self._system_menu.sub.full_redraw = True
+                self._clip_rect.add(-120, -120, 120, 120)
             for i in range(len(self._last_enabled)):
                 redraw = redraw or self._last_enabled[i] not in self._enabled
             self._last_enabled = self._enabled
@@ -276,12 +283,13 @@ class Compositor(Responder):
                 redraw = True
             if not redraw:
                 return
-            octx = sys_display.ctx(sys_display.osd)
-            octx.save()
-            octx.compositing_mode = octx.CLEAR
             self._last_clip.add_region(self._clip_rect)
-            self._last_clip.fill(octx)
-            octx.restore()
+            octx = sys_display.ctx(sys_display.osd)
+            if not self._system_menu.sub:
+                octx.save()
+                octx.compositing_mode = octx.CLEAR
+                self._last_clip.fill(octx)
+                octx.restore()
             for i in range(len(self._enabled)):
                 self._enabled[i].draw(octx)
             sys_display.update(octx)
@@ -471,7 +479,9 @@ class OverlaySystemMenu(Overlay):
 
     def needs_redraw(self, rect: Region) -> bool:
         if self.sub is not None:
-            rect.add(-120, -120, 120, 120)
+            pass
+            # adding it later in the pipeline for partial redraw reasons
+            # rect.add(-120, -120, 120, 120)
         else:
             rect.add(-44, -49, 44, 49)
         return True
@@ -551,8 +561,6 @@ class OverlayVolume(Overlay):
             return
         if self._showing is not None:
             self._showing -= delta_ms
-            if self._showing < 0:
-                self._showing = None
 
         if not self.override_os_button_volume:
             prs = (
@@ -594,7 +602,7 @@ class OverlayVolume(Overlay):
 
         # Foreground
         opacity = self._showing / 200
-        opacity = min(opacity, 1)
+        opacity = max(min(opacity, 1), 0)
 
         muted = self._muted
         if muted:
@@ -632,9 +640,11 @@ class OverlayVolume(Overlay):
         ctx.fill()
 
     def needs_redraw(self, rect: Region) -> bool:
-        if self._showing:
+        if self._showing is not None:
             rect.add(-40, -40, 40, 40)
-        return self._showing
+            if self._showing < 0:
+                self._showing = None
+        return self._showing is not None
 
 
 class Icon(Responder):
diff --git a/python_payload/st3m/ui/help.py b/python_payload/st3m/ui/help.py
index 777d88adeb..625c54e1bd 100644
--- a/python_payload/st3m/ui/help.py
+++ b/python_payload/st3m/ui/help.py
@@ -15,6 +15,7 @@ class Help(Responder):
         self.x = -self.line_width / 2
         self.y = -80
         self.lines = None
+        self.full_redraw = True
 
     def think(self, ins, delta_ms):
         if self.lines is None:
@@ -23,6 +24,8 @@ class Help(Responder):
         ud_dir -= self.input.buttons.app.left.pressed
         ud_dir += self.input.buttons.app.right.repeated
         ud_dir -= self.input.buttons.app.left.repeated
+        if ud_dir:
+            self.full_redraw = True
         self.y += -ud_dir * self.line_height * self.line_height_steps
         if self.y > -80:
             self.y = -80
@@ -31,32 +34,42 @@ class Help(Responder):
             self.y = min_y
 
     def draw(self, ctx):
-        ctx.rgb(0, 0, 0)
-        ctx.rectangle(-120, -120, 240, 240).fill()
-        ctx.rgb(0x81 / 255, 0xCD / 255, 0xC6 / 255)
-        ctx.move_to(0, self.y)
-        ctx.text_align = ctx.CENTER
-        if not (self.y < -120):
-            ctx.font_size = 24
-            ctx.text("~ help ~")
-        ctx.font_size = 16
-        offset = self.line_height
-        if not isinstance(self.help_text, str):
-            offset += self.line_height
-            ctx.move_to(0, self.y + offset)
-            ctx.text("no help found :/")
-            return
-        ctx.text_align = ctx.LEFT
-        if self.lines is None:
-            self.lines = wrap_text(self.help_text, self.line_width, ctx)
-        for line in self.lines:
-            offset += self.line_height
-            if (self.y + offset) < (-120):
-                continue
-            elif (self.y + offset) > (120 + self.line_height):
-                break
-            ctx.move_to(self.x, self.y + offset)
-            ctx.text(line)
+        if self.full_redraw:
+            ctx.rgb(0, 0, 0)
+            ctx.rectangle(-120, -120, 240, 240).fill()
+            ctx.rgb(0x81 / 255, 0xCD / 255, 0xC6 / 255)
+            ctx.move_to(0, self.y)
+            ctx.text_align = ctx.CENTER
+            if not (self.y < -120):
+                ctx.font_size = 24
+                ctx.text("~ help ~")
+            ctx.font_size = 16
+            offset = self.line_height
+            if not isinstance(self.help_text, str):
+                offset += self.line_height
+                ctx.move_to(0, self.y + offset)
+                ctx.text("no help found :/")
+                return
+            ctx.text_align = ctx.LEFT
+            if self.lines is None:
+                self.lines = wrap_text(self.help_text, self.line_width, ctx)
+            num_lines = len(self.lines)
+            x = 0
+            while x < num_lines:
+                line = self.lines[x]
+                offset += self.line_height
+                diff = -120 - self.y - offset
+                if diff > 0:
+                    jump = max(1, diff // self.line_height)
+                    offset += self.line_height * (jump - 1)
+                    x += jump
+                    continue
+                elif (self.y + offset) > (120 + self.line_height):
+                    break
+                if line:
+                    ctx.move_to(self.x, self.y + offset)
+                    ctx.text(line)
+                x += 1
 
     def on_exit(self):
         pass
diff --git a/python_payload/st3m/ui/mixer.py b/python_payload/st3m/ui/mixer.py
index aa32a7ac88..fdae6eb58f 100644
--- a/python_payload/st3m/ui/mixer.py
+++ b/python_payload/st3m/ui/mixer.py
@@ -5,10 +5,13 @@ import media
 
 
 def recall_blm_channel_stats(blm_chan):
+    print(blm_chan)
     if blm_chan.free:
+        print("wha-!")
         return
     if blm_chan.name in session_channel_vol_mute:
-        chan = bl00mboxChannel(SysChannel(blm_chan))
+        print("whee")
+        chan = bl00mboxChannel(bl00mbox.SysChannel(blm_chan))
         vol, mute = session_channel_vol_mute[chan.get_name()]
         if mute:
             vol = -math.inf
@@ -141,6 +144,7 @@ class HighlightColors:
 
 class AudioMixer(Responder):
     def __init__(self, inputcontroller):
+        self.full_redraw = True
         self.input = inputcontroller
         self._pos = 0
         self.chan_width = 46  # scaling factor between _pos and draw_pos
@@ -203,6 +207,7 @@ class AudioMixer(Responder):
         lr_dir -= self.input.buttons.app.left.pressed
         num_chans = len(self._chans)
         if self.input.buttons.app.middle.pressed:
+            self.full_redraw = True
             self._editing_channel += 1
             self._editing_channel %= 3
             if self._editing_channel == 1:
@@ -233,6 +238,8 @@ class AudioMixer(Responder):
             lr_dir -= self.input.buttons.app.left.repeated
             self._pos += lr_dir
             self._pos %= num_chans
+        if lr_dir:
+            self.full_redraw = True
         self.acc_ms += delta_ms
         if num_chans > 2:
             target = min(num_chans - 2, max(self._pos, 1))
@@ -243,21 +250,26 @@ class AudioMixer(Responder):
         target *= self.chan_width
 
         self.acc_ms += delta_ms
+        pos_prev = self._draw_pos[0]
         while self.acc_ms > 0:
             self.acc_ms -= 20
             self._draw_pos[2] = (target - self._draw_pos[0]) / 2
             self._draw_pos[2] -= self._draw_pos[1]
             self._draw_pos[1] += self._draw_pos[2] / 10
             self._draw_pos[0] += self._draw_pos[1] / 10
+        self._draw_pos[0] = round(self._draw_pos[0], 1)
+        if not self.full_redraw:
+            self.full_redraw = self._draw_pos[0] != pos_prev
 
     def draw(self, ctx):
-        ctx.rgb(0, 0, 0)
-        ctx.rectangle(-120, -120, 240, 240).fill()
         ctx.text_align = ctx.CENTER
         ctx.rgb(0x81 / 255, 0xCD / 255, 0xC6 / 255)
-        ctx.font_size = 24
-        ctx.move_to(0, -90)
-        ctx.text("~ mixer ~")
+        if self.full_redraw:
+            ctx.rgb(0, 0, 0)
+            ctx.rectangle(-120, -120, 240, 240).fill()
+            ctx.move_to(0, -90)
+            ctx.font_size = 24
+            ctx.text("~ mixer ~")
         for x, chan in enumerate(self._chans):
             xpos = x * self.chan_width - self._draw_pos[0]
             if abs(xpos) > 120 + self.chan_width / 2:
@@ -271,6 +283,8 @@ class AudioMixer(Responder):
             ctx.translate(xpos, (1 - highlight) * 2)
             self.draw_channel(colors, ctx, chan, highlight)
             ctx.restore()
+        if self.full_redraw:
+            self.full_redraw = False
 
     def draw_channel(self, colors, ctx, chan, highlight):
         ctx.text_align = ctx.RIGHT
@@ -278,61 +292,81 @@ class AudioMixer(Responder):
         chan_len = 160
         chan_upper = -80
         chan_width = self.chan_width - 4
-        ctx.round_rectangle(-chan_width / 2, chan_upper, chan_width, chan_len, 2).fill()
-
-        ctx.font_size = 16
-        # draw name
-        ctx.save()
-        ctx.rgb(*colors.name)
-        ctx.translate(0, 0)
-        ctx.rotate(-math.tau / 4)
-        ctx.move_to(-chan_upper - 4, -chan_width / 2 + 14)
-        name = chan.get_name()
-        while ctx.text_width(name) > 110:
-            name = name[:-1]
-        ctx.text(name)
-        ctx.restore()
-
-        # draw number of plugins (bl00mbox only)
-        num_plugins = chan.get_num_plugins()
-        if num_plugins is not None:
-            ctx.font_size = 12
-            ctx.rgb(*[0.7 * x for x in colors.bg])
-            ctx.move_to(chan_width / 2 - 3, chan_upper + 12)
-            ctx.text(str(num_plugins))
-
-        # volume
-        ctx.rgb(0, 0, 0)
-        vol = chan.get_vol_dB()
-        ctx.move_to(chan_width / 2 - 3, 75)
-        ctx.font_size = 14
-        ctx.text(f"{vol:.1f}")
-
-        ctx.rgb(0, 0, 0)
         len_bar = chan_len - 38
         bar_bottom = chan_upper + chan_len - 21
-        ctx.rectangle(chan_width / 2 - 13, bar_bottom - len_bar, 9, len_bar).fill()
-        ctx.move_to(chan_width / 2 - 13, bar_bottom - len_bar * 5 / 7)
-        ctx.rel_line_to(-5, 0).stroke()
-        # vol bar
-        ctx.rgb(*colors.vol_bar)
-        vol_bar_len = (vol + 50) / 70
-        vol_bar_len = min(1, max(0, vol_bar_len))
-        vol_bar_len *= len_bar
-        ctx.rectangle(
-            chan_width / 2 - 12, bar_bottom - vol_bar_len, 3, vol_bar_len
-        ).fill()
-        if self._editing_channel == 1 and highlight:
-            ctx.rgb(0, 1, 0)
-            ctx.move_to(6, bar_bottom - vol_bar_len)
-            ctx.rel_line_to(-5, -3)
-            ctx.rel_line_to(0, 6)
-            ctx.rel_line_to(5, -3)
-            ctx.fill()
+        if self.full_redraw:
+            ctx.round_rectangle(
+                -chan_width / 2, chan_upper, chan_width, chan_len, 2
+            ).fill()
 
+            # draw name
+            ctx.font_size = 16
+            ctx.save()
+            ctx.rgb(*colors.name)
+            ctx.translate(0, 0)
+            ctx.rotate(-math.tau / 4)
+            ctx.move_to(-chan_upper - 4, -chan_width / 2 + 14)
+            name = chan.get_name()
+            while ctx.text_width(name) > 110:
+                name = name[:-1]
+            ctx.text(name)
+            ctx.restore()
+
+            # draw number of plugins (bl00mbox only)
+            num_plugins = chan.get_num_plugins()
+            if num_plugins is not None:
+                ctx.font_size = 12
+                ctx.rgb(*[0.7 * x for x in colors.bg])
+                ctx.move_to(chan_width / 2 - 3, chan_upper + 12)
+                ctx.text(str(num_plugins))
+
+            # volume
+            vol = chan.get_vol_dB()
+            ctx.rgb(0, 0, 0)
+            ctx.move_to(chan_width / 2 - 3, 75)
+            ctx.font_size = 14
+            ctx.text(f"{vol:.1f}")
+
+            # mute
+            mute = int(chan.get_mute())
+            ctx.rgb(*(colors.mute_bg[mute]))
+            ctx.rectangle(-chan_width / 2 + 3, 41, 18, 18).fill()
+            ctx.text_align = ctx.CENTER
+            ctx.font_size = 16
+            ctx.move_to(-chan_width / 2 + 12, 55)
+            ctx.rgb(*(colors.mute_fg[mute]))
+            ctx.text("M")
+            if self._editing_channel == 2 and highlight:
+                ctx.rgb(0, 1, 0)
+                ctx.round_rectangle(-chan_width / 2 + 3, 41, 18, 18, 2).stroke()
+
+            ctx.rgb(0, 0, 0)
+            ctx.rectangle(chan_width / 2 - 13, bar_bottom - len_bar, 9, len_bar).fill()
+            ctx.move_to(chan_width / 2 - 13, bar_bottom - len_bar * 5 / 7)
+            ctx.rel_line_to(-5, 0).stroke()
+
+            # vol bar
+            ctx.rgb(*colors.vol_bar)
+            vol_bar_len = (vol + 50) / 70
+            vol_bar_len = min(1, max(0, vol_bar_len))
+            vol_bar_len *= len_bar
+            ctx.rectangle(
+                chan_width / 2 - 12, bar_bottom - vol_bar_len, 3, vol_bar_len
+            ).fill()
+
+            if self._editing_channel == 1 and highlight:
+                ctx.rgb(0, 1, 0)
+                ctx.move_to(6, bar_bottom - vol_bar_len)
+                ctx.rel_line_to(-5, -3)
+                ctx.rel_line_to(0, 6)
+                ctx.rel_line_to(5, -3)
+                ctx.fill()
+
+        ctx.rgb(0, 0, 0)
+        ctx.rectangle(chan_width / 2 - 9, bar_bottom - len_bar, 5, len_bar).fill()
         # rms bar
         # hmmmh do we reuse the 0dB notch for normalization...?
-        rms = chan.get_rms_dB() + 11
+        rms = chan.get_rms_dB() + 12
         ctx.rgb(*colors.rms_bar)
         rms_bar_len = (rms + 50) / 70
         rms_bar_len = min(1, max(0, rms_bar_len))
@@ -341,19 +375,6 @@ class AudioMixer(Responder):
             chan_width / 2 - 8, bar_bottom - rms_bar_len, 3, rms_bar_len
         ).fill()
 
-        # mute
-        mute = int(chan.get_mute())
-        ctx.rgb(*(colors.mute_bg[mute]))
-        ctx.rectangle(-chan_width / 2 + 3, 41, 18, 18).fill()
-        ctx.text_align = ctx.CENTER
-        ctx.font_size = 16
-        ctx.move_to(-chan_width / 2 + 12, 55)
-        ctx.rgb(*(colors.mute_fg[mute]))
-        ctx.text("M")
-        if self._editing_channel == 2 and highlight:
-            ctx.rgb(0, 1, 0)
-            ctx.round_rectangle(-chan_width / 2 + 3, 41, 18, 18, 2).stroke()
-
     def on_exit(self):
         for chan in self._chans:
             if isinstance(chan, bl00mboxChannel):
-- 
GitLab