yoda: phase 3 — drag-drop toolbar editor in Settings

Migrates the config model from the phase-2 bag-of-bools (ToolbarItems)
to an ordered Vec<ToolbarAction> so the user can pick BOTH the set of
buttons AND their order in the toolbar.

Config (config.rs):
- new ToolbarAction enum with 11 variants (LocationUp, Reload,
  NewFolder, NewFile, Rename, Delete, Cut, Copy, Paste,
  ToggleShowHidden, OpenTerminal) + to_u8/from_u8 for DnD payload
- Config.toolbar: Vec<ToolbarAction>, default = default_toolbar()
  (NewFolder, Rename, Delete, Cut, Copy, Paste — same 6 as phase 2)

Rendering (view()):
- iterate self.config.toolbar in order and emit a tooltip'd icon button
  per entry via the new toolbar_action_ui(action) helper shared with
  the Settings page. Paste stays disabled when clipboard empty.
- No hardcoded groups or auto-dividers anymore — order is 100% user.

Settings page (toolbar_settings_section):
- two stacked lists:
  * 'Toolbar': currently-enabled actions in their Vec order. Each row
    is wrapped in dnd_source (drags a ToolbarActionPayload carrying
    the enum discriminant) + dnd_destination (accepts drops from other
    rows, fires Message::ToolbarReorder { src, target } to move src
    before target in the Vec). A list-drag-handle icon + a minus button
    (ToolbarRemove) per row.
  * 'Available': actions not yet enabled, each with a plus button
    (ToolbarAdd) that pushes to the end of the Vec.
- 'Reset to defaults' button at the bottom (ToolbarReset).

DnD infra (app.rs top):
- TOOLBAR_MIME constant: 'application/x-cosmic-files-toolbar-action'
- ToolbarActionPayload(u8) with AsMimeTypes + AllowedMimeTypes +
  TryFrom<(Vec<u8>, String)> impls — single-byte wire format matching
  the enum discriminant.

Messages:
- ToolbarAdd(ToolbarAction) — append to toolbar vec if absent
- ToolbarRemove(ToolbarAction)
- ToolbarReorder { src, target } — remove src, reinsert before target
- ToolbarReset — restore default_toolbar()

i18n (en + fr):
- new keys: toolbar-available, toolbar-empty-hint, toolbar-reset

Migration: existing installs with a phase-2 ToolbarItems struct in
their config will error at load time (different shape); cosmic_config
falls back to Self::default() which gives the phase-2 minimal-6 set —
a safe reset rather than a broken partial read.
This commit is contained in:
Lionel DARNIS 2026-04-24 08:13:30 +02:00
parent 33a5c8ff99
commit 1cf17dcde8
4 changed files with 356 additions and 182 deletions

View file

@ -172,10 +172,11 @@ pub struct Config {
pub show_details: bool,
pub show_recents: bool,
pub tab: TabConfig,
/// Yoda: Dolphin-style quick actions toolbar under the tab bar.
/// Each bool toggles one button; order in the UI is fixed (logical
/// grouping file-ops then clipboard then view toggles).
pub toolbar: ToolbarItems,
/// Yoda phase 3: Dolphin-style quick actions toolbar. An ordered list
/// of enabled buttons — position in the vec drives the toolbar order.
/// Reorder in Settings via drag-drop; items not in the vec are
/// hidden. Default = the minimal-6 set from phase 1.
pub toolbar: Vec<ToolbarAction>,
pub type_to_search: TypeToSearch,
}
@ -240,46 +241,95 @@ impl Default for Config {
show_details: false,
show_recents: true,
tab: TabConfig::default(),
toolbar: ToolbarItems::default(),
toolbar: default_toolbar(),
type_to_search: TypeToSearch::Recursive,
}
}
}
/// Yoda: visibility toggles for each quick-action toolbar button.
/// Default = the original "minimal 6" set (new_folder, rename, delete,
/// cut, copy, paste). Other items default to false so users opt in.
#[derive(Clone, Copy, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ToolbarItems {
pub new_folder: bool,
pub new_file: bool,
pub rename: bool,
pub delete: bool,
pub cut: bool,
pub copy: bool,
pub paste: bool,
pub reload: bool,
pub toggle_show_hidden: bool,
pub open_terminal: bool,
pub location_up: bool,
/// Yoda phase 3: ordered enum of quick-action toolbar buttons.
/// The Config stores `Vec<ToolbarAction>` so the user can pick BOTH
/// visibility (just include/exclude the variant) AND order (position in
/// the vec). Drag-drop reorder in the Settings page moves items around.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum ToolbarAction {
LocationUp,
Reload,
NewFolder,
NewFile,
Rename,
Delete,
Cut,
Copy,
Paste,
ToggleShowHidden,
OpenTerminal,
}
impl Default for ToolbarItems {
fn default() -> Self {
Self {
new_folder: true,
new_file: false,
rename: true,
delete: true,
cut: true,
copy: true,
paste: true,
reload: false,
toggle_show_hidden: false,
open_terminal: false,
location_up: false,
impl ToolbarAction {
/// Stable list of every supported action. Ordered roughly by logical
/// grouping (location → create/edit → clipboard → view/misc) so that
/// the default enabled set follows a sensible shape and the Settings
/// row for a not-yet-enabled action lands in a predictable spot.
pub const ALL: &'static [Self] = &[
Self::LocationUp,
Self::Reload,
Self::NewFolder,
Self::NewFile,
Self::Rename,
Self::Delete,
Self::Cut,
Self::Copy,
Self::Paste,
Self::ToggleShowHidden,
Self::OpenTerminal,
];
/// u8 discriminant used to carry the action over a DnD mime payload.
pub const fn to_u8(self) -> u8 {
match self {
Self::LocationUp => 0,
Self::Reload => 1,
Self::NewFolder => 2,
Self::NewFile => 3,
Self::Rename => 4,
Self::Delete => 5,
Self::Cut => 6,
Self::Copy => 7,
Self::Paste => 8,
Self::ToggleShowHidden => 9,
Self::OpenTerminal => 10,
}
}
pub const fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::LocationUp),
1 => Some(Self::Reload),
2 => Some(Self::NewFolder),
3 => Some(Self::NewFile),
4 => Some(Self::Rename),
5 => Some(Self::Delete),
6 => Some(Self::Cut),
7 => Some(Self::Copy),
8 => Some(Self::Paste),
9 => Some(Self::ToggleShowHidden),
10 => Some(Self::OpenTerminal),
_ => None,
}
}
}
/// Default set shown on a fresh install — same "minimal 6" as phase 1/2.
pub fn default_toolbar() -> Vec<ToolbarAction> {
vec![
ToolbarAction::NewFolder,
ToolbarAction::Rename,
ToolbarAction::Delete,
ToolbarAction::Cut,
ToolbarAction::Copy,
ToolbarAction::Paste,
]
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]