from zenbook_conf.xrandr import XrandrConf
from .bookpaint_menu import BookPaintMenu
-from .draw_image import DrawImage
+from .draw_image import DrawImage, StrokeMethod
class BookPaint(Root):
)
self.background_color = self.BACKGROUND_COLOR
self.foreground_color = "white"
+ self.drawing_color = "foreground_color"
+ self.line_width = 1
+ self.stroke_method = StrokeMethod.PYGAME
self.menu = BookPaintMenu(self)
self.draw_image = DrawImage(self)
- self.draw_image.clear(self.background_color)
+ self.clear()
self.xrandr_conf = XrandrConf(True)
def key_escape(self):
(0, 0), self.display_flags, display=self.current_display
)
self.dirty = True
+
+ def clear(self):
+ self.draw_image.clear(self.background_color)
+from functools import partial
from pathlib import Path
import pygame
TextInput,
)
+from .color_chooser import ColorChooser
+from .draw_image import StrokeMethod
+
class BookPaintMenu(Modal):
+ def get_stroke_methods(self):
+ return (
+ self.root.stroke_method.name.title(),
+ [stroke_method.name.title() for stroke_method in StrokeMethod],
+ )
+
def __init__(self, parent):
super().__init__(parent)
Button(
str(Path("book/").absolute()),
unfocused_align=TextInput.RIGHT,
)
- ColorButton(
+ color_chooser = ColorChooser(self)
+ self.background_color = ColorButton(
self,
grid_layout.get_rect((1, 1)).inflate((pad, pad)),
self.root.background_color,
- lambda: None,
+ partial(color_chooser.activate_for, "background_color"),
)
Button(
self,
grid_layout.get_rect((2, 1)).inflate((pad, pad)),
"Fill Page",
- lambda: None,
+ self.root.clear,
)
- ColorButton(
+ self.foreground_color = ColorButton(
self,
grid_layout.get_rect((1, 2)).inflate((pad, pad)),
self.root.foreground_color,
- lambda: None,
+ partial(color_chooser.activate_for, "foreground_color"),
)
- Slider(
+ self.width_slider = Slider(
self,
grid_layout.get_rect((1, 3)).inflate((pad, pad)),
Slider.HORIZONTAL,
- handle_size=96,
- callback=lambda _: None,
- ),
- DropDown(
+ self.root.line_width - 1,
+ 96,
+ self.set_width,
+ )
+ self.width_label = CenterLabel(
+ self,
+ grid_layout.get_rect((2, 3)),
+ "1",
+ )
+ self.stroke_method = DropDown(
self,
grid_layout.get_rect((1, 4)).inflate((pad, pad)),
- "<choose>",
- ["a", "b", "c"],
- lambda _: None,
+ *self.get_stroke_methods(),
+ self.set_stroke_method,
)
DropDown(
self,
button_bar_layout.get_rect(),
"|<",
lambda: None,
- ),
+ )
RepeatButton(
self,
button_bar_layout.get_rect(),
"<",
lambda: None,
- ),
+ )
RepeatButton(
self,
button_bar_layout.get_rect(),
">",
lambda: None,
- ),
+ )
Button(
self,
button_bar_layout.get_rect(),
">|",
lambda: None,
- ),
+ )
Button(
self,
button_bar_layout.get_rect(),
"New",
lambda: None,
- ),
-
- def key_escape(self):
- self.deactivate()
+ )
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: key_escape}}
+ KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
def set_book_path(self, value):
print("book path", value)
+
+ def set_color(self, attr, color):
+ setattr(self.root, attr, color)
+ getattr(self, attr).color = color
+ self.dirty = True
+
+ def set_width(self, slider_width):
+ if slider_width < 0:
+ slider_width = 0
+ if self.width_slider.value != slider_width:
+ self.width_slider.value = slider_width
+ self.dirty = True
+ width = (slider_width * 256 // self.width_slider.extent) + 1
+ if self.root.line_width != width:
+ self.root.line_width = width
+ self.width_label.value = str(width)
+ self.dirty = True
+
+ def set_stroke_method(self, i):
+ self.root.stroke_method = StrokeMethod(i + 1)
+ self.stroke_method.value = self.root.stroke_method.name.title()
--- /dev/null
+from colorsys import hsv_to_rgb
+from functools import partial
+
+import pygame
+
+from ui import Modal, Rect
+
+from ui import ColorButton
+
+
+def hsv_to_color(hsv):
+ return pygame.Color(*(int(c * 255) for c in hsv_to_rgb(*(c / 255 for c in hsv))))
+
+
+class ColorChooser(Modal):
+ def get_color_grid(self, lefttop, cell_size):
+ """
+ 8 * 8 * 8 hsv colors => 351 unique colors of that cube.
+ Arrange them in a visually pleasing way.
+ """
+ x = y = 0
+ pos = list(lefttop)
+ colors_seen = set()
+ grays = []
+ grays_pos = None
+ for v_range in (range(7, 3, -1), range(3, -1, -1)):
+ for s in range(7, -1, -1):
+ for v in v_range:
+ for h in range(8):
+ color = hsv_to_color((h * 255 / 7, s * 255 / 7, v * 255 / 7))
+ int_color = (color.r << 16) | (color.g << 8) | color.b
+ if int_color not in colors_seen:
+ colors_seen.add(int_color)
+ if v == 0:
+ grays_pos = pos.copy()
+ grays.insert(0, color)
+ elif s == 0:
+ grays.append(color)
+ else:
+ yield (pygame.Rect(pos, cell_size), color)
+ x += 1
+ pos[0] += cell_size[0] // (2 - bool(x % 8))
+ if x < 32:
+ continue
+ x, pos[0] = 0, lefttop[0]
+ y += 1
+ pos[1] += cell_size[1] // (2 - bool(y % 8))
+ for i, gray in enumerate((*grays[1:], grays[0])):
+ yield (
+ pygame.Rect(
+ (
+ grays_pos[0] + (i % 4) * cell_size[0],
+ grays_pos[1] + (i // 4) * cell_size[1],
+ ),
+ cell_size,
+ ),
+ gray,
+ )
+
+ def __init__(self, parent):
+ super().__init__(parent)
+ # self.rect = rect
+ size = self.surf.get_size()
+ rect = pygame.Rect(
+ (448, 192),
+ (size[0] - 896, size[1] - 384),
+ )
+ Rect(self, rect, "black", "gray34")
+ self.attr = None
+ self.color = None
+ for r, color in self.get_color_grid((rect.left + 48, rect.top + 192), (64, 64)):
+ ColorButton(self, r, color, partial(self.set_color, color))
+
+ KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+
+ def activate_for(self, attr):
+ self.attr = attr
+ super().activate()
+
+ def set_color(self, color):
+ self.color = color
+ self.deactivate()
+
+ def deactivate(self):
+ super().deactivate()
+ self.parent.set_color(self.attr, self.color)
+from enum import Enum, auto
+
import pygame
from ui import Child
+from vectors import StrokeRoundLine, StrokeSquareLine
+
+
+class StrokeMethod(Enum):
+ PYGAME = auto()
+ ROUND = auto()
+ SQUARE = auto()
class DrawImage(Child):
super().__init__(parent)
self.surface = pygame.Surface(self.surf.get_size(), 0, 24)
self.clear(self.root.BACKGROUND_COLOR)
+ self.prev_pos = None
def clear(self, background_color):
self.surface.fill(background_color)
def draw(self):
self.surf.blit(self.surface, (0, 0))
+
+ def pygame_stroke(self, a, b):
+ root = self.root
+ pygame.draw.line(
+ self.surface, getattr(root, root.drawing_color), a, b, root.line_width
+ )
+
+ def round_stroke(self, a, b):
+ root = self.root
+ StrokeRoundLine(
+ a, b, root.line_width
+ ).draw(self.surface, getattr(root, root.drawing_color))
+
+ def square_stroke(self, a, b):
+ root = self.root
+ StrokeSquareLine(
+ a, b, root.line_width
+ ).draw(self.surface, getattr(root, root.drawing_color))
+
+ STROKE_MAPPING = {
+ StrokeMethod.PYGAME: pygame_stroke,
+ StrokeMethod.ROUND: round_stroke,
+ StrokeMethod.SQUARE: square_stroke
+ }
+
+ def handle_mousebuttondown(self, ev):
+ if ev.button == 1:
+ self.root.drawing_color = "foreground_color"
+ elif ev.button == 3:
+ self.root.drawing_color = "background_color"
+ else:
+ return
+ self.prev_pos = ev.pos
+
+ def handle_mousemotion(self, ev):
+ if self.prev_pos is not None:
+ self.STROKE_MAPPING[self.root.stroke_method](self, self.prev_pos, ev.pos)
+ self.prev_pos = ev.pos
+ self.dirty = True
+
+ def handle_mousebuttonup(self, ev):
+ if self.prev_pos is not None:
+ self.STROKE_MAPPING[self.root.stroke_method](self, self.prev_pos, ev.pos)
+ self.prev_pos = None
+ self.dirty = True