From: mar77i Date: Sun, 21 Sep 2025 18:49:40 +0000 (+0200) Subject: finish layout configurator. X-Git-Url: https://git.mar77i.info/?a=commitdiff_plain;h=c2c3150d33fe389b541949315aec6634016fec63;p=zenbook_gui finish layout configurator. --- diff --git a/keyboard/clock.py b/keyboard/clock.py index 60a5ac8..a80cdbc 100644 --- a/keyboard/clock.py +++ b/keyboard/clock.py @@ -25,14 +25,12 @@ class ClockWidget(Child): def __init__(self, parent, rect): super().__init__(parent) self.rect = rect - min_rect_size = min(rect.size) - self.radius = min_rect_size * self.CLOCK_RADIUS - self.widths = tuple(w * min_rect_size for w in self.WIDTHS) + self.widths = tuple(w * min(rect.size) for w in self.WIDTHS) self.state = (0, 0, 0, 0, 0, 0) def update(self): now = datetime.now() - state = ( + self.state = ( now.strftime("%b"), now.day, now.hour, @@ -40,16 +38,14 @@ class ClockWidget(Child): now.second, now.microsecond, ) - if state != self.state: - self.state = state - self.dirty = True + self.dirty = True def draw(self): month, day, hour, minute, second, microsecond = self.state second = second + microsecond / 1000000 fractional_minute = minute + second / 60 fractional_hour = hour + fractional_minute / 60 - self.draw_markings() + self.draw_markings(self.surf, self.rect) self.draw_date(month, "gold") self.draw_date(str(day), "gold", True) self.draw_hand_polygon( @@ -65,36 +61,41 @@ class ClockWidget(Child): ) self.draw_hand_polygon(second * tau / 60, self.HANDS[3], self.widths[3], "blue") - def draw_markings(self): - center = self.rect.center - radius = self.radius - markings = tuple(m * radius for m in self.MARKINGS) - for i in range(12): - angle = tau * i / 12 + @classmethod + def get_symbolic_surf(cls, size): + surf = pygame.Surface(size, pygame.SRCALPHA) + surf.fill(0x7F000000) + rect = surf.get_rect() + pygame.draw.circle( + surf, "black", rect.center, min(surf.get_size()) * cls.CLOCK_RADIUS + ) + cls.draw_markings(surf, rect) + return surf + + @classmethod + def draw_markings(cls, surf, rect): + center = rect.center + radius = min(rect.size) * cls.CLOCK_RADIUS + for i in range(60): + angle = tau * i / 60 fractions = cos(angle), -sin(angle) pygame.draw.line( - self.surf, - "green", - tuple(c + (f * markings[0]) for c, f in zip(center, fractions)), - tuple(c + (f * radius) for c, f in zip(center, fractions)), + surf, + ("green", "yellow")[i % 5 != 0], + *( + tuple(c + (f * radius * value) for c, f in zip(center, fractions)) + for value in (cls.MARKINGS[i % 5 != 0], 1) + ), ) - for j in range(i * 5 + 1, (i + 1) * 5): - angle = tau * j / 60 - fractions = cos(angle), -sin(angle) - pygame.draw.line( - self.surf, - "yellow", - tuple(c + (f * markings[1]) for c, f in zip(center, fractions)), - tuple(c + (f * radius) for c, f in zip(center, fractions)), - ) - pygame.draw.circle(self.surf, "green", center, radius, 1) + pygame.draw.circle(surf, "green", center, radius, 1) def draw_date(self, string, color, right=False): fs = self.font.render(string, True, color) + x_offset = self.MARKINGS[0] * min(self.rect.size) * self.CLOCK_RADIUS if right: - x_offset = self.MARKINGS[0] * self.radius - fs.get_width() + x_offset -= fs.get_width() else: - x_offset = -self.MARKINGS[0] * self.radius + x_offset *= -1 self.surf.blit( fs, ( @@ -106,21 +107,22 @@ class ClockWidget(Child): def draw_hand_polygon(self, angle, interval, width, color): # fix origin and direction angle = tau / 4 - angle - center = self.rect.center - radius = self.radius - fractions = cos(angle), -sin(angle) - points = ( - tuple(c + (f * interval[0] * radius) for c, f in zip(center, fractions)), - tuple(c + (f * interval[1] * radius) for c, f in zip(center, fractions)), + points = tuple( + tuple( + c + (f * value * min(self.rect.size) * self.CLOCK_RADIUS) + for c, f in zip(self.rect.center, (cos(angle), -sin(angle))) + ) + for value in interval ) offset = (cos(angle + tau / 4) * width / 2, -sin(angle + tau / 4) * width / 2) pygame.draw.polygon( self.surf, color, [ - (points[0][0] - offset[0], points[0][1] - offset[1]), - (points[0][0] + offset[0], points[0][1] + offset[1]), - (points[1][0] + offset[0], points[1][1] + offset[1]), - (points[1][0] - offset[0], points[1][1] - offset[1]), + ( + points[i // 2][0] + offset[0] * sign, + points[i // 2][1] + offset[1] * sign, + ) + for i, sign in zip(range(4), (-1, 1, 1, -1)) ], ) diff --git a/keyboard/draggable.py b/keyboard/draggable.py index 462aec5..c3d76d4 100644 --- a/keyboard/draggable.py +++ b/keyboard/draggable.py @@ -18,28 +18,29 @@ class DraggableButton(Child): self.drag_rel = None def draw(self): - if not self.pushed: - value_color = "lime" if self.highlight else "gray" - colors = ("black", value_color, value_color) - else: - colors = ("darkgray", "lightgray", "black") - pygame.draw.rect(self.surf, colors[0], self.rect) - pygame.draw.rect(self.surf, colors[1], self.rect, 8) - self.surf.blit(self.surface, self.rect.topleft) - if self.drag_pos is not None: - for dest in self.dests: - width = 6 - if dest.collidepoint(self.drag_pos): - dest = dest.inflate((128, 128)) - width *= 2 - pygame.draw.rect(self.surf, "red", dest, width) - self.surf.blit( - self.surface, - ( - self.drag_pos[0] - self.drag_rel[0], - self.drag_pos[1] - self.drag_rel[1], - ), - ) + if self.drag_pos is None: + self.surf.blit(self.surface, self.rect.topleft) + + def draw_post(self): + if self.drag_pos is None: + return + for dest in self.dests: + width = 6 + if isinstance(dest, tuple): + test_cb, dest = dest + else: + test_cb = dest.collidepoint + if test_cb(self.drag_pos): + dest = dest.inflate((128, 128)) + width *= 2 + pygame.draw.rect(self.surf, "red", dest, width) + self.surf.blit( + self.surface, + ( + self.drag_pos[0] - self.drag_rel[0], + self.drag_pos[1] - self.drag_rel[1], + ), + ) def toggle(self, pos=None, pushed=None): if pos is None: @@ -72,9 +73,13 @@ class DraggableButton(Child): def drop(self, pos): for i, dest in enumerate(self.dests): - if dest.collidepoint(pos): + if isinstance(dest, tuple): + test_cb, dest = dest + else: + test_cb = dest.collidepoint + if test_cb(pos): self.rect, self.dests[i] = dest, self.rect - self.callback() + self.callback(i) break def handle_fingerdown(self, event): diff --git a/keyboard/keyboard.py b/keyboard/keyboard.py index 26e77c6..c7a7122 100644 --- a/keyboard/keyboard.py +++ b/keyboard/keyboard.py @@ -32,7 +32,7 @@ class Root(BaseRoot): self.mouse_fingers = {} self.display = self.keyboard._display self.led_mask = self.display.get_keyboard_control().led_mask - FPSWidget(self) + self.fps = FPSWidget(self) size = self.surf.get_size() self.menu_modal = MenuModal( self, @@ -41,6 +41,10 @@ class Root(BaseRoot): self.keys_index = len(self.children) self.setup_widgets() + def draw(self): + super().draw() + self.fps.draw() + def setup_widgets(self): while len(self.children) > self.keys_index: self.children.pop() diff --git a/keyboard/layout.py b/keyboard/layout.py index 92936a1..73c1bc4 100644 --- a/keyboard/layout.py +++ b/keyboard/layout.py @@ -1,6 +1,9 @@ +from collections import OrderedDict +from functools import partial + import pygame -from ui import Child, QuittableModal, Rect +from ui import Child, LabelledRect, QuittableModal, Rect from .clock import ClockWidget from .draggable import DraggableButton @@ -9,142 +12,75 @@ from .mousepad import MousePadWidget from .touchbutton import TouchButton from .key import KEYBOARD, KEYPAD_KEYS, CURSOR_KEYS +PADDING = 8 - -class LayoutWidget(Child): - padding = 8 - - @classmethod - def get_keyboard_keys(cls, surf, size, keys): - result = [] - x_sums = [0] - y_spans = [] - for key in keys: - if key == "\n": - x_sums.append(0) - apply_y_spans(x_sums, y_spans) - elif len(key) > 2: - width = key[2].get("x_hint", 1) - x_sums[-1] += width - y_hint = key[2].get("y_hint", 1) - if y_hint > 1: - y_spans.append([width, y_hint - 1]) - else: - x_sums[-1] += 1 - apply_y_spans(x_sums, y_spans) - assert all(item[1] <= 0 for item in y_spans) - x, y = (0, 0) - x_sums_iter = iter(x_sums) - x_sums_len = len(x_sums) - key_size = get_key_size(size, x_sums_iter, x_sums_len) - for key in keys: - if key == "\n": - x, y = 0, y + key_size[1] - key_size = get_key_size(size, x_sums_iter, x_sums_len) - continue - key_width, key_height = key_size - if key != (None, None): - if len(key) > 2: - key_width *= key[2].get("x_hint", 1) - key_height *= key[2].get("y_hint", 1) - rect = pygame.Rect( - (x + cls.padding / 2, y + cls.padding / 2), - (key_width - cls.padding, key_height - cls.padding), - ) - pygame.draw.rect(surf, "gray", rect, 1) - pygame.draw.ellipse( - surf, - "0x333333", - pygame.Rect( - ( - rect.centerx - key_size[0] / 6, - rect.centery - key_size[1] / 4, - ), - (key_size[0] / 3, key_size[1] / 2), - ) - ) - x += key_width - return result - - def get_widget_row_rects(self, num_widgets): - if self.parent.keyboard_row == 0: - top = self.rect.centery +def get_symbolic_keys(surf, size, keys): + result = [] + x_sums = [0] + y_spans = [] + for key in keys: + if key == "\n": + x_sums.append(0) + apply_y_spans(x_sums, y_spans) + elif len(key) > 2: + width = key[2].get("x_hint", 1) + x_sums[-1] += width + y_hint = key[2].get("y_hint", 1) + if y_hint > 1: + y_spans.append([width, y_hint - 1]) else: - top = self.rect.top - size = (self.rect.width / 3, self.rect.height / 2) - rects = [] - if num_widgets == 1: - rects.append( - pygame.Rect( - (self.rect.left + size[0], top), - size, - ) - ) - elif num_widgets == 2: - rects.append( - pygame.Rect( - (self.rect.left + self.rect.width / 4 - size[0] / 2, top), - size, - ) + x_sums[-1] += 1 + apply_y_spans(x_sums, y_spans) + assert all(item[1] <= 0 for item in y_spans) + x, y = (0, 0) + x_sums_iter = iter(x_sums) + x_sums_len = len(x_sums) + key_size = get_key_size(size, x_sums_iter, x_sums_len) + for key in keys: + if key == "\n": + x, y = 0, y + key_size[1] + key_size = get_key_size(size, x_sums_iter, x_sums_len) + continue + key_width, key_height = key_size + if key != (None, None): + if len(key) > 2: + key_width *= key[2].get("x_hint", 1) + key_height *= key[2].get("y_hint", 1) + rect = pygame.Rect( + (x + PADDING / 2, y + PADDING / 2), + (key_width - PADDING, key_height - PADDING), ) - rects.append( + pygame.draw.rect(surf, "black", rect) + pygame.draw.rect(surf, "gray", rect, 1) + pygame.draw.ellipse( + surf, + "0x333333", pygame.Rect( - (self.rect.left + self.rect.width * 3 / 4 - size[0] / 2, top), - size, - ) + ( + rect.centerx - key_size[0] / 6, + rect.centery - key_size[1] / 4, + ), + (key_size[0] / 3, key_size[1] / 2), + ), ) - elif num_widgets == 3: - for i in range(3): - rects.append( - pygame.Rect( - (self.rect.left + i * size[0], top), - size, - ) - ) - return rects - - def get_widget_row_rect_dests(self, len_widget_row, keyset, source_rect): - if keyset in self.parent.widget_row: - keypad_dests = self.get_widget_row_rects(len_widget_row) - keypad_idx = self.parent.widget_row.index(KEYPAD_KEYS) - keypad_dests[keypad_idx], source_rect = source_rect, keypad_dests[keypad_idx] - elif len_widget_row < 3: - keypad_dests = self.get_widget_row_rects(len_widget_row + 1) - else: - keypad_dests = [] - return source_rect, keypad_dests + x += key_width + return result + +class ScreenWidget(Child): def __init__(self, parent, rect): super().__init__(parent) self.rect = rect - keyboard_size = (rect.width, rect.height / 2) - self.keyboard_rects = [ - pygame.Rect(rect.topleft, keyboard_size), - pygame.Rect((rect.left, rect.centery), keyboard_size), - ] - widget_size = (rect.width / 3, keyboard_size[1]) - self.widget_rects = [ - [ - pygame.Rect( - (rect.left + rect.width / 4 - widget_size[0] / 2, rect.top), - widget_size, - ), - pygame.Rect( - (rect.left + rect.width * 3 / 4 - widget_size[0] / 2, rect.top), - widget_size, - ) - ], - [ - pygame.Rect((rect.left, rect.top), widget_size), - pygame.Rect((rect.left + widget_size[0], rect.top), widget_size), - pygame.Rect((rect.left + 2 * widget_size[1], rect.top), widget_size), - ], - ] - surf = pygame.Surface(keyboard_size) - surf.fill("black") - self.get_keyboard_keys(surf, keyboard_size, KEYBOARD) - self.keyboard = DraggableButton( + self.keyboard_rects = parent.keyboard_rects + self.keyboard = self.get_keyboard_button() + + def get_keyboard_button(self): + keyboard_size = self.keyboard_rects[0].size + surf = pygame.Surface(keyboard_size, pygame.SRCALPHA) + surf.fill(0x7F000000) + get_symbolic_keys(surf, keyboard_size, KEYBOARD) + return DraggableButton( self.parent, self.keyboard_rects[self.parent.keyboard_row], surf, @@ -157,60 +93,252 @@ class LayoutWidget(Child): self.keyboard.dests.clear() self.keyboard.dests.append(self.keyboard_rects[not self.parent.keyboard_row]) - def keyboard_callback(self): + def keyboard_callback(self, _): self.parent.keyboard_row = int( (self.keyboard.rect.top - self.rect.top) * 2 / self.rect.height ) - # move the visual widget row out of the way here - - def keypad_callback(self): - if KEYPAD_KEYS in self.parent.widget_row: - self.parent.widget_row.remove(KEYPAD_KEYS) - if self.keypad.rect != self.keypad_rect: - if len(self.parent.widget_row) == 0: - self.parent.widget_row.append(KEYPAD_KEYS) - return - # insert KEYPAD_KEYS to widget_row at the right index - # rearrange the visual widget row + self.parent.update_draggable_buttons() def draw(self): pygame.draw.rect(self.surf, "gray", self.rect.inflate((-2, -2)), 1) class LayoutModal(QuittableModal): - WIDGETS = { - "KEYPAD_KEYS": KEYPAD_KEYS, - "CURSOR_KEYS": CURSOR_KEYS, - "MousePadWidget": MousePadWidget, - "ClockWidget": ClockWidget, - } - - def __init__(self, parent, config=None): + WIDGETS = OrderedDict( + ( + ("KEYPAD_KEYS", KEYPAD_KEYS), + ("CURSOR_KEYS", CURSOR_KEYS), + ("MousePadWidget", MousePadWidget), + ("ClockWidget", ClockWidget), + ) + ) + LABELS = OrderedDict( + ( + (id(KEYPAD_KEYS), "Keypad keys"), + (id(CURSOR_KEYS), "Arrow keys"), + (id(MousePadWidget), "Mouse pad"), + (id(ClockWidget), "Clock"), + ) + ) + + def __init__(self, parent): super().__init__(parent) size = self.surf.get_size() self.rect = pygame.Rect(150, 150, size[0] - 300, size[1] - 300) Rect(self, self.rect, "black", "gray") + self.keyboard_row = 0 + self.widget_row = [] + + half_size = (size[0] / 2, size[1] / 2) + screen_rect = pygame.Rect((half_size[0] / 2, half_size[1] / 2), half_size) TouchButton( self, - pygame.Rect((self.rect.centerx - 128, self.rect.bottom - 128), (256, 128)), + pygame.Rect( + ( + self.rect.centerx - 128, + (self.rect.bottom + screen_rect.bottom) / 2 - 64, + ), + (256, 128), + ), "Close", self.deactivate, ) - self.keyboard_row = 0 - self.widget_row = [] - half_size = (size[0] / 2, size[1] / 2) - self.layout_widget = LayoutWidget( - self, pygame.Rect((half_size[0] / 2, half_size[1] / 2), half_size) + self.widget_size = (screen_rect.width / 3, screen_rect.height / 2) + left = ( + self.rect.left + + (screen_rect.left - self.rect.left - self.widget_size[0]) / 2 + ) + right = ( + screen_rect.right + + (self.rect.right - screen_rect.right - self.widget_size[0]) / 2 + ) + spacing = (self.rect.height - self.widget_size[1] * 2) / 3 + self.home_rects = ( + pygame.Rect((left, self.rect.top + spacing), self.widget_size), + pygame.Rect( + (left, self.rect.top + self.widget_size[1] + 2 * spacing), + self.widget_size, + ), + pygame.Rect((right, self.rect.top + spacing), self.widget_size), + pygame.Rect( + (right, self.rect.top + self.widget_size[1] + 2 * spacing), + self.widget_size, + ), + ) + self.keyboard_rects = ( + pygame.Rect( + screen_rect.topleft, (screen_rect.width, screen_rect.height / 2) + ), + pygame.Rect( + (screen_rect.left, screen_rect.centery), + (screen_rect.width, screen_rect.height / 2), + ), + ) + self._screen_widget_rects = tuple( + ( + pygame.Rect( + ( + screen_rect.left + screen_rect.width / 12, + top, + ), + self.widget_size, + ), + pygame.Rect( + ( + screen_rect.left + screen_rect.width * 7 / 12, + top, + ), + self.widget_size, + ), + pygame.Rect((screen_rect.left, top), self.widget_size), + pygame.Rect( + (screen_rect.left + self.widget_size[0], top), + self.widget_size, + ), + pygame.Rect( + (screen_rect.left + 2 * self.widget_size[0], top), + self.widget_size, + ), + ) + for top in (screen_rect.centery, screen_rect.top) ) + for rect, widget in zip(self.home_rects, self.WIDGETS.values()): + LabelledRect(self, rect, self.LABELS[id(widget)], "orange", None, "gray") + self.screen_widget = ScreenWidget(self, screen_rect) + self.draggable_buttons = self.get_draggable_buttons() + for draggable_button in self.draggable_buttons: + draggable_button.callback = partial( + self.draggable_callback, draggable_button + ) + + @property + def screen_widget_rects(self): + return self._screen_widget_rects[self.keyboard_row] + + @staticmethod + def get_key_surf(size, keyset): + surf = pygame.Surface(size, pygame.SRCALPHA) + surf.fill(0x7F000000) + get_symbolic_keys(surf, size, keyset) + return surf + + def get_draggable_buttons(self): + widget_size = self.home_rects[0].size + keypad_keys = DraggableButton( + self, + self.home_rects[0], + self.get_key_surf(widget_size, KEYPAD_KEYS), + [], + None, + ) + cursor_keys = DraggableButton( + self, + self.home_rects[1], + self.get_key_surf(widget_size, CURSOR_KEYS), + [], + None, + ) + mousepad_widget = DraggableButton( + self, + self.home_rects[2], + MousePadWidget.get_symbolic_surf(widget_size), + [], + None, + ) + clock_widget = DraggableButton( + self, + self.home_rects[3], + ClockWidget.get_symbolic_surf(widget_size), + [], + None, + ) + return keypad_keys, cursor_keys, mousepad_widget, clock_widget def activate(self): super().activate() - self.layout_widget.activate() + self.screen_widget.activate() + self.update_draggable_buttons() + + def drops_to_home_rect(self, pos): + return not self.screen_widget.rect.collidepoint(pos) + + def update_draggable_buttons_zero(self): + for i, draggable_button in enumerate(self.draggable_buttons): + draggable_button.dests.clear() + draggable_button.rect = self.home_rects[i] + draggable_button.dests.append(self.screen_widget_rects[3]) + + def update_draggable_buttons_one(self): + for (i, draggable_button), widget in zip( + enumerate(self.draggable_buttons), self.WIDGETS.values() + ): + draggable_button.dests.clear() + if widget in self.widget_row: + draggable_button.rect = self.screen_widget_rects[3] + draggable_button.dests.append( + (self.drops_to_home_rect, self.home_rects[i]) + ) + else: + draggable_button.rect = self.home_rects[i] + draggable_button.dests.extend( + self.screen_widget_rects[i] for i in range(2) + ) + + def update_draggable_buttons_two(self): + for (i, draggable_button), widget in zip( + enumerate(self.draggable_buttons), self.WIDGETS.values() + ): + draggable_button.dests.clear() + if widget in self.widget_row: + draggable_button.dests.append( + (self.drops_to_home_rect, self.home_rects[i]) + ) + index = self.widget_row.index(widget) + draggable_button.rect = self.screen_widget_rects[index] + draggable_button.dests.extend( + self.screen_widget_rects[i] for i in range(2) if i != index + ) + else: + draggable_button.rect = self.home_rects[i] + draggable_button.dests.extend( + self.screen_widget_rects[2 + i] for i in range(3) + ) + + def update_draggable_buttons_three(self): + for (i, draggable_button), widget in zip( + enumerate(self.draggable_buttons), self.WIDGETS.values() + ): + draggable_button.dests.clear() + if widget in self.widget_row: + draggable_button.dests.append( + (self.drops_to_home_rect, self.home_rects[i]) + ) + index = self.widget_row.index(widget) + draggable_button.rect = self.screen_widget_rects[2 + index] + draggable_button.dests.extend( + self.screen_widget_rects[2 + i] for i in range(3) if i != index + ) + else: + draggable_button.rect = self.home_rects[i] + + def update_draggable_buttons(self): + ( + self.update_draggable_buttons_zero, + self.update_draggable_buttons_one, + self.update_draggable_buttons_two, + self.update_draggable_buttons_three, + )[len(self.widget_row)]() def deactivate(self): self.root.setup_widgets() super().deactivate() + def draw(self): + super().draw() + for child in self.children: + if hasattr(child, "draw_post"): + child.draw_post() + def apply_config(self, data): if "keyboard_row" in data: self.keyboard_row = data["keyboard_row"] @@ -220,20 +348,62 @@ class LayoutModal(QuittableModal): w for w in (self.WIDGETS.get(key) for key in data["widget_row"]) if w ) - @classmethod - def stringify_widgets(cls, widget_row): - result = [None] * len(widget_row) - for key, value in cls.WIDGETS.items(): - try: - index = widget_row.index(value) - except ValueError: - continue - else: - result[index] = key - return result - def get_config(self): + names_per_widget_ids = {id(value): key for key, value in self.WIDGETS.items()} return { "keyboard_row": self.keyboard_row, - "widget_row": self.stringify_widgets(self.widget_row), + "widget_row": [ + names_per_widget_ids[id(widget)] for widget in self.widget_row + ], } + + def draggable_callback(self, draggable_button, replaced_dest): + # update widget_row based on the completed drag and drop action + old_rect = draggable_button.dests[replaced_dest] + new_rect = draggable_button.rect + widget = tuple(self.WIDGETS.values())[ + self.draggable_buttons.index(draggable_button) + ] + len_widget_row = len(self.widget_row) + if old_rect in self.home_rects: + screen_rects = self.screen_widget_rects + # we inserted the draggable button to the screen rect + if len_widget_row == 0: + self.widget_row.append(widget) + elif len_widget_row == 1: + if new_rect == screen_rects[0]: + self.widget_row.insert(0, widget) + elif new_rect == screen_rects[1]: + self.widget_row.append(widget) + elif len_widget_row == 2: + if new_rect == screen_rects[2]: + self.widget_row.insert(0, widget) + elif new_rect == screen_rects[3]: + self.widget_row.insert(1, widget) + elif new_rect == screen_rects[4]: + self.widget_row.append(widget) + elif new_rect in self.home_rects: + self.widget_row.remove(widget) + elif len_widget_row == 2: + new_row = (self.widget_row[1], self.widget_row[0]) + self.widget_row.clear() + self.widget_row.extend(new_row) + elif len_widget_row == 3: + self.swap_draggable_three({new_rect.topleft, old_rect.topleft}) + self.update_draggable_buttons() + + def swap_draggable_three(self, swapped): + # reorder widget_row based on the two known indices + screen_rects = self.screen_widget_rects + if swapped == {screen_rects[2].topleft, screen_rects[3].topleft}: + new_row = (self.widget_row[1], self.widget_row[0], self.widget_row[2]) + self.widget_row.clear() + self.widget_row.extend(new_row) + elif swapped == {screen_rects[2].topleft, screen_rects[4].topleft}: + new_row = (self.widget_row[2], self.widget_row[1], self.widget_row[0]) + self.widget_row.clear() + self.widget_row.extend(new_row) + elif swapped == {screen_rects[3].topleft, screen_rects[4].topleft}: + new_row = (self.widget_row[0], self.widget_row[2], self.widget_row[1]) + self.widget_row.clear() + self.widget_row.extend(new_row) diff --git a/keyboard/mousepad.py b/keyboard/mousepad.py index 1a7f64e..22a81a5 100644 --- a/keyboard/mousepad.py +++ b/keyboard/mousepad.py @@ -20,10 +20,15 @@ SPEEDS = ((2400, 2400), (600, -600)) class MousePadWidget(Child): + BUTTON_HEIGHT_DIVISOR = 6 + def __init__(self, parent, rect, click_cb, move_cb): super().__init__(parent) self.rect = rect - button_size = (rect.width / len(BUTTONS), rect.height / 6) + button_size = ( + rect.width / len(BUTTONS), + rect.height / self.BUTTON_HEIGHT_DIVISOR, + ) for i, (value, args) in enumerate(BUTTONS): KeyButton( parent, @@ -44,6 +49,28 @@ class MousePadWidget(Child): move_cb, ) + @classmethod + def get_symbolic_surf(cls, size): + from .layout import get_symbolic_keys + + surf = pygame.Surface(size, pygame.SRCALPHA) + surf.fill(0x7F000000) + button_height = size[1] / MousePadWidget.BUTTON_HEIGHT_DIVISOR + get_symbolic_keys( + surf.subsurface( + pygame.Rect((0, size[1] - button_height), (size[0], button_height)) + ), + (size[0], button_height), + BUTTONS, + ) + pygame.draw.rect( + surf, "black", pygame.Rect((0, 0), (size[0], size[1] - button_height)) + ) + pygame.draw.rect( + surf, "yellow", pygame.Rect((0, 0), (size[0], size[1] - button_height)), 1 + ) + return surf + @dataclass class Finger: @@ -96,6 +123,13 @@ class MousePadArea(Child): def draw(self): pygame.draw.rect(self.surf, "yellow", self.rect, 1) + for finger in self.fingers.values(): + pygame.draw.circle( + self.surf, + "yellow", + MultitouchHandler.map_coords(finger.old, self.surf.get_size()), + 16, + ) def handle_fingerdown(self, event): pos = (event.x, event.y) diff --git a/ui/__init__.py b/ui/__init__.py index cb4cbff..8b2ecc1 100644 --- a/ui/__init__.py +++ b/ui/__init__.py @@ -10,7 +10,7 @@ from .label import Label from .message_box import MessageBox from .modal import Modal, QuittableModal from .parent import Parent -from .rect import Rect +from .rect import LabelledRect, Rect from .root import BaseRoot from .scroll import Scroll from .slider import Slider @@ -31,6 +31,7 @@ __all__ = [ "Icon", "IconButton", "Label", + "LabelledRect", "MessageBox", "Modal", "Parent", diff --git a/ui/rect.py b/ui/rect.py index 3959c31..2cb134a 100644 --- a/ui/rect.py +++ b/ui/rect.py @@ -15,3 +15,21 @@ class Rect(Child): pygame.draw.rect(self.surf, self.background_color, self.rect) if self.border_color: pygame.draw.rect(self.surf, self.border_color, self.rect, 1) + + +class LabelledRect(Rect): + def __init__(self, parent, rect, value, text_color, background_color, border_color): + super().__init__(parent, rect, background_color, border_color) + self.value = value + self.text_color = text_color + + def draw(self): + super().draw() + fs = self.font.render(self.value, True, self.text_color) + self.surf.blit( + fs, + ( + self.rect.centerx - fs.get_width() / 2, + self.rect.centery - fs.get_height() / 2, + ), + )