diff --git a/menu.py b/menu.py index 035598a..3525c97 100644 --- a/menu.py +++ b/menu.py @@ -19,12 +19,14 @@ from screensavers import ClockScreensaver class ButtonDef(TypedDict): text: str cbk: Callable[[], None] + is_empty: bool class ActionType(Enum): - MSG = auto() # Shows a message for about 5 seconds - GET = auto() # Performs a HTTP GET - QUIT = auto() # Quits an application + MSG = auto() # Shows a message for about 5 seconds + GET = auto() # Performs a HTTP GET + QUIT = auto() # Quits an application + EMPTY = auto() # Shows nothing instead of a button class Action(TypedDict): @@ -56,7 +58,7 @@ class MultilineText: current_top += rect.top + self.VMARGIN -class Button: +class Button(IBoard): PRESS_OFFSET = 2 HOVER_ANIMATION_STEPS = 5 @@ -108,7 +110,7 @@ class Button: self.text_pos.width, self.text_pos.height) return self.text_pos - def draw(self, screen: pygame.Surface): + def draw(self, screen: pygame.Surface, _): pygame.draw.rect(screen, self.get_bg_color(), self.get_position(), 0) self.label.render_to(screen, self.get_text_pos(), self.theme["btn_text_color"]) @@ -147,13 +149,22 @@ class MenuBoard(IBoard): BUTTON_MARGINS = 10 def __init__(self, screen_rect: pygame.Rect, buttons: List[ButtonDef], theme: Dict): + def generate_buttons(button_positions_generator): + def impl(data: ButtonDef) -> Optional[IBoard]: + if data['is_empty']: + next(button_positions) + return None + else: + return Button(next(button_positions), data['text'], theme, data['cbk']) + return impl + self.rows = math.floor(math.sqrt(len(buttons))) self.cols = math.ceil(math.sqrt(len(buttons))) if (self.rows * self.cols) < len(buttons): # extra row if buttons don't fit self.rows += 1 button_positions = self.generate_button_positions(screen_rect) - self.buttons = list(map(lambda d: Button(next(button_positions), d['text'], theme, d['cbk']), buttons)) + self.buttons = list(filter(lambda x: x is not None, map(generate_buttons(button_positions), buttons))) self.theme = theme def generate_button_positions(self, screen_rect: pygame.Rect): @@ -178,7 +189,7 @@ class MenuBoard(IBoard): if force_redraw or any(map(Button.needs_redrawing, self.buttons)): screen.fill(self.theme["menu_bg_color"]) for b in self.buttons: - b.draw(screen) + b.draw(screen, force_redraw) class App: @@ -197,7 +208,10 @@ class App: self.clock = pygame.time.Clock() self.running = False self.theme = theme - buttons = list(map(lambda u: ButtonDef(text=u['label'], cbk=self.button_press_handler(u)), actions)) + buttons = list(map(lambda u: ButtonDef(text=u['label'], + cbk=self.button_press_handler(u), + is_empty=(u['action_type'] == ActionType.EMPTY)), + actions)) self.board: IBoard = MenuBoard(self.get_screen_rect(), buttons, theme) self.task_q = Queue() self.screensaver = ClockScreensaver() @@ -261,6 +275,8 @@ class App: return self.get_handler(action) elif action['action_type'] == ActionType.QUIT: return self.quit + elif action['action_type'] == ActionType.EMPTY: + return lambda: print("Tried to do an action on EMPTY - shouldn't happen") raise NotImplementedError(action['action_type']) def loop(self): @@ -307,7 +323,7 @@ class App: def get_url_defs(config_data: dict) -> List[Action]: url_defs = [] for d in data['actions']: - url_defs.append(Action(label=d['label'], + url_defs.append(Action(label=d.get('label'), action_type=ActionType[d['action_type']], action_param=d.get('action_param'), action_message=str(d.get('action_message'))))