Feat: text selection using mouse

This commit is contained in:
2026-02-25 23:20:42 +01:00
parent 03c8187359
commit aa607a78d8
5 changed files with 125 additions and 6 deletions

View File

@@ -26,6 +26,12 @@ pub fn handle_mouse(ctx: &mut InputContext, mouse: MouseEvent, term: Rect) {
match kind {
MouseEventKind::Down(MouseButton::Left) => handle_click(ctx, col, row, term),
MouseEventKind::Drag(MouseButton::Left) | MouseEventKind::Moved => {
handle_editor_drag(ctx, col, row, term);
}
MouseEventKind::Up(MouseButton::Left) => {
ctx.app.editor_ctx.mouse_selecting = false;
}
MouseEventKind::ScrollUp => handle_scroll(ctx, col, row, term, true),
MouseEventKind::ScrollDown => handle_scroll(ctx, col, row, term, false),
_ => {}
@@ -59,6 +65,55 @@ fn contains(area: Rect, col: u16, row: u16) -> bool {
col >= area.x && col < area.x + area.width && row >= area.y && row < area.y + area.height
}
fn handle_editor_drag(ctx: &mut InputContext, col: u16, row: u16, term: Rect) {
if ctx.app.editor_ctx.mouse_selecting {
handle_editor_mouse(ctx, col, row, term, true);
}
}
fn handle_editor_mouse(ctx: &mut InputContext, col: u16, row: u16, term: Rect, dragging: bool) {
// Reconstruct editor area (mirrors render_modal_editor / ModalFrame::render_centered)
let width = (term.width * 80 / 100).max(40);
let height = (term.height * 60 / 100).max(10);
let modal_w = width.min(term.width.saturating_sub(4));
let modal_h = height.min(term.height.saturating_sub(4));
let mx = term.x + (term.width.saturating_sub(modal_w)) / 2;
let my = term.y + (term.height.saturating_sub(modal_h)) / 2;
// inner = area inside 1-cell border
let inner_x = mx + 1;
let inner_y = my + 1;
let inner_w = modal_w.saturating_sub(2);
let inner_h = modal_h.saturating_sub(2);
let show_search = ctx.app.editor_ctx.editor.search_active()
|| !ctx.app.editor_ctx.editor.search_query().is_empty();
let reserved = 1 + if show_search { 1 } else { 0 };
let editor_y = inner_y + if show_search { 1 } else { 0 };
let editor_h = inner_h.saturating_sub(reserved);
if col < inner_x || col >= inner_x + inner_w || row < editor_y || row >= editor_y + editor_h {
return;
}
let scroll = ctx.app.editor_ctx.editor.scroll_offset();
let text_row = (row - editor_y) + scroll;
let text_col = col - inner_x;
if dragging {
if !ctx.app.editor_ctx.editor.is_selecting() {
ctx.app.editor_ctx.editor.start_selection();
}
} else {
ctx.app.editor_ctx.mouse_selecting = true;
ctx.app.editor_ctx.editor.cancel_selection();
}
ctx.app
.editor_ctx
.editor
.move_cursor_to(text_row, text_col);
}
fn handle_click(ctx: &mut InputContext, col: u16, row: u16, term: Rect) {
// Sticky minimap intercepts all clicks
if matches!(ctx.app.ui.minimap, MinimapMode::Sticky) {
@@ -745,8 +800,11 @@ fn handle_engine_click(ctx: &mut InputContext, col: u16, row: u16, area: Rect) {
fn handle_modal_click(ctx: &mut InputContext, col: u16, row: u16, term: Rect) {
match &ctx.app.ui.modal {
Modal::Editor | Modal::Preview => {
// Don't dismiss editor/preview on click
Modal::Editor => {
handle_editor_mouse(ctx, col, row, term, false);
}
Modal::Preview => {
// Don't dismiss preview on click
}
Modal::Confirm { .. } => {
handle_confirm_click(ctx, col, row, term);