# todo:
-# - [ ] textinput place cursor next to the mouse
# - [ ] spinner
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):
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")
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])
)
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:
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)
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,
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)