yoda: direct drag-drop reorder on the toolbar itself
User feedback: going through Settings to reorder is too indirect. Now each toolbar button is wrapped in dnd_source + dnd_destination so the user can grab an icon in the live toolbar and drop it onto another icon to reorder in-place. The underlying icon button keeps firing its on_press for quick clicks (dnd_source only starts a drag past the default 8 px motion threshold), so regular clicks continue to run the associated Action — no mode-switch needed. Settings retains the ↑↓/add/remove UI as a fallback (discoverability + keyboard-friendly) and for the same drag-drop if the user prefers working from the panel. The config model (Vec<ToolbarAction> + ToolbarReorder message) is already shared, so both paths mutate the same state.
This commit is contained in:
parent
11d435770e
commit
af843d204d
1 changed files with 25 additions and 6 deletions
31
src/app.rs
31
src/app.rs
|
|
@ -6807,25 +6807,44 @@ impl Application for App {
|
||||||
|
|
||||||
// Yoda phase 3: Dolphin-style quick actions toolbar. Items are
|
// Yoda phase 3: Dolphin-style quick actions toolbar. Items are
|
||||||
// rendered from self.config.toolbar (Vec<ToolbarAction>) — the user
|
// rendered from self.config.toolbar (Vec<ToolbarAction>) — the user
|
||||||
// picks the set AND the order via drag-drop in Settings. Dispatch
|
// picks the set AND the order via direct drag-drop on the toolbar.
|
||||||
// goes through Action::message so keybinding and toolbar share the
|
// Short click = action (shared Action::message dispatch); drag past
|
||||||
// same code path.
|
// the default 8px threshold = reorder (ToolbarReorder message).
|
||||||
if !self.config.toolbar.is_empty() {
|
if !self.config.toolbar.is_empty() {
|
||||||
|
use cosmic::iced::clipboard::dnd::DndAction as DndAct;
|
||||||
let clipboard_has = self.clipboard_has_content();
|
let clipboard_has = self.clipboard_has_content();
|
||||||
let buttons: Vec<Element<_>> = self
|
let buttons: Vec<Element<_>> = self
|
||||||
.config
|
.config
|
||||||
.toolbar
|
.toolbar
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| {
|
.map(|a| {
|
||||||
let (icon, label, msg) = toolbar_action_ui(*a);
|
let action = *a;
|
||||||
let enabled = !matches!(a, ToolbarAction::Paste) || clipboard_has;
|
let (icon, label, msg) = toolbar_action_ui(action);
|
||||||
|
let enabled = !matches!(action, ToolbarAction::Paste) || clipboard_has;
|
||||||
let btn = widget::button::icon(widget::icon::from_name(icon).size(16));
|
let btn = widget::button::icon(widget::icon::from_name(icon).size(16));
|
||||||
let btn = if enabled { btn.on_press(msg) } else { btn };
|
let btn = if enabled { btn.on_press(msg) } else { btn };
|
||||||
widget::tooltip(
|
let tooltip = widget::tooltip(
|
||||||
btn,
|
btn,
|
||||||
widget::text::body(label),
|
widget::text::body(label),
|
||||||
widget::tooltip::Position::Bottom,
|
widget::tooltip::Position::Bottom,
|
||||||
|
);
|
||||||
|
let source = widget::dnd_source::<Message, ToolbarActionPayload>(tooltip)
|
||||||
|
.drag_content(move || ToolbarActionPayload(action.to_u8()));
|
||||||
|
widget::dnd_destination(
|
||||||
|
source,
|
||||||
|
vec![std::borrow::Cow::Borrowed(TOOLBAR_MIME)],
|
||||||
)
|
)
|
||||||
|
.data_received_for::<ToolbarActionPayload>(
|
||||||
|
move |payload: Option<ToolbarActionPayload>| {
|
||||||
|
match payload.and_then(|p| ToolbarAction::from_u8(p.0)) {
|
||||||
|
Some(src) if src != action => {
|
||||||
|
Message::ToolbarReorder { src, target: action }
|
||||||
|
}
|
||||||
|
_ => Message::ToolbarReorder { src: action, target: action },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.action(DndAct::Move)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue