#!/usr/bin/env python3
-import sys
-from contextlib import redirect_stdout
-from io import StringIO
-from pathlib import Path
+from launch import run
-sys.path.append(str(Path(__file__).parent))
-
-from bookpaint.bookpaint import BookPaint
-
-with redirect_stdout(StringIO()):
- # ruff: noqa: F401
- import pygame # type: ignore
-
-BookPaint().run()
+if __name__ == "__main__":
+ run()
--- /dev/null
+import pygame
+
+from ui import Modal
+
+
+class BaseMenu(Modal):
+ def handle_quit(self, _):
+ self.root.running = False
+
+ key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}}
pygame.display.set_mode((0, 0), pygame.FULLSCREEN),
pygame.font.Font(None, size=96),
)
+ self.draw_image = DrawImage(self, pygame.Color("white"))
self.color_menu = ColorMenu(self)
- self.draw_image = DrawImage(self, "white")
self.line_menu = LineMenu(self)
+ self.book_manager = BookManager(self, Path("book"))
self.page_menu = PageMenu(self)
self.menu = Menu(self)
- self.book_manager = BookManager(self, Path("book"))
self.page_menu.update_label()
self.setup_deactivate_keys()
+from colorsys import hsv_to_rgb, rgb_to_hsv
+from functools import partial
+
import pygame
-from ui import Button, Label, Modal, TextInput
+from ui import Child, Label, Slider, Spinner, TextInput
+
+from .base_menu import BaseMenu
+
+class ColorLabel(Child):
+ def __init__(self, parent, rect, color):
+ super().__init__(parent)
+ self.rect = rect
+ self.color = color
+
+ def draw(self):
+ pygame.draw.rect(self.surf, self.color, self.rect)
-class ColorMenu(Modal):
+
+class ColorMenu(BaseMenu):
def __init__(self, parent):
super().__init__(parent)
size = self.surf.get_size()
- self.size_third = tuple(a // 3 for a in size)
+ self.modal_rect = pygame.Rect(
+ (size[0] // 8, size[1] // 8),
+ (size[0] * 3 // 4, size[1] * 3 // 4),
+ )
+ y = self.modal_rect.top
button_height = 128
- y = self.size_third[1]
Label(
self,
- pygame.Rect((self.size_third[0], y), (self.size_third[0], 128)),
+ pygame.Rect(
+ (self.modal_rect.left, y), (self.modal_rect.width, button_height)
+ ),
"Color Menu",
Label.HAlign.CENTER,
)
+ label_width = self.modal_rect.width // 16
+ slider_width = self.modal_rect.width * 5 // 16
+ input_width = self.modal_rect.width * 2 // 16
+ label_left = self.modal_rect.left
+ slider_left = label_left + label_width
+ input_left = slider_left + slider_width
+ label_right = self.modal_rect.centerx
+ slider_right = label_right + label_width
+ input_right = slider_right + slider_width
+
+ y += button_height * 2
+ Label(
+ self,
+ pygame.Rect((label_left, y), (label_width, button_height)),
+ "R",
+ Label.HAlign.CENTER,
+ )
+ self.red_slider = Slider(
+ self,
+ pygame.Rect((slider_left, y), (slider_width, button_height)),
+ Slider.Direction.HORIZONTAL,
+ 0,
+ button_height,
+ partial(self.set_rgb, dest="r", sender_type=Slider),
+ )
+ self.red_input = Spinner(
+ self,
+ pygame.Rect((input_left, y), (input_width, button_height)),
+ partial(self.set_rgb, dest="r", sender_type=Spinner),
+ )
+ Label(
+ self,
+ pygame.Rect((label_right, y), (label_width, button_height)),
+ "H",
+ Label.HAlign.CENTER,
+ )
+ self.hue_slider = Slider(
+ self,
+ pygame.Rect((slider_right, y), (slider_width, button_height)),
+ Slider.Direction.HORIZONTAL,
+ 0,
+ button_height,
+ partial(self.set_hsv, dest=0, sender_type=Slider),
+ )
+ self.hue_input = Spinner(
+ self,
+ pygame.Rect((input_right, y), (input_width, button_height)),
+ partial(self.set_hsv, dest=0, sender_type=Spinner),
+ )
+
+ y += button_height + 16
+ Label(
+ self,
+ pygame.Rect((label_left, y), (label_width, button_height)),
+ "G",
+ Label.HAlign.CENTER,
+ )
+ self.green_slider = Slider(
+ self,
+ pygame.Rect((slider_left, y), (slider_width, button_height)),
+ Slider.Direction.HORIZONTAL,
+ 0,
+ button_height,
+ partial(self.set_rgb, dest="g", sender_type=Slider),
+ )
+ self.green_input = Spinner(
+ self,
+ pygame.Rect((input_left, y), (input_width, button_height)),
+ partial(self.set_rgb, dest="g", sender_type=Spinner),
+ )
+ Label(
+ self,
+ pygame.Rect((label_right, y), (label_width, button_height)),
+ "S",
+ Label.HAlign.CENTER,
+ )
+ self.saturation_slider = Slider(
+ self,
+ pygame.Rect((slider_right, y), (slider_width, button_height)),
+ Slider.Direction.HORIZONTAL,
+ 0,
+ button_height,
+ partial(self.set_hsv, dest=1, sender_type=Slider),
+ )
+ self.saturation_input = Spinner(
+ self,
+ pygame.Rect((input_right, y), (input_width, button_height)),
+ partial(self.set_hsv, dest=1, sender_type=Spinner),
+ )
+
y += button_height + 16
- self.input = TextInput(
+ Label(
+ self,
+ pygame.Rect((label_left, y), (label_width, button_height)),
+ "B",
+ Label.HAlign.CENTER,
+ )
+ self.blue_slider = Slider(
+ self,
+ pygame.Rect((slider_left, y), (slider_width, button_height)),
+ Slider.Direction.HORIZONTAL,
+ 0,
+ button_height,
+ partial(self.set_rgb, dest="b", sender_type=Slider),
+ )
+ self.blue_input = Spinner(
+ self,
+ pygame.Rect((input_left, y), (input_width, button_height)),
+ partial(self.set_rgb, dest="b", sender_type=Spinner),
+ )
+ Label(
+ self,
+ pygame.Rect((label_right, y), (label_width, button_height)),
+ "V",
+ Label.HAlign.CENTER,
+ )
+ self.value_slider = Slider(
+ self,
+ pygame.Rect((slider_right, y), (slider_width, button_height)),
+ Slider.Direction.HORIZONTAL,
+ 0,
+ button_height,
+ partial(self.set_hsv, dest=2, sender_type=Slider),
+ )
+ self.value_input = Spinner(
+ self,
+ pygame.Rect((input_right, y), (input_width, button_height)),
+ partial(self.set_hsv, dest=2, sender_type=Spinner),
+ )
+
+ y += button_height * 2
+ half_width = self.modal_rect.width // 2
+ self.hex_input = TextInput(
self,
- pygame.Rect((self.size_third[0], y), (self.size_third[0], 128)),
+ pygame.Rect(
+ (self.modal_rect.left + 128, y), (half_width - 256, button_height)
+ ),
self.set_color,
"",
)
- y += (button_height + 16) * 2
- Button(
+ self.color_label = ColorLabel(
self,
- pygame.Rect((self.size_third[0], y), (self.size_third[0], 128)),
- "Close",
- self.deactivate
+ pygame.Rect(
+ (self.modal_rect.centerx + 128, y),
+ (half_width - 256, button_height),
+ ),
+ pygame.Color("white"),
)
def activate(self):
super().activate()
- self.input.value = f"{int(self.parent.draw_image.color) >> 8:06x}"
+ self.update_controls()
- def set_color(self, value):
+ @staticmethod
+ def color_to_hsv(color):
+ return tuple(
+ max(min(x * 255, 255), 0)
+ for x in rgb_to_hsv(color.r / 255, color.g / 255, color.b / 255)
+ )
+
+ @staticmethod
+ def hsv_to_color(hsv):
+ return pygame.Color(
+ *(int(c * 255) for c in hsv_to_rgb(*(x / 255 for x in hsv)))
+ )
+
+ def update_controls(self, skip_hsv=False):
color = self.parent.draw_image.color
- try:
- color = pygame.Color(f"0x{value}")
- except ValueError:
- try:
- color = pygame.Color(value)
- except ValueError:
- pass
- self.parent.draw_image.color = color
+ self.color_label.color = color
+ self.hex_input.value = f"{int(color) >> 8:06x}"
+ controls = (
+ (self.red_slider, self.red_input, color.r),
+ (self.green_slider, self.green_input, color.g),
+ (self.blue_slider, self.blue_input, color.b),
+ )
+ if not skip_hsv:
+ hsv = self.color_to_hsv(color)
+ controls = (
+ *controls,
+ (self.hue_slider, self.hue_input, int(hsv[0])),
+ (self.saturation_slider, self.saturation_input, int(hsv[1])),
+ (self.value_slider, self.value_input, int(hsv[2])),
+ )
+ for slider, spinner, value in controls:
+ slider.value = slider.extent * value // 255
+ spinner.value = value
def draw_modal(self):
super().draw_modal()
- rect = pygame.Rect(self.size_third, self.size_third)
+ rect = self.modal_rect.copy()
rect.inflate_ip((32, 32))
pygame.draw.rect(self.surf, "black", rect)
pygame.draw.rect(self.surf, "gray", rect, 1)
- def draw(self):
- super().draw()
- pygame.draw.rect(
- self.surf,
- self.parent.draw_image.color,
- pygame.Rect(
- (self.size_third[0], self.size_third[1] + 288),
- (self.size_third[0], 128),
- ),
+ def set_rgb(self, value, dest, sender_type):
+ slider, spinner = {
+ "r": (self.red_slider, self.red_input),
+ "g": (self.green_slider, self.green_input),
+ "b": (self.blue_slider, self.blue_input),
+ }[dest]
+ if sender_type is Slider:
+ value = value * 255 // slider.extent
+ elif sender_type is Spinner:
+ pass
+ else:
+ raise KeyError(sender_type)
+ setattr(self.parent.draw_image.color, dest, max(min(value, 255), 0))
+ self.update_controls()
+ self.dirty = True
+
+ def set_hsv(self, value, dest, sender_type):
+ slider, spinner = (
+ (self.hue_slider, self.hue_input),
+ (self.saturation_slider, self.saturation_input),
+ (self.value_slider, self.value_input),
+ )[dest]
+ if sender_type is Slider:
+ limited_value = max(min(value, slider.extent), 0)
+ if limited_value != value:
+ value = limited_value
+ slider.value = value
+ value = value * 255 // slider.extent
+ spinner.value = int(value)
+ elif sender_type is Spinner:
+ limited_value = max(min(int(value), 255), 0)
+ if limited_value != value:
+ value = limited_value
+ spinner.value = value
+ slider.value = value * slider.extent // 255
+ else:
+ raise KeyError(sender_type)
+ self.parent.draw_image.color = self.hsv_to_color(
+ (
+ value if dest == 0 else self.hue_input.value,
+ value if dest == 1 else self.saturation_input.value,
+ value if dest == 2 else self.value_input.value,
+ )
)
+ self.update_controls(True)
+ self.dirty = True
- key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}}
+ def set_color(self, value):
+ try:
+ color = pygame.Color(value)
+ except:
+ color = pygame.Color(f"0x{value}")
+ self.parent.draw_image.color = color
+ self.update_controls()
+ self.dirty = True
def __init__(self, parent, color):
super().__init__(parent)
- if not isinstance(color, pygame.Color):
- color = pygame.Color(color)
self.color = color
self.line_width = 1
self.image = pygame.Surface(self.surf.get_size(), 0, 24)
self.dirty = True
def dot(self, surf, pos):
- if self.stroke_method == self.StrokeMethod.SQUARE:
+ if self.stroke_method is self.StrokeMethod.SQUARE:
pass
elif self.line_width // 2 == 0:
surf.set_at(pos, self.color)
if ev.pos == self.prev_pos:
self.dot(self.image, ev.pos)
return
- if self.stroke_method == self.StrokeMethod.ROUND:
+ if self.stroke_method is self.StrokeMethod.ROUND:
StrokeRoundLine(
self.prev_pos, ev.pos, self.line_width
).draw(self.image, self.color)
- elif self.stroke_method == self.StrokeMethod.SQUARE:
+ elif self.stroke_method is self.StrokeMethod.SQUARE:
StrokeSquareLine(
self.prev_pos, ev.pos, self.line_width
).draw(self.image, self.color)
- elif self.stroke_method == self.StrokeMethod.PYGAME:
+ elif self.stroke_method is self.StrokeMethod.PYGAME:
pygame.draw.line(
self.image, self.color, self.prev_pos, ev.pos, self.line_width
)
+ else:
+ raise IndexError(self.stroke_method)
self.prev_pos = ev.pos
def handle_mousebuttonup(self, ev):
import pygame
-from ui import DropDown, Label, Modal, Slider, TextInput
+from ui import DropDown, Label, Slider, TextInput
from .draw_image import DrawImage
+from .base_menu import BaseMenu
-class LineMenu(Modal):
+class LineMenu(BaseMenu):
def __init__(self, parent):
super().__init__(parent)
size = self.surf.get_size()
rect.inflate_ip((32, 32))
pygame.draw.rect(self.surf, "black", rect)
pygame.draw.rect(self.surf, "gray", rect, 1)
-
- key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}}
import pygame
-from ui import Button, Label, Modal
+from ui import Button, Label
from .book_manager import BookManager
+from .base_menu import BaseMenu
-class PageMenu(Modal):
+class PageMenu(BaseMenu):
def __init__(self, parent):
super().__init__(parent)
size = self.surf.get_size()
)
y += button_height + 16
callbacks_per_value = (
- ("|<", partial(self.parent.nav, BookManager.Nav.FIRST)),
- ("<", partial(self.parent.nav, BookManager.Nav.PREV)),
- (">", partial(self.parent.nav, BookManager.Nav.NEXT)),
- (">|", partial(self.parent.nav, BookManager.Nav.LAST)),
- ("NEW", partial(self.parent.nav, BookManager.Nav.NEW)),
+ ("|<", partial(self.nav, BookManager.Nav.FIRST)),
+ ("<", partial(self.nav, BookManager.Nav.PREV)),
+ (">", partial(self.nav, BookManager.Nav.NEXT)),
+ (">|", partial(self.nav, BookManager.Nav.LAST)),
+ ("NEW", partial(self.nav, BookManager.Nav.NEW)),
)
button_width = size[0] // (2 * len(callbacks_per_value))
x = self.base_rect.left
pygame.draw.rect(self.surf, "black", rect)
pygame.draw.rect(self.surf, "gray", rect, 1)
- key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}}
-
def update_label(self):
self.label.value = " / ".join(
(
str(self.parent.book_manager.get_current_index() + 1),
str(len(self.parent.book_manager.pages)),
)
- )
\ No newline at end of file
+ )
+
+ def nav(self, nav):
+ self.parent.nav(nav)
+
+ key_methods = {
+ **BaseMenu.key_methods,
+ frozenset({pygame.KMOD_CTRL}): {
+ pygame.K_HOME: partial(nav, nav=BookManager.Nav.FIRST),
+ pygame.K_PAGEUP: partial(nav, nav=BookManager.Nav.PREV),
+ pygame.K_PAGEDOWN: partial(nav, nav=BookManager.Nav.NEXT),
+ pygame.K_END: partial(nav, nav=BookManager.Nav.LAST),
+ pygame.K_n: partial(nav, nav=BookManager.Nav.NEW),
+ }
+ }
from math import pi, sqrt
from time import time
-import pygame
-
+from launch import run, pygame
from ui import Button, MessageBox, Rect, Root
from vectors import StrokeCircleSegment
if __name__ == "__main__":
- ConnectFour().run()
+ run()
script_dir="$(realpath -Pe "$(dirname "${0}")")"
executable="${script_dir}/zenbook_conf.py"
icon="${script_dir}/laptop-single.svg"
-virtual_env=
help() {
echo " ${0} [--executable PATH] [--icon PATH] [--virtual-env PATH] | -h|--help"
icon="${2}"
shift
;;
- --virtual-env)
- virtual_env="${2}"
- shift
- ;;
-h|--help)
help=1
;;
echo "[Desktop Entry]"
echo "Type=Application"
-if [[ "${virtual_env}" ]]; then
- virtual_env="$(realpath -Pe "${virtual_env}")"
- env="VIRTUAL_ENV=${virtual_env} PATH=\$VIRTUAL_ENV/bin:\$PATH "
-else
- env=
-fi
-echo "Exec=/usr/bin/sh -c '${env}$(realpath -Pe "${executable}")'"
+echo "Exec=$(realpath -Pe "${executable}")"
echo "Icon=$(realpath -Pe "${icon}")"
echo ""
echo "Name=Zenbook Duo settings"
--- /dev/null
+import os
+import shlex
+import sys
+from contextlib import redirect_stdout
+from importlib import import_module
+from inspect import stack
+from io import StringIO
+from pathlib import Path
+from time import time
+
+
+def setup_venv():
+ venv_dir = (Path(__file__).parents[1] / "venv").absolute()
+ if venv_dir.is_dir():
+ install_venv = max(
+ p.stat().st_mtime
+ for p in (
+ venv_dir,
+ *(
+ base / ent
+ for base, dirs, files in venv_dir.walk()
+ for ent in (*dirs, *files)
+ )
+ )
+ ) < time() - 86400
+ else:
+ if venv_dir.exists():
+ venv_dir.unlink()
+ assert not venv_dir.exists()
+ os.system(shlex.join((sys.executable, "-m", "venv", str(venv_dir))))
+ install_venv = True
+ new_env = {"VIRTUAL_ENV": str(venv_dir)}
+ venv_bin_str = str(venv_dir / "bin")
+ if venv_bin_str not in os.environ['PATH'].split(os.pathsep):
+ new_env["PATH"] = f"{venv_bin_str}{os.pathsep}{os.environ['PATH']}"
+ os.environ.update(new_env)
+ if install_venv:
+ os.system("python -m pip install -qU pip")
+ os.system("python -m pip install -qU pygame")
+ venv_dir.touch(0o755, True)
+ os.execl(sys.executable, Path(sys.executable).name, *sys.argv)
+ exit(1)
+
+
+try:
+ with redirect_stdout(StringIO()):
+ # ruff: noqa: F401
+ import pygame # type: ignore
+except ImportError:
+ setup_venv()
+ raise
+
+
+def find_root(module_globals):
+ from ui import Root
+
+ for key, value in module_globals.items():
+ if key.startswith("__") and key.endswith("__"):
+ continue
+ if value is not Root and isinstance(value, type) and issubclass(value, Root):
+ return value
+ return None
+
+
+def run():
+ for fi in stack():
+ caller_name = fi.frame.f_globals["__name__"]
+ if caller_name == "__main__" or caller_name.endswith(".__main__"):
+ module_globals = fi.frame.f_globals
+ break
+ else:
+ raise ValueError("Could not determine caller name")
+ root_class = find_root(module_globals)
+ if root_class is None:
+ package = Path(module_globals["__file__"]).stem
+ root_class = find_root(import_module(f"{package}.{package}").__dict__)
+ root_class().run()
from pathlib import Path
from secrets import choice
-import pygame
-
+from launch import run, pygame
from ui import Button, Child, Label, Modal, Root, TextInput
if __name__ == "__main__":
- MemoryGame().run()
+ run()
#!/usr/bin/env python3
-import sys
-from contextlib import redirect_stdout
-from io import StringIO
-from pathlib import Path
-
-sys.path.append(str(Path(__file__).parent))
-
-from rps.rps import RockPaperScissors
-
-with redirect_stdout(StringIO()):
- # ruff: noqa: F401
- import pygame # type: ignore
-
+from launch import run
if __name__ == "__main__":
- RockPaperScissors().run()
+ run()
+from copy import deepcopy
from functools import partial
import pygame
-from .utils import CowDict
-
class EventMethodDispatcher:
MODS = (pygame.KMOD_CTRL, pygame.KMOD_ALT, pygame.KMOD_META, pygame.KMOD_SHIFT)
def __init__(self):
- self.key_methods = CowDict(getattr(type(self), "key_methods", {}))
+ self.key_methods = deepcopy(getattr(type(self), "key_methods", {}))
def get_key_method(self, key, mod):
mods = set()
from .child import Child
-class HorizontalSlider(Child):
- def _get_extent_base(self):
- return self.rect.width
+class Slider(Child):
+ class Direction(Enum):
+ HORIZONTAL = auto()
+ VERTICAL = auto()
- def __init__(self, parent, rect, value=0, handle_size=None, callback=None):
+ def _get_extent_base(self):
+ return {
+ self.Direction.HORIZONTAL: self.rect.width,
+ self.Direction.VERTICAL: self.rect.height,
+ }[self.direction]
+
+ def __init__(
+ self,
+ parent,
+ rect,
+ direction,
+ value=0,
+ handle_size=None,
+ callback=None,
+ ):
super().__init__(parent)
self.rect = rect
+ self.direction = direction
self.extent = (
self._get_extent_base() - (1 if handle_size is None else handle_size)
)
self._pushed = None
def value_from_pos(self, pos):
- return pos[0]
+ return {
+ self.Direction.HORIZONTAL: pos[0],
+ self.Direction.VERTICAL: pos[1],
+ }[self.direction]
def set_rel_value(self, value):
- self.set_value(value - self.rect.left)
+ self.set_value(
+ {
+ self.Direction.HORIZONTAL: value - self.rect.left,
+ self.Direction.VERTICAL: self.extent - (value - self.rect.top),
+ }[self.direction]
+ )
def get_cursor_rect(self):
value, limited = self.map_value()
- return pygame.Rect(
- (self.rect.left + value, self.rect.top),
- (self.handle_size, self.rect.height),
- ), limited
+ if self.direction is self.Direction.HORIZONTAL:
+ rect = pygame.Rect(
+ (self.rect.left + value, self.rect.top),
+ (self.handle_size, self.rect.height),
+ )
+ elif self.direction is self.Direction.VERTICAL:
+ rect = pygame.Rect(
+ (self.rect.left, self.rect.top + self.extent - value),
+ (self.rect.width, self.handle_size),
+ )
+ else:
+ raise IndexError(self.direction)
+ return rect, limited
def draw_cursor_line(self):
value, limited = self.map_value()
- x = self.rect.left + value
- pygame.draw.line(
- self.surf,
- "dimgray" if limited else "gray",
- (x, self.rect.top),
- (x, self.rect.bottom),
- 8,
- )
+ if self.direction is self.Direction.HORIZONTAL:
+ x = self.rect.left + value
+ points = (x, self.rect.top), (x, self.rect.bottom)
+ elif self.direction is self.Direction.VERTICAL:
+ y = self.rect.top + self.extent - value
+ points = (self.rect.left, y), (self.rect.right, y)
+ else:
+ raise IndexError(self.direction)
+ pygame.draw.line(self.surf, "dimgray" if limited else "gray", *points, 8)
def page_flip(self, cursor_rect, value):
- if value < cursor_rect.left:
- return max(self.value - cursor_rect.width, 0)
+ if self.direction is self.Direction.HORIZONTAL:
+ if value < cursor_rect.left:
+ return max(self.value - cursor_rect.width, 0)
+ else:
+ return min(self.value + cursor_rect.width, self.extent)
+ elif self.direction is self.Direction.VERTICAL:
+ if value < cursor_rect.top:
+ return min(self.value + cursor_rect.height, self.extent)
+ else:
+ return max(self.value - cursor_rect.height, 0)
else:
- return min(self.value + cursor_rect.width, self.extent)
+ raise IndexError(self.direction)
def set_value(self, value):
if value == self.value:
self.pushed = value
def get_rel(self, value):
- return self.pushed - value
+ if self.direction is self.Direction.HORIZONTAL:
+ return self.pushed - value
+ elif self.direction is self.Direction.VERTICAL:
+ return value - self.pushed
+ raise IndexError(self.direction)
def handle_mousemotion(self, ev):
if self.pushed is None:
value = -value
limited = True
if value > self.extent:
- value = int(self.extent * (self.extent / value))
+ value = self.extent ** 2 // value
limited = True
return value, limited
else:
color = "gray"
pygame.draw.rect(self.surf, color, rect)
-
-
-class VerticalSlider(HorizontalSlider):
- def _get_extent_base(self):
- return self.rect.height
-
- def value_from_pos(self, pos):
- return pos[1]
-
- def set_rel_value(self, value):
- self.set_value(self.extent - (value - self.rect.top))
-
- def get_cursor_rect(self):
- value, limited = self.map_value()
- return pygame.Rect(
- (self.rect.left, self.rect.top + self.extent - value),
- (self.rect.width, self.handle_size),
- ), limited
-
- def draw_cursor_line(self):
- value, limited = self.map_value()
- y = self.rect.top + self.extent - value
- pygame.draw.line(
- self.surf,
- "dimgray" if limited else "gray",
- (self.rect.left, y),
- (self.rect.right, y),
- 8,
- )
-
- def page_flip(self, cursor_rect, value):
- if value < cursor_rect.top:
- return min(self.value + cursor_rect.height, self.extent)
- else:
- return max(self.value - cursor_rect.height, 0)
-
- def get_rel(self, value):
- return value - self.pushed
-
-
-class Slider:
- class Direction(Enum):
- HORIZONTAL = auto()
- VERTICAL = auto()
-
- def __new__(cls, parent, rect, direction, value=0, handle_size=None, callback=None):
- if direction == cls.Direction.HORIZONTAL:
- klass = HorizontalSlider
- else: # direction == cls.Direction.VERTICAL
- klass = VerticalSlider
- return klass(parent, rect, value, handle_size, callback)
def __init__(self, parent, rect, callback, value=0):
super().__init__(parent)
self.callback = callback
+ self._value = 0
self.value = value
button_size = (rect.height // 2, rect.height // 2)
self.text_input = TextInput(
partial(self.spin_callback, -1),
)
- def call_callback(self, value):
+ @property
+ def value(self):
+ return self._value
+
+ @value.setter
+ def value(self, value):
try:
int_value = int(value)
except ValueError:
- pass
- else:
- if int_value != self.value:
- self.value = int_value
- self.callback(int_value)
+ return
+ if int_value == self.value:
+ return
+ self.value = int_value
str_value = str(self.value)
if str_value != self.text_input.value:
- self.text_input.value = str(self.value)
+ self.text_input.value = str_value
self.dirty = True
+ def call_callback(self, value):
+ old_value = self.value
+ self.value = value
+ if old_value != self.value:
+ self.callback(self.value)
+
def spin_callback(self, value):
self.value += value
- self.text_input.value = str(self.value)
self.callback(self.value)
self.dirty = True
class FloatSpinner(Spinner):
- def call_callback(self, value):
+ @property
+ def value(self):
+ return self._value
+
+ @value.setter
+ def value(self, value):
try:
float_value = float(value)
except ValueError:
- pass
- else:
- if float_value != self.value:
- self.value = float_value
- self.callback(float_value)
+ return
+ if float_value == self.value:
+ return
+ self.value = float_value
str_value = str(self.value)
if str_value != self.text_input.value:
- self.text_input.value = str(self.value)
+ self.text_input.value = str_value
self.dirty = True
+++ /dev/null
-class CowDict:
- def __init__(self, orig_dict):
- self.orig_dict = orig_dict
- self.copy = None
-
- @property
- def dict(self):
- return self.copy or self.orig_dict
-
- def __getitem__(self, key):
- return self.dict[key]
-
- def _clone(self):
- if self.copy is None:
- self.copy = self.orig_dict.copy()
-
- def __setitem__(self, key, value):
- self._clone()
- self.copy[key] = value
-
- def __getattr__(self, item):
- mod_methods = ("clear", "pop", "popitem", "setdefault", "update")
- if self.copy is None and item in mod_methods:
- self._clone()
- return getattr(self.dict, item)
from secrets import choice, randbelow
from time import time
-import pygame
-
+from launch import run, pygame
from ui import Button, Child, FloatSpinner, Label, Root, Spinner
if __name__ == "__main__":
- VSMemory().run()
+ run()
#!/usr/bin/env python3
-import sys
-from contextlib import redirect_stdout
-from io import StringIO
-from pathlib import Path
+from launch import run
-sys.path.append(str(Path(__file__).parent))
-
-from zenbook_conf.zenbook_conf import ZenbookConf
-
-with redirect_stdout(StringIO()):
- # ruff: noqa: F401
- import pygame # type: ignore
-
-ZenbookConf().run()
+if __name__ == "__main__":
+ run()