W21 Phase 2: drag-selectable chat messages (per-message)
Add `SelectableRichText`, a custom iced widget that mirrors `rich_text` (linkified spans + A13 link clicks) and adds per-message drag selection, Ctrl/Cmd+C copy, and Ctrl/Cmd+A select-all. Swap it in for the chat body element; the chat row and attachment layout are unchanged. Selection offsets are paragraph-global byte offsets (matching cosmic-text's hit_test), which equals a single global range because sanitize_chat keeps every message on one logical line. Pure seam `selected_substring` / `select_all` is unit-tested incl. unicode/emoji byte boundaries. Highlight quads are computed from public Paragraph primitives, falling back to a whole-message span_bounds union if sub-range rects can't be derived. Only one message holds a selection at a time: each widget clears its own selection on a left-press that lands outside its bounds. Right-click menu (Part B) intentionally deferred — native Ctrl/Cmd+C/A is the path. Implemented by Codex on branch, reviewed/committed by Claude. 464 lib tests, clippy --all-targets clean, release green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+4
-2
@@ -12,11 +12,12 @@ use crate::hotkeys::{format_binding, HotkeyAction, HotkeyContext, KeyBinding};
|
||||
use crate::presence::PresenceMode;
|
||||
use crate::theme::{AppTheme, Palette};
|
||||
use crate::widget::context_input::{context_input, locked_value};
|
||||
use crate::widget::selectable_text::selectable_rich_text;
|
||||
|
||||
use iced::widget::{
|
||||
container, column, row, text, button, scrollable, slider, checkbox, pick_list,
|
||||
radio, tooltip, progress_bar, canvas, Canvas, Column, stack, mouse_area,
|
||||
rich_text, span, responsive,
|
||||
span, responsive,
|
||||
};
|
||||
use iced::widget::text_input;
|
||||
use iced::widget::canvas::{Action, Frame, Geometry, Path, Program};
|
||||
@@ -4670,8 +4671,9 @@ fn view(state: &AppState) -> Element<'_, AppMessage> {
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let body = rich_text(spans)
|
||||
let body = selectable_rich_text(spans)
|
||||
.on_link_click(AppMessage::OpenUrl)
|
||||
.selection_color(color_blue)
|
||||
.width(iced::Length::Fill);
|
||||
// Small avatar keyed on the sender's id (falls back to name); the
|
||||
// " (You)" suffix on our own echoes is stripped for clean initials.
|
||||
|
||||
Reference in New Issue
Block a user