Source code for simvx.ide.widgets.goto_line

"""GotoLineDialog -- small floating dialog for line number navigation (Ctrl+G)."""


from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from simvx.core import Vec2
from simvx.core.ui.core import Control, UIInputEvent
from simvx.core.ui.theme import get_theme

if TYPE_CHECKING:
    from ..state import IDEState

log = logging.getLogger(__name__)

_WIDTH = 300.0
_HEIGHT = 60.0
_PAD = 8.0
_FONT_SIZE = 14.0
_OVERLAY_BG = (0.0, 0.0, 0.0, 0.25)


[docs] class GotoLineDialog(Control): """Small centered dialog for entering a line number. Enter navigates to the line, Escape dismisses. """ def __init__(self, state: IDEState, **kwargs): super().__init__(**kwargs) self._state = state self.visible = False self.z_index = 2000 self._input: str = "" self._max_line: int = 1 self._cursor_blink: float = 0.0 self.size = Vec2(0, 0)
[docs] def show(self, max_line: int = 1): self.visible = True self._input = "" self._max_line = max(max_line, 1) self._cursor_blink = 0.0 self.set_focus()
[docs] def hide(self): self.visible = False self._input = "" self.release_focus()
def _on_gui_input(self, event: UIInputEvent): if not self.visible: return if event.key == "escape" and event.pressed: self.hide() return if event.key == "enter" and event.pressed: self._navigate() return if event.key == "backspace" and event.pressed: if self._input: self._input = self._input[:-1] return if event.char and event.char.isdigit(): self._input += event.char def _navigate(self): if not self._input: self.hide() return try: line = int(self._input) except ValueError: self.hide() return line = max(1, min(line, self._max_line)) - 1 # Convert to 0-indexed path = self._state.active_file self.hide() self._state.goto(path, line, 0)
[docs] def process(self, dt: float): if self.visible: self._cursor_blink += dt if self._cursor_blink > 1.0: self._cursor_blink = 0.0
[docs] def draw(self, renderer): if not self.visible: return theme = get_theme() ss = self._get_parent_size() sw, sh = ss.x, ss.y scale = _FONT_SIZE / 14.0 # Semi-transparent overlay renderer.draw_filled_rect(0, 0, sw, sh, _OVERLAY_BG) # Dialog centered dx = (sw - _WIDTH) / 2 dy = (sh - _HEIGHT) / 2 # Background + border renderer.draw_filled_rect(dx, dy, _WIDTH, _HEIGHT, theme.popup_bg) renderer.draw_rect_coloured(dx, dy, _WIDTH, _HEIGHT, theme.accent) # Label label = f"Go to Line (1-{self._max_line}):" label_y = dy + 6 renderer.draw_text_coloured(label, dx + _PAD, label_y, scale * 0.85, theme.text_dim) # Input field input_y = dy + 26 input_h = 24 renderer.draw_filled_rect(dx + _PAD, input_y, _WIDTH - _PAD * 2, input_h, theme.bg_input) renderer.draw_rect_coloured(dx + _PAD, input_y, _WIDTH - _PAD * 2, input_h, theme.accent) # Input text text_y = input_y + (input_h - _FONT_SIZE) / 2 renderer.draw_text_coloured(self._input, dx + _PAD + 4, text_y, scale, theme.text) # Cursor if self._cursor_blink < 0.5: cx = dx + _PAD + 4 + renderer.text_width(self._input, scale) renderer.draw_line_coloured(cx, input_y + 4, cx, input_y + input_h - 4, theme.text)