mirror of https://git.sr.ht/~michalr/menu
Configurable color schemes, set Nord as an example
This commit is contained in:
parent
3b9e279db1
commit
0ecf532464
|
@ -31,5 +31,12 @@
|
||||||
"action_message": "Doing GET example"
|
"action_message": "Doing GET example"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bg_color": "lime"
|
"color_scheme": {
|
||||||
}
|
"menu_bg_color": "#4c566a",
|
||||||
|
"message_bg_color": "#eceff4",
|
||||||
|
"message_text_color": "#2e3440",
|
||||||
|
"btn_bg_color": "#d8dee9",
|
||||||
|
"btn_hover_color": "#eceff4",
|
||||||
|
"btn_text_color": "#2e3440"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
44
menu.py
44
menu.py
|
@ -7,7 +7,7 @@ from enum import Enum, auto
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
from typing import Tuple, Callable, TypedDict, List
|
from typing import Tuple, Callable, TypedDict, List, Dict
|
||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
import pygame.freetype
|
import pygame.freetype
|
||||||
|
@ -38,18 +38,19 @@ class Button:
|
||||||
PRESS_OFFSET = 2
|
PRESS_OFFSET = 2
|
||||||
HOVER_ANIMATION_STEPS = 5
|
HOVER_ANIMATION_STEPS = 5
|
||||||
|
|
||||||
def __init__(self, position: pygame.Rect, text: str, cbk: Callable[[], None]):
|
def __init__(self, position: pygame.Rect, text: str, color_scheme: Dict, cbk: Callable[[], None]):
|
||||||
self.position: pygame.Rect
|
self.position: pygame.Rect
|
||||||
self.set_position(position)
|
self.set_position(position)
|
||||||
self.text = text
|
self.text = text
|
||||||
|
self.color_scheme = color_scheme
|
||||||
self.cbk = cbk
|
self.cbk = cbk
|
||||||
self.font = pygame.freetype.SysFont(name="Sans", size=20)
|
self.font = pygame.freetype.SysFont(name="Sans", size=20)
|
||||||
text_rect = self.font.get_rect(self.text)
|
text_rect = self.font.get_rect(self.text)
|
||||||
self.text_pos = pygame.Rect((self.position.left + (self.position.width - text_rect.width)/2),
|
self.text_pos = pygame.Rect((self.position.left + (self.position.width - text_rect.width)/2),
|
||||||
(self.position.top + (self.position.height - text_rect.height)/2),
|
(self.position.top + (self.position.height - text_rect.height)/2),
|
||||||
text_rect.width, text_rect.height)
|
text_rect.width, text_rect.height)
|
||||||
self.bg_color = pygame.Color("black")
|
self.bg_color = pygame.Color(self.color_scheme["btn_bg_color"])
|
||||||
self.hover_bg_color = pygame.Color("violetred4")
|
self.hover_bg_color = pygame.Color(self.color_scheme["btn_hover_color"])
|
||||||
self.hover_animation_step = 0
|
self.hover_animation_step = 0
|
||||||
|
|
||||||
def get_bg_color(self):
|
def get_bg_color(self):
|
||||||
|
@ -87,7 +88,7 @@ class Button:
|
||||||
|
|
||||||
def draw(self, screen: pygame.Surface):
|
def draw(self, screen: pygame.Surface):
|
||||||
pygame.draw.rect(screen, self.get_bg_color(), self.get_position(), 0)
|
pygame.draw.rect(screen, self.get_bg_color(), self.get_position(), 0)
|
||||||
self.font.render_to(screen, self.get_text_pos(), self.text, fgcolor="pink")
|
self.font.render_to(screen, self.get_text_pos(), self.text, fgcolor=self.color_scheme["btn_text_color"])
|
||||||
|
|
||||||
def pos_is_inside(self, pos: Tuple) -> bool:
|
def pos_is_inside(self, pos: Tuple) -> bool:
|
||||||
return (self.position.left < pos[0] and (self.position.left + self.position.width) > pos[0]) and \
|
return (self.position.left < pos[0] and (self.position.left + self.position.width) > pos[0]) and \
|
||||||
|
@ -100,14 +101,14 @@ class Button:
|
||||||
|
|
||||||
|
|
||||||
class MessageBoard(IBoard):
|
class MessageBoard(IBoard):
|
||||||
def __init__(self, screen_rect: pygame.Rect, text: str, bg_color: str):
|
def __init__(self, screen_rect: pygame.Rect, text: str, color_scheme: Dict):
|
||||||
self.text = text
|
self.text = text
|
||||||
self.font = pygame.freetype.SysFont(name="Sans", size=30)
|
self.font = pygame.freetype.SysFont(name="Sans", size=30)
|
||||||
text_rect = self.font.get_rect(self.text)
|
text_rect = self.font.get_rect(self.text)
|
||||||
self.text_pos = pygame.Rect((screen_rect.left + (screen_rect.width - text_rect.width)/2),
|
self.text_pos = pygame.Rect((screen_rect.left + (screen_rect.width - text_rect.width)/2),
|
||||||
(screen_rect.top + (screen_rect.height - text_rect.height)/2),
|
(screen_rect.top + (screen_rect.height - text_rect.height)/2),
|
||||||
text_rect.width, text_rect.height)
|
text_rect.width, text_rect.height)
|
||||||
self.bg_color = bg_color
|
self.color_scheme = color_scheme
|
||||||
self.was_drawn_already = False
|
self.was_drawn_already = False
|
||||||
|
|
||||||
def handle_event(self, _):
|
def handle_event(self, _):
|
||||||
|
@ -115,23 +116,23 @@ class MessageBoard(IBoard):
|
||||||
|
|
||||||
def draw(self, screen: pygame.Surface, force_redraw: bool):
|
def draw(self, screen: pygame.Surface, force_redraw: bool):
|
||||||
if not self.was_drawn_already or force_redraw:
|
if not self.was_drawn_already or force_redraw:
|
||||||
screen.fill(self.bg_color)
|
screen.fill(self.color_scheme["message_bg_color"])
|
||||||
self.font.render_to(screen, self.text_pos, self.text, fgcolor="black")
|
self.font.render_to(screen, self.text_pos, self.text, fgcolor=self.color_scheme["message_text_color"])
|
||||||
self.was_drawn_already = True
|
self.was_drawn_already = True
|
||||||
|
|
||||||
|
|
||||||
class MenuBoard(IBoard):
|
class MenuBoard(IBoard):
|
||||||
BUTTON_MARGINS = 10
|
BUTTON_MARGINS = 10
|
||||||
|
|
||||||
def __init__(self, screen_rect: pygame.Rect, buttons: List[ButtonDef], bg_color: str):
|
def __init__(self, screen_rect: pygame.Rect, buttons: List[ButtonDef], color_scheme: Dict):
|
||||||
self.rows = math.floor(math.sqrt(len(buttons)))
|
self.rows = math.floor(math.sqrt(len(buttons)))
|
||||||
self.cols = math.ceil(math.sqrt(len(buttons)))
|
self.cols = math.ceil(math.sqrt(len(buttons)))
|
||||||
if (self.rows * self.cols) < len(buttons):
|
if (self.rows * self.cols) < len(buttons):
|
||||||
# extra row if buttons don't fit
|
# extra row if buttons don't fit
|
||||||
self.rows += 1
|
self.rows += 1
|
||||||
button_positions = self.generate_button_positions(screen_rect)
|
button_positions = self.generate_button_positions(screen_rect)
|
||||||
self.buttons = list(map(lambda d: Button(next(button_positions), d['text'], d['cbk']), buttons))
|
self.buttons = list(map(lambda d: Button(next(button_positions), d['text'], color_scheme, d['cbk']), buttons))
|
||||||
self.bg_color = bg_color
|
self.color_scheme = color_scheme
|
||||||
|
|
||||||
def generate_button_positions(self, screen_rect: pygame.Rect):
|
def generate_button_positions(self, screen_rect: pygame.Rect):
|
||||||
current_button_row = 0
|
current_button_row = 0
|
||||||
|
@ -153,7 +154,7 @@ class MenuBoard(IBoard):
|
||||||
|
|
||||||
def draw(self, screen: pygame.Surface, force_redraw: bool):
|
def draw(self, screen: pygame.Surface, force_redraw: bool):
|
||||||
if force_redraw or any(map(Button.needs_redrawing, self.buttons)):
|
if force_redraw or any(map(Button.needs_redrawing, self.buttons)):
|
||||||
screen.fill(self.bg_color)
|
screen.fill(self.color_scheme["menu_bg_color"])
|
||||||
for b in self.buttons:
|
for b in self.buttons:
|
||||||
b.draw(screen)
|
b.draw(screen)
|
||||||
|
|
||||||
|
@ -161,7 +162,7 @@ class MenuBoard(IBoard):
|
||||||
class App:
|
class App:
|
||||||
FPS = 30
|
FPS = 30
|
||||||
|
|
||||||
def __init__(self, urls: List[Action], bg_color: str, fullscreen: bool, screensaver_delay: int):
|
def __init__(self, urls: List[Action], color_scheme: Dict, fullscreen: bool, screensaver_delay: int):
|
||||||
pygame.init()
|
pygame.init()
|
||||||
info = pygame.display.Info()
|
info = pygame.display.Info()
|
||||||
flags = 0
|
flags = 0
|
||||||
|
@ -170,10 +171,10 @@ class App:
|
||||||
self.screen = pygame.display.set_mode((info.current_w, info.current_h), flags=flags)
|
self.screen = pygame.display.set_mode((info.current_w, info.current_h), flags=flags)
|
||||||
self.clock = pygame.time.Clock()
|
self.clock = pygame.time.Clock()
|
||||||
self.running = False
|
self.running = False
|
||||||
self.bg_color = bg_color
|
self.color_scheme = color_scheme
|
||||||
buttons = list(map(lambda u: ButtonDef(text=u['label'], cbk=self.button_press_handler(u)), urls))
|
buttons = list(map(lambda u: ButtonDef(text=u['label'], cbk=self.button_press_handler(u)), urls))
|
||||||
buttons.append(ButtonDef(text="Exit", cbk=self.quit))
|
buttons.append(ButtonDef(text="Exit", cbk=self.quit))
|
||||||
self.board: IBoard = MenuBoard(self.get_screen_rect(), buttons, bg_color)
|
self.board: IBoard = MenuBoard(self.get_screen_rect(), buttons, color_scheme)
|
||||||
self.task_q = Queue()
|
self.task_q = Queue()
|
||||||
self.screensaver = ClockScreensaver()
|
self.screensaver = ClockScreensaver()
|
||||||
self.TICKS_UNTIL_SCREENSAVER = screensaver_delay * self.FPS
|
self.TICKS_UNTIL_SCREENSAVER = screensaver_delay * self.FPS
|
||||||
|
@ -196,7 +197,8 @@ class App:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
def f(ex):
|
def f(ex):
|
||||||
def x():
|
def x():
|
||||||
self.board = MessageBoard(self.get_screen_rect(), f"Exception caught: {ex}", bg_color="red")
|
self.board = MessageBoard(self.get_screen_rect(), f"Exception caught: {ex}",
|
||||||
|
color_scheme=self.color_scheme)
|
||||||
return x
|
return x
|
||||||
self.task_q.put(f(e))
|
self.task_q.put(f(e))
|
||||||
sleep(5)
|
sleep(5)
|
||||||
|
@ -205,7 +207,7 @@ class App:
|
||||||
|
|
||||||
process_thr = Thread(target=thr_fun)
|
process_thr = Thread(target=thr_fun)
|
||||||
process_thr.start()
|
process_thr.start()
|
||||||
self.board = MessageBoard(self.get_screen_rect(), action['action_message'], bg_color=self.bg_color)
|
self.board = MessageBoard(self.get_screen_rect(), action['action_message'], color_scheme=self.color_scheme)
|
||||||
return impl
|
return impl
|
||||||
|
|
||||||
def get_handler(self, action: Action) -> Callable[[], None]:
|
def get_handler(self, action: Action) -> Callable[[], None]:
|
||||||
|
@ -222,7 +224,7 @@ class App:
|
||||||
|
|
||||||
process_thr = Thread(target=thr_fun)
|
process_thr = Thread(target=thr_fun)
|
||||||
process_thr.start()
|
process_thr.start()
|
||||||
self.board = MessageBoard(self.get_screen_rect(), action['action_message'], bg_color=self.bg_color)
|
self.board = MessageBoard(self.get_screen_rect(), action['action_message'], color_scheme=self.color_scheme)
|
||||||
return impl
|
return impl
|
||||||
|
|
||||||
def button_press_handler(self, action: Action) -> Callable[[], None]:
|
def button_press_handler(self, action: Action) -> Callable[[], None]:
|
||||||
|
@ -261,7 +263,7 @@ class App:
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
|
|
||||||
def quit(self):
|
def quit(self):
|
||||||
self.board = MessageBoard(self.get_screen_rect(), "Exiting...", bg_color=self.bg_color)
|
self.board = MessageBoard(self.get_screen_rect(), "Exiting...", color_scheme=self.color_scheme)
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
|
|
||||||
|
@ -288,6 +290,6 @@ if __name__ == '__main__':
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
url_defs = get_url_defs(data)
|
url_defs = get_url_defs(data)
|
||||||
|
|
||||||
app = App(url_defs, bg_color=data["bg_color"], fullscreen=not args.no_fullscreen,
|
app = App(url_defs, color_scheme=data["color_scheme"], fullscreen=not args.no_fullscreen,
|
||||||
screensaver_delay=args.screensaver_delay)
|
screensaver_delay=args.screensaver_delay)
|
||||||
app.loop()
|
app.loop()
|
||||||
|
|
Loading…
Reference in New Issue