--- /dev/null
+import pygame
+
+from ui.ui import Button, DrawImage, FPSWidget, Modal, Root
+
+
+class BookPaintMenu(Modal):
+ def __init__(self, root):
+ super().__init__(root)
+ size = self.surf.get_size()
+ root.children.extend(
+ (
+ Button(
+ root,
+ pygame.Rect((size[0] - 128, 0), (128, 96)),
+ "×",
+ root.quit,
+ ),
+ Button(
+ root,
+ pygame.Rect((size[0] - 256, 0), (128, 96)),
+ "_",
+ root.iconify,
+ ),
+ FPSWidget(root),
+ ),
+ )
+
+ def key_escape(self):
+ self.deactivate()
+
+ KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: key_escape}}
+
+ def draw(self):
+ self.root.surf.fill(0x333333)
+ super().draw()
+
+
+class BookPaint(Root):
+ BACKGROUND_COLOR = "black"
+
+ def __init__(self):
+ pygame.init()
+ super().__init__(
+ pygame.display.set_mode(flags=pygame.FULLSCREEN),
+ pygame.font.Font(None, size=96),
+ )
+ self.children.extend(
+ (
+ DrawImage(
+ self,
+ self.surf.get_rect(),
+ self.BACKGROUND_COLOR,
+ ),
+ FPSWidget(
+ self,
+ ),
+ )
+ )
+
+ def key_escape(self):
+ if isinstance(self.children[0], DrawImage):
+ BookPaintMenu(self)
+
+ def quit(self):
+ self.running = False
+
+ def iconify(self):
+ pygame.display.iconify()
+
+ KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: key_escape}}
--- /dev/null
+import pygame
+
+from ui.ui import Child
+
+
+class DrawImage(Child):
+ def __init__(self, root, rect, background_color=None):
+ super().__init__(root)
+ self.pos = rect.topleft
+ self.surface = pygame.Surface(rect.size, 0, 24)
+ if background_color is not None:
+ self.surface.fill(background_color)
+ self.last_pos = None
+ self.color = "white"
+ self.line = pygame.draw.line
+
+ def draw_line(self, pos):
+ if self.last_pos is not None:
+ self.line(self.surface, self.color, self.last_pos, pos)
+ else:
+ self.surface.set_at(pos, self.color)
+
+ def handle_mousebuttondown(self, ev):
+ self.draw_line(ev.pos)
+ self.last_pos = ev.pos
+ self.dirty = True
+
+ def handle_mousemotion(self, ev):
+ if self.last_pos is None:
+ return
+ self.draw_line(ev.pos)
+ self.last_pos = ev.pos
+ self.dirty = True
+
+ def handle_mousebuttonup(self, ev):
+ self.draw_line(ev.pos)
+ if self.last_pos is not None:
+ self.last_pos = None
+ self.dirty = True
+
+ def draw(self):
+ self.surf.blit(self.surface, self.pos)
def __init__(self):
self.children = []
- def handle_event(self, ev):
- super().handle_event(ev)
- for child in self.children:
- child.handle_event(ev)
+ def get_event_objects(self):
+ return (self, *self.children)
+
+ def handle_event_for_child(self, child, ev):
+ child.handle_event(ev)
+ return True
+
+ def call_event_handlers(self, ev):
+ for child in self.get_event_objects():
+ if not self.handle_event_for_child(child, ev):
+ break
def update(self):
for child in self.children:
pass
-class CursorParent(Parent):
- def __init__(self):
- super().__init__()
- self.cursor: Cursor | None = None
-
- def handle_event(self, ev):
- super().handle_event(ev)
- if self.cursor is not None:
- self.cursor.handle_event(ev)
-
- def update(self):
- super().update()
- if self.cursor is not None:
- self.cursor.update()
-
-
-class CursorChild(Child):
- @property
- def cursor(self):
- cursor = self.root.cursor
- if cursor is not None and cursor.text_input is self:
- return cursor
- return None
-
- @cursor.setter
- def cursor(self, value):
- self.root.cursor = value
-
-
-class Root(CursorParent):
+class Root(Parent):
BACKGROUND_COLOR: pygame.Color
def __init__(self, surf, font=None):
self.running = True
self.dirty = False
self.clock = pygame.time.Clock()
+ self.cursor: Cursor | None = None
+ self.stop_event = False
def handle_quit(self, _):
self.running = False
KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: key_escape}}
+ def get_event_objects(self):
+ return filter(None, (self, self.cursor, *self.children))
+
+ def handle_event_for_child(self, child, ev):
+ if child is not None:
+ child.handle_event(ev)
+ return self.running and not self.stop_event
+
def draw(self):
if hasattr(self, "BACKGROUND_COLOR"):
self.surf.fill(self.BACKGROUND_COLOR)
super().draw()
+ def update(self):
+ super().update()
+ if self.cursor is not None:
+ self.cursor.update()
+
def run(self):
while True:
for ev in pygame.event.get():
- self.handle_event(ev)
+ self.stop_event = False
+ self.call_event_handlers(ev)
if not self.running:
- return
+ break
+ if not self.running:
+ break
self.update()
if self.dirty:
self.draw()
def draw(self):
if not self.pushed:
value_color = "lime" if self.is_active else "gray"
- frame_color = value_color
+ colors = ("black", value_color, value_color)
else:
- pygame.draw.rect(self.surf, "darkgray", self.rect)
- frame_color = "lightgray"
- value_color = "black"
- fs = self.font.render(self.value, True, value_color)
- pygame.draw.rect(self.surf, frame_color, self.rect, 8)
+ colors = ("darkgray", "lightgray", "black")
+ pygame.draw.rect(self.surf, colors[0], self.rect)
+ pygame.draw.rect(self.surf, colors[1], self.rect, 8)
+ fs = self.font.render(self.value, True, colors[2])
fs_size = fs.get_size()
center = self.rect.center
- self.surf.blit(
- fs, (center[0] - fs_size[0] // 2, center[1] - fs_size[1] // 2)
- )
+ self.surf.blit(fs, (center[0] - fs_size[0] // 2, center[1] - fs_size[1] // 2))
def handle_mousebuttondown(self, ev):
if ev.button == 1 and self.rect.collidepoint(ev.pos):
}
-class TextInput(CursorChild):
+class TextInput(Child):
def __init__(self, root, rect, callback, value="", value_filter=None):
super().__init__(root)
self.rect = rect
self.value_filter = value_filter
self.offset = 0
+ @property
+ def cursor(self):
+ cursor = self.root.cursor
+ if cursor is not None and cursor.text_input is self:
+ return cursor
+ return None
+
+ @cursor.setter
+ def cursor(self, value):
+ self.root.cursor = value
+
def draw(self):
pygame.draw.rect(self.surf, "black", self.rect)
if self.cursor is not None:
self.children = root.children.copy()
root.children.clear()
root.children.append(self)
+ root.stop_event = True
self.dirty = True
def draw(self):