Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,46 @@ hidden = [
"top.utility.screenshot",
]

[ui.toolbar.items.order]
# Optional order overrides. Empty lists use the built-in order.
# Known IDs omitted from a non-empty list are appended in the default order.
# Side section ordering uses runtime block representatives; detailed sections
# such as eraser-mode, polygon-sides, and font remain visibility-only IDs.
top_tools = [
"top.tool.select",
"top.tool.pen",
"top.tool.marker",
"top.tool.step-marker",
"top.tool.eraser",
"top.tool.line",
"top.tool.rect",
"top.tool.ellipse",
"top.tool.arrow",
"top.tool.blur",
]
top_controls = [
"top.utility.text",
"top.utility.sticky-note",
"top.utility.screenshot",
"top.utility.clear-canvas",
"top.utility.highlight",
]
side_sections = [
"side.group.colors",
"side.group.presets",
"side.group.thickness",
"side.group.arrow-labels",
"side.group.step-markers",
"side.group.marker-opacity",
"side.group.text-size",
"side.group.actions",
"side.group.boards",
"side.group.pages",
"side.group.step-undo",
"side.group.session",
"side.group.settings",
]

# ───────────────────────────────────────────────────────────────────────────────
# Status Bar Styling
# ───────────────────────────────────────────────────────────────────────────────
Expand Down
5 changes: 3 additions & 2 deletions configurator/src/app/search/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::path::PathBuf;

use crate::models::session::SessionArtifactSummary;
use crate::models::{KeybindingsTabId, SearchQuery, SessionCatalogItem, TabId, UiTabId};
use wayscriber::config::toolbar_item_ids as ids;

#[test]
fn active_search_tab_click_corrects_keybindings_nested_tab() {
Expand Down Expand Up @@ -289,8 +290,8 @@ fn parent_scoped_ui_queries_match_concrete_nested_tabs() {
fn ui_nested_visible_control_labels_match_concrete_nested_tabs() {
let cases = [
("layout mode", UiTabId::Toolbar),
("top.tool.blur", UiTabId::ToolbarVisibility),
("side.group.presets", UiTabId::ToolbarVisibility),
(ids::TOP_TOOL_BLUR.as_str(), UiTabId::ToolbarVisibility),
(ids::SIDE_GROUP_PRESETS.as_str(), UiTabId::ToolbarVisibility),
("status bar position", UiTabId::StatusBar),
("click highlight radius", UiTabId::ClickHighlight),
];
Expand Down
24 changes: 23 additions & 1 deletion configurator/src/app/update/fields.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use iced::Task;
use wayscriber::config::{ToolbarItemId, ToolbarItemOrderGroup};

use crate::messages::Message;
use crate::models::{
Expand All @@ -12,7 +13,6 @@ use crate::models::{
};
#[cfg(feature = "tablet-input")]
use crate::models::{PressureThicknessEditModeOption, PressureThicknessEntryModeOption};
use wayscriber::config::ToolbarItemId;

use super::super::state::{ConfiguratorApp, StatusMessage};

Expand Down Expand Up @@ -180,6 +180,28 @@ impl ConfiguratorApp {
Task::none()
}

pub(super) fn handle_toolbar_item_move_requested(
&mut self,
group: ToolbarItemOrderGroup,
id: ToolbarItemId,
delta: isize,
) -> Task<Message> {
self.status = StatusMessage::idle();
self.draft.move_toolbar_item(group, id, delta);
self.refresh_dirty_flag();
Task::none()
}

pub(super) fn handle_toolbar_item_order_reset(
&mut self,
group: ToolbarItemOrderGroup,
) -> Task<Message> {
self.status = StatusMessage::idle();
self.draft.reset_toolbar_item_order(group);
self.refresh_dirty_flag();
Task::none()
}

pub(super) fn handle_session_storage_mode_changed(
&mut self,
option: SessionStorageModeOption,
Expand Down
4 changes: 4 additions & 0 deletions configurator/src/app/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ impl ConfiguratorApp {
Message::ToolbarItemVisibilityChanged(id, visible) => {
self.handle_toolbar_item_visibility_changed(id, visible)
}
Message::ToolbarItemMoveRequested(group, id, delta) => {
self.handle_toolbar_item_move_requested(group, id, delta)
}
Message::ToolbarItemOrderReset(group) => self.handle_toolbar_item_order_reset(group),
Message::BoardsAddItem => self.handle_boards_add_item(),
Message::BoardsRemoveItem(index) => self.handle_boards_remove_item(index),
Message::BoardsMoveItemUp(index) => self.handle_boards_move_item(index, true),
Expand Down
82 changes: 74 additions & 8 deletions configurator/src/app/view/ui/toolbar.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use iced::widget::{checkbox, column, pick_list, row, scrollable, text};
use std::collections::BTreeSet;

use iced::widget::{button, checkbox, column, pick_list, row, scrollable, text};
use iced::{Element, Length};
use wayscriber::config::{
ResolvedToolbarItems, ToolbarItemCategory, ToolbarItemDefinition, ToolbarItemSurface,
ToolbarItemsConfig, toolbar_item_definitions,
ResolvedToolbarItems, ToolbarItemCategory, ToolbarItemDefinition, ToolbarItemOrderGroup,
ToolbarItemSurface, ToolbarItemsConfig, toolbar_item_definitions, toolbar_item_order_group,
};

use crate::app::scroll::CONTENT_SCROLL_ID;
Expand Down Expand Up @@ -255,7 +257,7 @@ fn toolbar_item_visibility_section<'a>(
);
}

for definition in toolbar_item_definitions() {
for definition in toolbar_item_definitions_for_display(&resolved) {
if current_surface != Some(definition.surface) {
current_surface = Some(definition.surface);
current_category = None;
Expand Down Expand Up @@ -287,17 +289,81 @@ fn toolbar_item_visibility_row<'a>(
"default: {}",
visibility_override_label(!defaults.is_hidden(id))
);
let order_group = configurator_order_group(definition);
let order = order_group.and_then(|group| {
let index = resolved.order.index_of(group, id)?;
let len = resolved.order.ordered_ids(group).len();
Some((group, index, index > 0, index + 1 < len))
});

row![
let mut cells = row![
checkbox(visible)
.label(definition.label)
.on_toggle(move |value| Message::ToolbarItemVisibilityChanged(id, value)),
text(definition.id.as_str()).size(12).width(Length::Fill),
text(default).size(12),
]
.spacing(12)
.align_y(iced::Alignment::Center)
.into()
.align_y(iced::Alignment::Center);
if let Some((group, _, can_move_up, can_move_down)) = order {
let up = if can_move_up {
button(text("^")).on_press(Message::ToolbarItemMoveRequested(group, id, -1))
} else {
button(text("^"))
};
let down = if can_move_down {
button(text("v")).on_press(Message::ToolbarItemMoveRequested(group, id, 1))
} else {
button(text("v"))
};
cells = cells
.push(up)
.push(down)
.push(button(text("Reset")).on_press(Message::ToolbarItemOrderReset(group)));
}
cells.push(text(default).size(12)).into()
}

fn toolbar_item_definitions_for_display(
resolved: &ResolvedToolbarItems,
) -> Vec<&'static ToolbarItemDefinition> {
let mut result = Vec::new();
let mut emitted = BTreeSet::new();
let mut emitted_groups = BTreeSet::new();

for definition in toolbar_item_definitions() {
if let Some(group) = configurator_order_group(definition) {
if emitted_groups.insert(group) {
for id in resolved.order.ordered_ids(group) {
if let Some(ordered_definition) = toolbar_item_definitions()
.iter()
.find(|candidate| candidate.id == *id)
&& configurator_order_group(ordered_definition) == Some(group)
{
result.push(ordered_definition);
emitted.insert(ordered_definition.id);
}
}
}
if emitted.contains(&definition.id) {
continue;
}
}
result.push(definition);
emitted.insert(definition.id);
}

result
}

fn configurator_order_group(definition: &ToolbarItemDefinition) -> Option<ToolbarItemOrderGroup> {
let group = toolbar_item_order_group(definition)?;
matches!(
group,
ToolbarItemOrderGroup::TopTools
| ToolbarItemOrderGroup::TopControls
| ToolbarItemOrderGroup::SideSections
)
.then_some(group)
}

fn visibility_override_label(visible: bool) -> &'static str {
Expand Down
4 changes: 3 additions & 1 deletion configurator/src/messages.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::PathBuf;
use std::sync::Arc;

use wayscriber::config::{Config, ToolbarItemId};
use wayscriber::config::{Config, ToolbarItemId, ToolbarItemOrderGroup};

use crate::models::{
BoardBackgroundOption, BoardItemTextField, BoardItemToggleField, ColorMode, ColorPickerId,
Expand Down Expand Up @@ -74,6 +74,8 @@ pub enum Message {
ToolbarOverrideModeChanged(ToolbarLayoutModeOption),
ToolbarOverrideChanged(ToolbarOverrideField, OverrideOption),
ToolbarItemVisibilityChanged(ToolbarItemId, bool),
ToolbarItemMoveRequested(ToolbarItemOrderGroup, ToolbarItemId, isize),
ToolbarItemOrderReset(ToolbarItemOrderGroup),
BoardsAddItem,
BoardsRemoveItem(usize),
BoardsMoveItemUp(usize),
Expand Down
15 changes: 14 additions & 1 deletion configurator/src/models/config/setters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::super::fields::{
ToolbarOverrideField, TripletField,
};
use super::draft::ConfigDraft;
use wayscriber::config::ToolbarItemId;
use wayscriber::config::{ToolbarItemId, ToolbarItemOrderGroup};

impl ConfigDraft {
pub fn apply_toolbar_layout_mode(&mut self, mode: ToolbarLayoutModeOption) {
Expand Down Expand Up @@ -36,6 +36,19 @@ impl ConfigDraft {
self.ui_toolbar_items.set_hidden(id, !visible);
}

pub fn move_toolbar_item(
&mut self,
group: ToolbarItemOrderGroup,
id: ToolbarItemId,
delta: isize,
) {
self.ui_toolbar_items.move_item_by(group, id, delta);
}

pub fn reset_toolbar_item_order(&mut self, group: ToolbarItemOrderGroup) {
self.ui_toolbar_items.reset_known_order_to_defaults(group);
}

pub fn set_mouse_drag_tool(
&mut self,
button: DragMouseButton,
Expand Down
45 changes: 38 additions & 7 deletions configurator/src/models/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use super::{ConfigDraft, RenderProfileSelectionOption};
use wayscriber::config::{
ColorSpec, Config, PdfFitMode, PdfLabelContentMode, PdfLabelPosition, PdfOrientation,
PdfPageSize, PdfTransparentBackground, PresetToolStatesConfig, RenderColorMappingConfig,
RenderProfileConfig, RenderProfileExportMode, ToolPresetConfig, ToolbarItemsConfig,
XdgFocusLossBehavior,
RenderProfileConfig, RenderProfileExportMode, ToolPresetConfig, ToolbarItemOrderConfig,
ToolbarItemOrderGroup, ToolbarItemsConfig, XdgFocusLossBehavior, toolbar_item_ids as ids,
};
use wayscriber::input::{DragTool, PerToolDrawingSettings, Tool};

Expand Down Expand Up @@ -69,22 +69,53 @@ fn config_draft_round_trips_toolbar_item_visibility_preserving_unknown_ids() {
config.ui.toolbar.items = ToolbarItemsConfig {
hidden: vec![
"future.toolbar.item".to_string(),
"side.actions.undo-all".to_string(),
"side.actions.undo-all".to_string(),
ids::SIDE_ACTIONS_UNDO_ALL.as_str().to_string(),
ids::SIDE_ACTIONS_UNDO_ALL.as_str().to_string(),
],
order: ToolbarItemOrderConfig::default(),
};

let mut draft = ConfigDraft::from_config(&config);
draft.set_toolbar_item_visible("side.actions.undo-all".parse().expect("known id"), true);
draft.set_toolbar_item_visible("top.tool.pen".parse().expect("known id"), false);
draft.set_toolbar_item_visible(ids::SIDE_ACTIONS_UNDO_ALL, true);
draft.set_toolbar_item_visible(ids::TOP_TOOL_PEN, false);

let round_trip = draft
.to_config(&config)
.expect("expected config to round trip");

assert_eq!(
round_trip.ui.toolbar.items.hidden,
vec!["future.toolbar.item", "top.tool.pen"]
vec![
"future.toolbar.item".to_string(),
ids::TOP_TOOL_PEN.as_str().to_string()
]
);
}

#[test]
fn config_draft_round_trips_toolbar_item_order_preserving_unknown_ids() {
let mut config = Config::default();
config.ui.toolbar.items.order.top_tools = vec![
"future.toolbar.item".to_string(),
ids::TOP_TOOL_PEN.as_str().to_string(),
ids::TOP_TOOL_SELECT.as_str().to_string(),
];

let mut draft = ConfigDraft::from_config(&config);
draft.move_toolbar_item(ToolbarItemOrderGroup::TopTools, ids::TOP_TOOL_PEN, 1);

let round_trip = draft
.to_config(&config)
.expect("expected config to round trip");

assert!(
round_trip
.ui
.toolbar
.items
.order
.top_tools
.contains(&"future.toolbar.item".to_string())
);
}

Expand Down
31 changes: 31 additions & 0 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,35 @@ hidden = [
"side.actions.undo-all",
"side.group.presets",
]

[ui.toolbar.items.order]
# Optional order overrides. Empty lists use the built-in order.
# Known IDs omitted from a non-empty list append in the default order.
# Side section ordering uses runtime block representatives; detailed sections
# such as eraser-mode, polygon-sides, and font remain visibility-only IDs.
top_tools = [
"top.tool.select",
"top.tool.pen",
"top.tool.marker",
"top.tool.step-marker",
"top.tool.eraser",
]
top_controls = [
"top.utility.text",
"top.utility.sticky-note",
"top.utility.screenshot",
"top.utility.clear-canvas",
"top.utility.highlight",
]
side_sections = [
"side.group.colors",
"side.group.presets",
"side.group.thickness",
"side.group.actions",
"side.group.pages",
"side.group.boards",
"side.group.settings",
]
```

**Behavior:**
Expand All @@ -591,6 +620,8 @@ hidden = [
- **Force inline**: `force_inline` (or `WAYSCRIBER_FORCE_INLINE_TOOLBARS`) skips layer-shell toolbars.
- **Pinned**: `top_pinned`/`side_pinned` control whether each toolbar opens on startup.
- **Hidden items**: `ui.toolbar.items.hidden` removes known toolbar buttons/sections from sizing, drawing, and hit testing while preserving unknown future IDs.
- **Item order**: `ui.toolbar.items.order.top_tools`, `top_controls`, and `side_sections` reorder supported toolbar items. `side_sections` orders runtime block representatives; `side.group.eraser-mode`, `side.group.polygon-sides`, and `side.group.font` can be hidden individually but are not independently orderable. Unknown future IDs and wrong-group IDs are ignored at runtime but preserved across saves.
- **Live customization**: the overlay Customize tab supports show/hide, move up/down, and drag reorder for supported groups. The configurator supports the same saved order with up/down controls.
- **Screenshot toolbar button**: `top.utility.screenshot` is hidden by default; remove it from `ui.toolbar.items.hidden` or enable it in the configurator/overlay customization to show it.

**Defaults:** all set as above.
Expand Down
Loading
Loading