]> git.mar77i.info Git - zenbook_gui/commitdiff
right-aligned text inputs
authormar77i <mar77i@protonmail.ch>
Mon, 17 Feb 2025 15:27:13 +0000 (16:27 +0100)
committermar77i <mar77i@protonmail.ch>
Mon, 17 Feb 2025 15:27:13 +0000 (16:27 +0100)
bookpaint/bookpaint_menu.py
ui/text_input.py

index 6bb376acdf3db24d286390c7f7df92fc3c79886c..dd489a7110e7dec278ad135301626991851408da 100644 (file)
@@ -1,3 +1,5 @@
+from pathlib import Path
+
 import pygame
 
 from layout import BarLayout, GridLayout
@@ -76,7 +78,8 @@ class BookPaintMenu(Modal):
             self,
             rect.inflate((pad, pad)),
             self.set_book_path,
-            "book/",
+            str(Path("book/").absolute()),
+            unfocused_align=TextInput.RIGHT,
         )
         ColorButton(
             self,
index 3f30c358d98526cef9b1a1cf48af726b922453d5..51fa4a3a986f4f2a734da4a3762f0b320e76a061 100644 (file)
@@ -1,6 +1,6 @@
 from contextlib import contextmanager
 from dataclasses import dataclass
-from functools import partial
+from functools import cached_property, partial
 from math import floor
 from time import time
 from typing import Optional, Callable
@@ -65,15 +65,37 @@ def search_same_isspace_forward(value, pos):
 
 
 class TextInput(Focusable, Child):
-    def __init__(self, parent, rect, callback, value="", value_filter=None):
+    LEFT = 0
+    RIGHT = 1
+    PADDING = 16
+
+    def __init__(
+        self,
+        parent,
+        rect,
+        callback,
+        value="",
+        value_filter=None,
+        unfocused_align=LEFT,
+    ):
         super().__init__(parent)
+        assert rect.width > self.PADDING * 2
         self.rect = rect
         self.callback = callback
         self.value = value
         self.value_filter = value_filter
+        assert unfocused_align in (self.LEFT, self.RIGHT)
+        self.unfocused_align = unfocused_align
         self.offset = 0
         self.cursor = None
 
+    @cached_property
+    def padded_rect(self):
+        return pygame.Rect(
+            (self.rect.left + self.PADDING, self.rect.top),
+            (self.rect.width - self.PADDING * 2, self.rect.height),
+        )
+
     @staticmethod
     def maybe_scroll_font_surface(font, value_to_cursor, fs, width, height):
         x = font.size(value_to_cursor)[0]
@@ -94,27 +116,38 @@ class TextInput(Focusable, Child):
             )
         return fs, offset, x - offset
 
-    def get_font_surface(self):
+    def get_focused_font_surface(self):
         if self.cursor.pos > len(self.value):
             self.cursor.pos = len(self.value)
-        fs = self.font.render(self.value, True, "gray")
-        rect = self.rect
-        if self.value:
-            fs, self.offset, x = self.maybe_scroll_font_surface(
-                self.font,
-                self.value[:self.cursor.pos],
-                fs,
-                rect.width - 24,
-                rect.height,
-            )
-            if x == fs.get_width():
-                x -= 1
-        else:
-            x = 0
+        if not self.value:
             fs = pygame.Surface((1, self.font.size("W")[1]), pygame.SRCALPHA, 32)
+            pygame.draw.line(fs, "orange", (0, 0), (0, fs.get_height()))
+            return fs
+        fs = self.font.render(self.value, True, "gray")
+        fs, self.offset, x = self.maybe_scroll_font_surface(
+            self.font,
+            self.value[:self.cursor.pos],
+            fs,
+            *self.padded_rect.size,
+        )
+        if x == fs.get_width():
+            x -= 1
         pygame.draw.line(fs, "orange", (x, 0), (x, fs.get_height()))
         return fs
 
+    def get_unfocused_font_surface(self):
+        fs = self.font.render(self.value, True, "gray")
+        if self.unfocused_align == self.LEFT:
+            self.offset = 0
+            return fs
+        fs_size = fs.get_size()
+        if fs_size[0] <= self.padded_rect.width:
+            return fs
+        self.offset = fs_size[0] - self.padded_rect.width
+        return fs.subsurface(
+            pygame.Rect((self.offset, 0), (self.padded_rect.width, fs_size[1]))
+        )
+
     def update(self):
         if getattr(self.cursor, "key_callback", None) is None:
             return
@@ -124,20 +157,21 @@ class TextInput(Focusable, Child):
     def draw(self):
         pygame.draw.rect(self.surf, "black", self.rect)
         if self.focused:
-            fs = self.get_font_surface()
+            fs = self.get_focused_font_surface()
         else:
-            fs = self.font.render(self.value, True, "gray")
+            fs = self.get_unfocused_font_surface()
         self.surf.subsurface(self.rect).blit(
-            fs, (16, (self.rect.height - fs.get_height()) // 2)
+            fs, (self.PADDING, (self.rect.height - fs.get_height()) // 2)
         )
         pygame.draw.rect(self.surf, "gray", self.rect, 1)
 
     def pos_from_offset(self, x_offset):
         value = self.value
+        len_value = len(value)
         if x_offset is None:
-            return len(value)
+            return len_value
         a, a_x = 0, 0
-        b, b_x = len(value), self.font.size(value)[0]
+        b, b_x = len_value, self.font.size(value)[0]
         if x_offset <= a_x:
             return a
         elif x_offset >= b_x:
@@ -147,11 +181,11 @@ class TextInput(Focusable, Child):
             c_x = self.font.size(value[:c])[0]
             if c_x < x_offset:
                 a, a_x = c, c_x
-            else:
+            elif c_x > x_offset:
                 b, b_x = c, c_x
-        if abs(a_x - x_offset) < abs(b_x - x_offset):
-            return a
-        return b
+            else:
+                return c
+        return a if abs(a_x - x_offset) < abs(b_x - x_offset) else b
 
     def handle_mousebuttondown(self, ev):
         if ev.button != 1:
@@ -160,7 +194,7 @@ class TextInput(Focusable, Child):
             self.activate()
             with self.check_dirty():
                 self.cursor.pos = self.pos_from_offset(
-                    ev.pos[0] - self.rect.left - 16 + self.offset
+                    ev.pos[0] - self.padded_rect.left + self.offset
                 )
         elif self.focused:
             self.deactivate(True)