more organization
This commit is contained in:
+30
-39
@@ -1,24 +1,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from imgui_bundle import imgui
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Callable, List, Dict, Any
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class Command:
|
||||
id: str
|
||||
title: str
|
||||
category: str
|
||||
shortcut: Optional[str] = None
|
||||
description: str = ""
|
||||
id: str
|
||||
title: str
|
||||
category: str
|
||||
shortcut: Optional[str] = None
|
||||
description: str = ""
|
||||
enabled_when: Optional[str] = None
|
||||
action: Optional[Callable] = None
|
||||
|
||||
action: Optional[Callable] = None
|
||||
|
||||
@dataclass
|
||||
class ScoredCommand:
|
||||
command: Command
|
||||
score: float
|
||||
score: float
|
||||
|
||||
|
||||
class CommandRegistry:
|
||||
@@ -70,13 +72,10 @@ def _is_subsequence(query: str, target: str) -> bool:
|
||||
|
||||
def _compute_score(query: str, target: str) -> float:
|
||||
score = 0.0
|
||||
if target.startswith(query):
|
||||
score += 1.0
|
||||
elif _starts_at_word_boundary(query, target):
|
||||
score += 0.5
|
||||
if _is_contiguous(query, target):
|
||||
score += 0.3
|
||||
gaps = _count_gaps(query, target)
|
||||
if target.startswith(query): score += 1.0
|
||||
elif _starts_at_word_boundary(query, target): score += 0.5
|
||||
if _is_contiguous(query, target): score += 0.3
|
||||
gaps = _count_gaps(query, target)
|
||||
score -= 0.1 * gaps
|
||||
return score
|
||||
|
||||
@@ -92,24 +91,23 @@ def _is_contiguous(query: str, target: str) -> bool:
|
||||
|
||||
|
||||
def _count_gaps(query: str, target: str) -> int:
|
||||
qi = 0
|
||||
gaps = 0
|
||||
qi = 0
|
||||
gaps = 0
|
||||
last_match = -1
|
||||
for ti, ch in enumerate(target):
|
||||
if qi < len(query) and ch == query[qi]:
|
||||
if last_match >= 0 and ti - last_match > 1:
|
||||
gaps += ti - last_match - 1
|
||||
if last_match >= 0 and ti - last_match > 1: gaps += ti - last_match - 1
|
||||
last_match = ti
|
||||
qi += 1
|
||||
qi += 1
|
||||
return gaps
|
||||
|
||||
|
||||
def _close_palette(app: Any) -> None:
|
||||
"""Close the palette and reset all per-open state."""
|
||||
app.show_command_palette = False
|
||||
app._command_palette_query = ""
|
||||
app._command_palette_selected = 0
|
||||
app._command_palette_focused = False
|
||||
app.show_command_palette = False
|
||||
app._command_palette_query = ""
|
||||
app._command_palette_selected = 0
|
||||
app._command_palette_focused = False
|
||||
app._command_palette_input_focused = False
|
||||
|
||||
|
||||
@@ -128,19 +126,14 @@ def render_palette_modal(app: Any, commands: List[Command]) -> None:
|
||||
if not getattr(app, "show_command_palette", False):
|
||||
return
|
||||
|
||||
from imgui_bundle import imgui
|
||||
|
||||
viewport = imgui.get_main_viewport()
|
||||
center = viewport.get_center()
|
||||
center = viewport.get_center()
|
||||
imgui.set_next_window_pos((center.x - 300, center.y - 200), imgui.Cond_.always)
|
||||
imgui.set_next_window_size((600, 400), imgui.Cond_.always)
|
||||
|
||||
if not hasattr(app, "_command_palette_query"):
|
||||
app._command_palette_query = ""
|
||||
if not hasattr(app, "_command_palette_selected"):
|
||||
app._command_palette_selected = 0
|
||||
if not hasattr(app, "_command_palette_focused"):
|
||||
app._command_palette_focused = False
|
||||
if not hasattr(app, "_command_palette_query"): app._command_palette_query = ""
|
||||
if not hasattr(app, "_command_palette_selected"): app._command_palette_selected = 0
|
||||
if not hasattr(app, "_command_palette_focused"): app._command_palette_focused = False
|
||||
|
||||
# Set focus on the window + input field ONCE per open.
|
||||
if not app._command_palette_focused:
|
||||
@@ -154,7 +147,7 @@ def render_palette_modal(app: Any, commands: List[Command]) -> None:
|
||||
|
||||
expanded, opened = imgui.begin("Command Palette##manual_slop", True, imgui.WindowFlags_.no_collapse)
|
||||
if not expanded or not opened:
|
||||
app.show_command_palette = False
|
||||
app.show_command_palette = False
|
||||
app._command_palette_focused = False
|
||||
imgui.end()
|
||||
return
|
||||
@@ -167,10 +160,8 @@ def render_palette_modal(app: Any, commands: List[Command]) -> None:
|
||||
# Process Up/Down/Enter BEFORE input_text so we see the keys before the
|
||||
# input field consumes them for cursor movement / text editing.
|
||||
results = fuzzy_match(app._command_palette_query, commands, top_n=20)
|
||||
if results:
|
||||
app._command_palette_selected = max(0, min(app._command_palette_selected, len(results) - 1))
|
||||
else:
|
||||
app._command_palette_selected = 0
|
||||
if results: app._command_palette_selected = max(0, min(app._command_palette_selected, len(results) - 1))
|
||||
else: app._command_palette_selected = 0
|
||||
|
||||
if imgui.is_key_pressed(imgui.Key.down_arrow):
|
||||
if results:
|
||||
@@ -188,7 +179,7 @@ def render_palette_modal(app: Any, commands: List[Command]) -> None:
|
||||
if imgui.begin_child("##results", (0, -1)):
|
||||
for i, scored in enumerate(results):
|
||||
is_selected = (i == app._command_palette_selected)
|
||||
label = f"[{scored.command.category}] {scored.command.title}"
|
||||
label = f"[{scored.command.category}] {scored.command.title}"
|
||||
clicked, _ = imgui.selectable(label, is_selected)
|
||||
if clicked:
|
||||
app._command_palette_selected = i
|
||||
|
||||
Reference in New Issue
Block a user