import displayio import terminalio from adafruit_display_shapes.rect import Rect from adafruit_display_text import label from collections import namedtuple from supervisor import ticks_ms from actions import Action import ticks class Group(object): def __init__(self, color, actions): # The color of the LED for the selected element self.color = color self.actions = actions self._selected = actions[0][0] @property def selected(self): return self._selected @selected.setter def selected(self, value): """Set the value, and switch the LED accordingly""" keys = [action[0] for action in self.actions] if value not in keys: return for keyId in keys: Action().macropad.pixels[keyId] = 0 Action().macropad.pixels[value] = int(self.color) self._selected = value class Configuration(object): def __init__(self, title): # Set the application available in the menu by default self.visible = True self.is_layout = True self.Action = namedtuple("Action", ("name", "callback", "color")) self.title = title self.keys = { 0 : None, 1 : None, 2 : None, 3 : None, 4 : None, 5 : None, 6 : None, 7 : None, 8 : None, 9 : None, 10 : None, 11 : None, 12 : None, 13 : None, } self.group = [] self.tick = None def registerKey(self, keyId, name, callback, color): if keyId > 13: return self.keys[keyId] = self.Action(name, callback, color) def registerGroup(self, group): self.group.append(group) def setTick(self, duration, callback): self.tick = (duration, callback) class Handler(object): def __init__(self, macropad, configuration): self.macropad = macropad self.group = displayio.Group() self.keys = { 0 : None, 1 : None, 2 : None, 3 : None, 4 : None, 5 : None, 6 : None, 7 : None, 8 : None, 9 : None, 10 : None, 11 : None, 12 : None, 13 : None, } self.colors = { 0 : None, 1 : None, 2 : None, 3 : None, 4 : None, 5 : None, 6 : None, 7 : None, 8 : None, 9 : None, 10 : None, 11 : None, } self.stack(configuration) def stack(self, configuration): """ Add a new configuration on top of an existing one """ self.group = displayio.Group() self.name = configuration.title self.options = configuration.group for index, value in configuration.keys.items(): if value is None: continue self.keys[index] = value.name, value.callback self.colors[index] = value.color for option in self.options: for action in option.actions: keyId = action[0] self.keys[keyId] = action[1], lambda macropad, key, pressed: None self.macropad.pixels[option.selected] = int(option.color) self.periodic_callback = None self.next_tick = None if configuration.tick is not None: self.periodic_callback = configuration.tick self._update_tick() self.macropad.pixels.show() def _update_tick(self): self.next_tick = ticks.add( ticks_ms(), self.periodic_callback[0] ) def _set_color(self, key, pressed): if pressed: self.macropad.pixels[key] = int(0x010101) else: color = self.colors[key] self.macropad.pixels[key] = int( self.colors[key] or 0 ) for option in self.options: self.macropad.pixels[option.selected] = int(option.color) def tick(self): "Execute asyncronous events" Action().collect_tasks() if self.next_tick is None or ticks.less(self.next_tick): return self.periodic_callback[1](self.macropad) self._update_tick() def start(self): self.position = self.macropad.encoder for key_index in range(12): x = key_index % 3 y = key_index // 3 self.group.append(label.Label(terminalio.FONT, text='', color=0xFFFFFF, anchored_position=((self.macropad.display.width - 1) * x / 2, self.macropad.display.height - 1 - (3 - y) * 12), anchor_point=(x / 2, 1.0))) self.group.append(Rect(0, 0, self.macropad.display.width, 12, fill=0xFFFFFF)) self.group.append(label.Label(terminalio.FONT, text='', color=0x000000, anchored_position=(self.macropad.display.width//2, -2), anchor_point=(0.5, 0.0))) self.macropad.display.show(self.group) self.group[13].text = self.name # Application name for i in range(12): if self.keys[i] is not None: self.group[i].text = self.keys[i][0] self.macropad.keyboard.release_all() self.macropad.consumer_control.release() self.macropad.mouse.release_all() self.macropad.stop_tone() self.macropad.pixels.fill((0, 0, 0)) self.macropad.pixels.show() self.macropad.display.refresh() def execute_action(self, key, pressed): for option in self.options: option.selected = key action = self.keys[key] # Do nothing if there is no action for this key if action is None: return # Update the leds if key < 12: if pressed: Action().flash(key, 0x010101) else: self._set_color(key, pressed) else: for key_idx in range(12): self._set_color(key_idx, pressed) self.macropad.pixels.show() result = action[1](self.macropad, key, pressed) # If we use the rotary encoder, mark all the actions as resolved. if key >= 12: action[1](self.macropad, key, False) for key_idx in range(12): self._set_color(key_idx, False) self.macropad.pixels.show() return result