]> git.mar77i.info Git - zenbook_gui/commitdiff
allow mouse to place cursor
authormar77i <mar77i@protonmail.ch>
Mon, 27 Jan 2025 17:09:08 +0000 (18:09 +0100)
committermar77i <mar77i@protonmail.ch>
Mon, 27 Jan 2025 17:09:08 +0000 (18:09 +0100)
ui/ui.py

index d5b7f1ddb42db0d5ec14de83998f3651fa306526..1f76a4670a1ad124d50fc5b3971bf66ed3ff7eaf 100644 (file)
--- a/ui/ui.py
+++ b/ui/ui.py
@@ -8,7 +8,6 @@ import pygame
 
 
 # todo:
-# - [ ] textinput place cursor next to the mouse
 # - [ ] spinner
 
 
@@ -365,14 +364,33 @@ class Cursor(UIChild, EventMethodDispatcher):
     DELAY_MS = 500
     REPEAT_MS = 100
 
-    def __init__(self, text_input):
+    def __init__(self, text_input, x_offset):
         super().__init__(text_input.parent)
         self.text_input = text_input
         self.old_value = text_input.value
         self.key_callback = None
         self.key = None
         self.repeat_ts = None
-        self.pos = len(text_input.value)
+        self.pos = self.pos_from_offset(x_offset)
+
+    def pos_from_offset(self, x_offset):
+        value = self.text_input.value
+        a, a_x = 0, 0
+        b, b_x = len(value), self.font.size(value)[0]
+        if x_offset <= a_x:
+            return a
+        elif x_offset >= b_x:
+            return b
+        while b - a > 1:
+            c = a + (b - a) // 2
+            c_x = self.font.size(value[:c])[0]
+            if c_x < x_offset:
+                a, a_x = c, c_x
+            else:
+                b, b_x = c, c_x
+        if abs(a_x - x_offset) < abs(b_x - x_offset):
+            return a
+        return b
 
     @contextmanager
     def check_dirty(self):
@@ -522,25 +540,26 @@ class TextInput(UIChild):
         self.callback = callback
         self.value = value
         self.value_filter = value_filter
+        self.offset = 0
 
     @staticmethod
     def maybe_scroll_font_surface(font, value_to_cursor, fs, width, height):
         x = font.size(value_to_cursor)[0]
         fs_size = fs.get_size()
         if fs_size[0] < width:
-            return fs, fs_size, x
+            return fs, 0, x
         offset = 0
         offset_centered = max(x - width // 2, 0)
         offset_right = max(fs_size[0] - width, 0)
         if offset_centered < offset_right:
             offset = offset_centered
             width = min(width, fs_size[0] - offset)
-        elif offset_right:
+        elif offset_right > 0:
             offset = offset_right
         if offset == 0:
-            return fs, fs_size, x
+            return fs, 0, x
         fs = fs.subsurface(pygame.Rect((offset, 0), (width, min(height, fs_size[1]))))
-        return (fs, (width, height), x - offset)
+        return fs, offset, x - offset
 
     def get_font_surface(self):
         fs = self.font.render(self.value, True, "gray")
@@ -550,13 +569,14 @@ class TextInput(UIChild):
         if cursor.pos > len(self.value):
             cursor.pos = len(self.value)
         if self.value:
-            fs, fs_size, x = self.maybe_scroll_font_surface(
+            fs, self.offset, x = self.maybe_scroll_font_surface(
                 self.font,
                 self.value[:cursor.pos],
                 fs,
                 self.rect.width - 24,
                 self.rect.height,
             )
+            fs_size = fs.get_size()
         else:
             x = 1
             fs_size = (x, self.font.size("W")[1])
@@ -574,14 +594,19 @@ class TextInput(UIChild):
         )
         pygame.draw.rect(self.surf, "gray", self.rect, 1)
 
-    def focus(self):
+    def focus(self, x_offset):
         cursor = self.cursor
+        x_offset = x_offset - self.rect.left - 16 + self.offset
         if cursor is not None:
             if cursor.text_input is self:
+                new_pos = cursor.pos_from_offset(x_offset)
+                if new_pos != cursor.pos:
+                    cursor.pos = new_pos
+                    self.dirty = True
                 return
             cursor.text_input.blur(True)
         self.dirty = True
-        self.cursor = Cursor(self)
+        self.cursor = Cursor(self, x_offset)
 
     def blur(self, restore=False):
         if self.cursor is not None:
@@ -596,7 +621,7 @@ class TextInput(UIChild):
     def handle_mousebuttondown(self, ev):
         if ev.button == 1:
             if self.rect.collidepoint(ev.pos):
-                self.focus()
+                self.focus(ev.pos[0])
             elif self.cursor is not None:
                 self.blur(True)
 
@@ -724,11 +749,11 @@ class DropDown(Button):
         self.dropdown_callback = callback
 
     class DropDownMenu(Modal):
-        def __init__(self, sibling):
-            parent = sibling.parent
+        def __init__(self, drop_down):
+            parent = drop_down.parent
             super().__init__(parent)
-            self.callback = sibling.dropdown_callback
-            rect = sibling.rect
+            self.callback = drop_down.dropdown_callback
+            rect = drop_down.rect
             self.buttons = [
                 Button(
                     parent,
@@ -738,7 +763,7 @@ class DropDown(Button):
                     entry,
                     partial(self.choose, i),
                 )
-                for i, entry in enumerate(sibling.entries)
+                for i, entry in enumerate(drop_down.entries)
             ]
             parent.children.extend(self.buttons)