fix(styles): make double-click inline rename actually fire

The list row wrapped a `button` inside the `mouse_area`. The button
captured the press, so the parent `mouse_area` never saw the second
click and `on_double_click` never fired — double-clicking a style name
did nothing.

Drop the inner button: the row is now a plain `mouse_area` carrying both
`on_press` (select) and `on_double_click` (start rename), with selection
shown via the container background. Focus the rename field on start so
the user can type immediately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-10 15:10:09 +03:00
commit 65a25033bc
3 changed files with 47 additions and 42 deletions

View file

@ -6170,7 +6170,9 @@ impl OpenCADStudio {
// ── Shared inline rename (all style managers) ───────────────── // ── Shared inline rename (all style managers) ─────────────────
Message::StyleRenameStart(kind, name) => { Message::StyleRenameStart(kind, name) => {
self.style_rename_start(kind, name); self.style_rename_start(kind, name);
Task::none() // Focus the freshly-shown rename field so the user can type
// immediately after the double click.
iced::widget::operation::focus(crate::ui::style_list::rename_input_id())
} }
Message::StyleRenameEdit(s) => { Message::StyleRenameEdit(s) => {
self.style_rename_buf = s; self.style_rename_buf = s;

View file

@ -1,28 +1,51 @@
//! Shared style-manager list row. //! Shared style-manager list row.
//! //!
//! Every style manager renders the same left-hand list of style names where a //! Every style manager renders the same left-hand list of style names where a
//! single click selects and a double click renames inline. Only the row's //! single click selects and a double click renames inline.
//! button style differs per manager, so it is passed in. //!
//! The whole row is a `mouse_area` (not a `button`): an inner interactive
//! widget would capture the press and the parent `mouse_area` would never see
//! the second click, so `on_double_click` would never fire. `mouse_area`
//! carries both `on_press` (select) and `on_double_click` (rename) itself.
use crate::app::{Message, StyleKind}; use crate::app::{Message, StyleKind};
use iced::widget::button::{Status, Style}; use iced::widget::{container, mouse_area, text, text_input};
use iced::widget::{button, mouse_area, text, text_input}; use iced::{Background, Color, Element, Fill, Theme};
use iced::{Element, Fill, Theme};
/// Shared id for the inline rename field, so the rename-start handler can focus
/// it the moment the row turns editable.
pub fn rename_input_id() -> iced::widget::Id {
iced::widget::Id::new("style-rename-input")
}
const TEXT: Color = Color {
r: 0.88,
g: 0.88,
b: 0.88,
a: 1.0,
};
const ACTIVE: Color = Color {
r: 0.20,
g: 0.40,
b: 0.70,
a: 1.0,
};
/// One row of the style list. Renders an editable `text_input` when `name` is /// One row of the style list. Renders an editable `text_input` when `name` is
/// the style being renamed (`rename_active`), otherwise a selectable button /// the style being renamed (`rename_active`), otherwise a selectable row whose
/// wrapped in a `mouse_area` whose double click starts the rename. /// double click starts the rename.
pub fn item<'a>( pub fn item<'a>(
name: &str, name: &str,
label: String, label: String,
is_selected: bool,
kind: StyleKind, kind: StyleKind,
on_select: Message, on_select: Message,
rename_active: Option<&str>, rename_active: Option<&str>,
rename_buf: &'a str, rename_buf: &'a str,
style: impl Fn(&Theme, Status) -> Style + 'a,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
if rename_active == Some(name) { if rename_active == Some(name) {
text_input("", rename_buf) text_input("", rename_buf)
.id(iced::widget::Id::new("style-rename-input"))
.on_input(Message::StyleRenameEdit) .on_input(Message::StyleRenameEdit)
.on_submit(Message::StyleRenameCommit(kind)) .on_submit(Message::StyleRenameCommit(kind))
.size(11) .size(11)
@ -30,14 +53,17 @@ pub fn item<'a>(
.width(Fill) .width(Fill)
.into() .into()
} else { } else {
mouse_area( let row = container(text(label).size(11).color(TEXT))
button(text(label).size(11)) .padding([4, 8])
.on_press(on_select) .width(Fill)
.style(style) .style(move |_: &Theme| container::Style {
.padding([4, 8]) background: is_selected.then_some(Background::Color(ACTIVE)),
.width(Fill), text_color: Some(TEXT),
) ..Default::default()
.on_double_click(Message::StyleRenameStart(kind, name.to_string())) });
.into() mouse_area(row)
.on_press(on_select)
.on_double_click(Message::StyleRenameStart(kind, name.to_string()))
.into()
} }
} }

View file

@ -48,12 +48,6 @@ const ACCENT: Color = Color {
b: 0.85, b: 0.85,
a: 1.0, a: 1.0,
}; };
const ACTIVE: Color = Color {
r: 0.20,
g: 0.40,
b: 0.70,
a: 1.0,
};
const LIST: Color = Color { const LIST: Color = Color {
r: 0.12, r: 0.12,
g: 0.12, g: 0.12,
@ -128,11 +122,11 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> {
crate::ui::style_list::item( crate::ui::style_list::item(
name, name,
label, label,
is_sel,
s.kind, s.kind,
(s.on_select)(name.clone()), (s.on_select)(name.clone()),
s.rename_active, s.rename_active,
s.rename_buf, s.rename_buf,
list_item(is_sel),
) )
}) })
.collect(); .collect();
@ -222,23 +216,6 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, Status) -> Style {
} }
} }
fn list_item(active: bool) -> impl Fn(&Theme, Status) -> Style {
move |_: &Theme, st| Style {
background: Some(Background::Color(match (active, st) {
(true, _) => ACTIVE,
(false, Status::Hovered | Status::Pressed) => Color {
r: 0.26,
g: 0.26,
b: 0.26,
a: 1.0,
},
_ => Color::TRANSPARENT,
})),
text_color: TEXT,
..Default::default()
}
}
fn hdivider<'a>() -> Element<'a, Message> { fn hdivider<'a>() -> Element<'a, Message> {
container(Space::new().width(Fill).height(1)) container(Space::new().width(Fill).height(1))
.width(Fill) .width(Fill)