+from enum import Enum, auto
+from operator import itemgetter
from pathlib import Path
import pygame
except ValueError:
pages.append(p)
else:
- numbered_pages.append(p)
+ numbered_pages.append((p, page_no))
free_page_no = max(free_page_no, page_no + 1)
pages = sorted(pages)
- pages.extend(sorted(numbered_pages))
+ pages.extend(p[0] for p in sorted(numbered_pages, key=itemgetter(1)))
return free_page_no, pages
- def __init__(self, path: Path):
+ def __init__(self, parent, path: Path):
+ self.parent = parent
self.path = path
+ if not path.exists():
+ path.mkdir(0o755)
self.free_page_no, self.pages = self.get_pages()
self.current_file = self.get_new_file()
def get_new_file(self):
return self.path / f"{self.free_page_no}{self.SUFFIX}"
- def save_file(self, surf):
+ def save_file(self):
if self.current_file not in self.pages:
if self.current_file.exists():
self.pages.clear()
self.current_file = self.get_new_file()
self.pages.append(self.current_file)
self.free_page_no += 1
- pygame.image.save(surf, self.current_file)
+ pygame.image.save(self.parent.draw_image.surface, self.current_file)
- NewFile = type("NewFileType", (), {})()
+ class Nav(Enum):
+ FIRST = auto()
+ PREV = auto()
+ NEXT = auto()
+ LAST = auto()
+ NEW = auto()
- def next_file(self):
- if self.current_file not in self.pages:
+ def _get_index_for_page(self, nav):
+ if nav is self.Nav.NEW or len(self.pages) == 0:
+ return -1
+ elif nav is self.Nav.FIRST:
+ return 0
+ elif nav is self.Nav.LAST:
+ return len(self.pages) - 1
+ try:
+ current_index = self.pages.index(self.current_file)
+ except ValueError:
+ current_index = -1
+ if nav is self.Nav.PREV:
+ if current_index == -1:
+ return len(self.pages) - 1
+ elif current_index > 0:
+ return current_index - 1
return None
- i = self.pages.index(self.current_file)
- if i == len(self.pages) - 1:
- self.current_file = self.get_new_file()
- return self.NewFile
- self.current_file = self.pages[i + 1]
- return pygame.image.load(self.current_file)
+ elif nav is self.Nav.NEXT:
+ if current_index == -1:
+ return None
+ elif current_index < len(self.pages) - 1:
+ return current_index + 1
+ return -1
+ raise IndexError(nav)
- def prev_file(self):
- if self.current_file not in self.pages:
- i = len(self.pages)
- else:
- i = self.pages.index(self.current_file)
- if i == 0:
- return None
- self.current_file = self.pages[i - 1]
- return pygame.image.load(self.current_file)
+ def nav(self, nav):
+ if self.parent.draw_image.image_dirty:
+ self.save_file()
+ i = self._get_index_for_page(nav)
+ if i >= 0:
+ self.current_file = self.pages[i]
+ self.parent.draw_image.set_image(pygame.image.load(self.current_file))
+ elif self.current_file in self.pages:
+ self.current_file = self.get_new_file()
+ self.parent.draw_image.clear()
from .menu import Menu
from .page_menu import PageMenu
-
-# - color menu; set background (default for new pages) and foreground color
-# - stub with a 6 hex-digit textinput and a little rectangle to show the color
# - page menu; browse through next and prev pages using stylus
-# - line menu: choose line width and stroke method
-# - info layer (F3)
-# - list color and page menus in the main menu but also using keyboard shortcuts c/p/l
class BookPaint(Root):
self.line_menu = LineMenu(self)
self.page_menu = PageMenu(self)
self.menu = Menu(self)
- book_path = Path("book")
- if not book_path.exists():
- book_path.mkdir(0o755)
- self.book_manager = BookManager(book_path)
+ self.book_manager = BookManager(self, Path("book"))
def show_menu(self, menu_attr):
getattr(self, menu_attr).activate()
def save_file(self):
- self.book_manager.save_file(self.draw_image.surface)
- self.draw_image.image_dirty = False
-
- def set_image(self, surf):
- if surf == BookManager.NewFile:
- self.draw_image.clear()
- elif surf:
- self.draw_image.set_image(surf)
-
- def next_file(self):
- if self.draw_image.image_dirty:
- self.save_file()
- self.set_image(self.book_manager.next_file())
+ self.book_manager.save_file()
- def prev_file(self):
- if self.draw_image.image_dirty:
- self.save_file()
- self.set_image(self.book_manager.prev_file())
+ def nav(self, nav):
+ self.book_manager.nav(nav)
KEY_METHODS = {
frozenset(): {
},
frozenset({pygame.KMOD_CTRL}): {
pygame.K_s: save_file,
- pygame.K_PAGEDOWN: next_file,
- pygame.K_PAGEUP: prev_file,
+ 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),
},
}
),
)
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+ KEY_METHODS = {
+ frozenset(): {
+ pygame.K_ESCAPE: Modal.deactivate,
+ pygame.K_c: Modal.deactivate,
+ }
+ }
SQUARE = auto()
PYGAME = auto()
- DEFAULT_STROKE_METHOD = StrokeMethod.ROUND
-
def __init__(self, parent, color):
super().__init__(parent)
if not isinstance(color, pygame.Color):
self.color = color
self.line_width = 1
self.image = pygame.Surface(self.surf.get_size(), 0, 24)
- self.stroke_method = self.DEFAULT_STROKE_METHOD
+ self.stroke_method = self.StrokeMethod.ROUND
self.image_dirty = False
self.prev_pos = None
self.clear()
def clear(self):
self.image.fill("black")
+ self.prev_pos = None
+ self.image_dirty = False
+ self.dirty = True
+
+ def set_image(self, surf):
+ if self.image_dirty:
+ self.parent.book_manager.save_file()
+ self.image.blit(surf, (0, 0))
self.image_dirty = False
self.dirty = True
- self.prev_pos = None
def dot(self, surf, pos):
if self.stroke_method == self.StrokeMethod.SQUARE:
- hw = self.line_width // 2
- pygame.draw.rect(
- surf,
- self.color,
- pygame.Rect(
- (pos[0] - hw, pos[1] - hw), (self.line_width, self.line_width)
- )
- )
+ pass
elif self.line_width // 2 == 0:
surf.set_at(pos, self.color)
else:
def draw(self):
self.surf.blit(self.image, (0, 0))
- self.dot(self.surf, pygame.mouse.get_pos())
+ if self.root.focus_stack[-1] is self.parent:
+ self.dot(self.surf, pygame.mouse.get_pos())
def handle_mousebuttondown(self, ev):
if ev.button == 1:
if ev.button == 1:
self.handle_mousemotion(ev)
self.prev_pos = None
-
- def set_image(self, surf):
- self.image.blit(surf, (0, 0))
- self.dirty = True
"",
)
y += button_height + 16
- self.stroke_methods = list(DrawImage.StrokeMethod)
self.stroke_method = DropDown(
self,
pygame.Rect((self.size_third[0], y), (x, 128)),
- self.parent.draw_image.stroke_method.name.title(),
- [m.name.title() for m in self.stroke_methods],
+ "",
+ [m.name.title() for m in DrawImage.StrokeMethod],
self.set_stroke_method,
)
def activate(self):
super().activate()
- self.update_value(self.parent.draw_image.line_width)
+ self.update_values()
def set_line_width(self, value):
if isinstance(value, str):
if value <= 0:
value = 1
self.parent.draw_image.line_width = value
- self.update_value(value)
+ self.update_values()
def set_stroke_method(self, value):
- value = self.stroke_methods[value]
- self.parent.draw_image.stroke_method = value
- self.stroke_method.value = value.name.title()
+ self.parent.draw_image.stroke_method = DrawImage.StrokeMethod[
+ self.stroke_method.dropdown_menu.children[value].value.upper()
+ ]
+ self.update_values()
- def update_value(self, value):
- self.slider.value = value
- self.input.value = str(value)
+ def update_values(self):
+ line_width = self.parent.draw_image.line_width
+ self.slider.value = line_width
+ self.input.value = str(line_width)
+ self.stroke_method.value = self.parent.draw_image.stroke_method.name.title()
self.dirty = True
def draw_modal(self):
pygame.draw.rect(self.surf, "black", rect)
pygame.draw.rect(self.surf, "gray", rect, 1)
-
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+ KEY_METHODS = {
+ frozenset(): {
+ pygame.K_ESCAPE: Modal.deactivate,
+ pygame.K_l: Modal.deactivate,
+ }
+ }
pygame.draw.rect(self.surf, "black", rect)
pygame.draw.rect(self.surf, "gray", rect, 1)
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+ KEY_METHODS = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}}
+from functools import partial
+
import pygame
-from ui import Label, Modal
+from ui import Button, Label, Modal
+
+from .book_manager import BookManager
class PageMenu(Modal):
def __init__(self, parent):
super().__init__(parent)
- Label(self, pygame.Rect((100, 100), (200, 128)), "Page Menu")
+ size = self.surf.get_size()
+ button_height = 128
+ self.base_rect = pygame.Rect(
+ (size[0] // 4, size[1] * 6 // 8),
+ (size[0] // 2, max(size[1] // 8, button_height * 2 + 16)),
+ )
+ y = self.base_rect.top
+ Label(
+ self,
+ pygame.Rect((self.base_rect.left, y), (self.base_rect.width, button_height)),
+ "Page Menu",
+ Label.HAlign.CENTER,
+ )
+ 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)),
+ )
+ button_width = size[0] // (2 * len(callbacks_per_value))
+ x = self.base_rect.left
+ for value, callback in callbacks_per_value:
+ Button(
+ self,
+ pygame.Rect((x, y), (button_width, button_height)),
+ value,
+ callback
+ )
+ x += button_width
def draw_modal(self):
+ rect = self.base_rect.copy()
+ rect.inflate_ip((32, 32))
+ pygame.draw.rect(self.surf, "black", rect)
+ pygame.draw.rect(self.surf, "gray", rect, 1)
+
+ def first(self):
+ pass
+
+ def last(self):
pass
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+ KEY_METHODS = {
+ frozenset(): {
+ pygame.K_ESCAPE: Modal.deactivate,
+ pygame.K_p: Modal.deactivate,
+ }
+ }