Skip to content

Commit 86bce2d

Browse files
authored
Merge pull request #249 from devmobasa/configurable-toolbars
Configurable toolbars
2 parents 80d3215 + b744877 commit 86bce2d

77 files changed

Lines changed: 3460 additions & 832 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config.example.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,13 @@ side_offset_x = 0.0
420420
# Force inline toolbars even when layer-shell is available
421421
force_inline = false
422422

423+
[ui.toolbar.items]
424+
# Checked in the configurator means shown; IDs listed here are hidden.
425+
# The screenshot toolbar button is hidden by default.
426+
hidden = [
427+
"top.utility.screenshot",
428+
]
429+
423430
# ───────────────────────────────────────────────────────────────────────────────
424431
# Status Bar Styling
425432
# ───────────────────────────────────────────────────────────────────────────────

configurator/src/app/search/summary.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::app::state::ConfiguratorApp;
22
use crate::models::{KeybindingsTabId, SearchQuery, TabId, UiTabId};
3+
use wayscriber::config::toolbar_item_definitions;
34

45
use super::terms::*;
56
use super::types::{AppSearchSummary, SearchArea, TabSearchSummary};
@@ -178,6 +179,7 @@ fn ui_matches(query: &SearchQuery, summary: &mut TabSearchSummary) {
178179
.collect::<Vec<_>>();
179180
if query.matches_parts_scoped_to_tab(TabId::Ui, identity_parts.iter().copied())
180181
|| query.matches_parts(full_parts.iter().copied())
182+
|| (tab == UiTabId::ToolbarVisibility && toolbar_item_matches(query))
181183
|| (query.matches_any_raw_text(field_terms)
182184
&& query.matches_parts_scoped_to_tab(TabId::Ui, full_parts.iter().copied()))
183185
{
@@ -186,6 +188,26 @@ fn ui_matches(query: &SearchQuery, summary: &mut TabSearchSummary) {
186188
}
187189
}
188190

191+
fn toolbar_item_matches(query: &SearchQuery) -> bool {
192+
toolbar_item_definitions().iter().any(|definition| {
193+
query.matches_parts([
194+
"toolbar item",
195+
definition.label,
196+
definition.id.as_str(),
197+
definition.group.map_or("", |group| group.as_str()),
198+
]) || query.matches_parts_scoped_to_tab(
199+
TabId::Ui,
200+
[
201+
"toolbar visibility",
202+
"toolbar item",
203+
definition.label,
204+
definition.id.as_str(),
205+
definition.group.map_or("", |group| group.as_str()),
206+
],
207+
)
208+
})
209+
}
210+
189211
fn board_matches(app: &ConfiguratorApp, query: &SearchQuery, summary: &mut TabSearchSummary) {
190212
add_area_if(
191213
query,

configurator/src/app/search/terms.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub(super) fn tab_scope_aliases(tab: TabId) -> &'static [&'static str] {
4747
pub(super) fn ui_tab_aliases(tab: UiTabId) -> &'static [&'static str] {
4848
match tab {
4949
UiTabId::Toolbar => &["tools", "palette", "drawer"],
50+
UiTabId::ToolbarVisibility => &["toolbar items", "toolbar buttons", "show hide toolbar"],
5051
UiTabId::StatusBar => &["status", "badge", "indicator"],
5152
UiTabId::HelpOverlay => &["help", "quick help", "hints"],
5253
UiTabId::ClickHighlight => &["click", "cursor", "highlight"],
@@ -57,6 +58,7 @@ pub(super) fn ui_tab_aliases(tab: UiTabId) -> &'static [&'static str] {
5758
pub(super) fn ui_tab_terms(tab: UiTabId) -> &'static [&'static str] {
5859
match tab {
5960
UiTabId::Toolbar => UI_TOOLBAR_TERMS,
61+
UiTabId::ToolbarVisibility => UI_TOOLBAR_VISIBILITY_TERMS,
6062
UiTabId::StatusBar => UI_STATUS_BAR_TERMS,
6163
UiTabId::HelpOverlay => UI_HELP_OVERLAY_TERMS,
6264
UiTabId::ClickHighlight => UI_CLICK_HIGHLIGHT_TERMS,
@@ -193,6 +195,29 @@ pub(super) const UI_TOOLBAR_TERMS: &[&str] = &[
193195
"side offset x",
194196
"side offset y",
195197
];
198+
pub(super) const UI_TOOLBAR_VISIBILITY_TERMS: &[&str] = &[
199+
"toolbar visibility",
200+
"toolbar items",
201+
"items",
202+
"checked items are shown",
203+
"hidden item overrides",
204+
"hide toolbar buttons",
205+
"show toolbar buttons",
206+
"visible toolbar buttons",
207+
"top toolbar",
208+
"side toolbar",
209+
"toolbar controls",
210+
"tools",
211+
"utilities",
212+
"sections",
213+
"actions",
214+
"pages",
215+
"boards",
216+
"presets",
217+
"settings",
218+
"sessions",
219+
"tool options",
220+
];
196221
pub(super) const UI_STATUS_BAR_TERMS: &[&str] = &[
197222
"status bar",
198223
"show status bar",

configurator/src/app/search/tests.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,17 @@ fn ui_nested_alias_matches_concrete_nested_tab() {
259259
#[test]
260260
fn parent_scoped_ui_queries_match_concrete_nested_tabs() {
261261
let cases = [
262-
("ui toolbar", UiTabId::Toolbar),
263-
("ui layout mode", UiTabId::Toolbar),
264-
("interface presenter", UiTabId::PresenterMode),
265-
("interface status bar position", UiTabId::StatusBar),
262+
(
263+
"ui toolbar",
264+
&[UiTabId::Toolbar, UiTabId::ToolbarVisibility][..],
265+
),
266+
("ui layout mode", &[UiTabId::Toolbar][..]),
267+
("ui toolbar blur", &[UiTabId::ToolbarVisibility][..]),
268+
("interface presenter", &[UiTabId::PresenterMode][..]),
269+
("interface status bar position", &[UiTabId::StatusBar][..]),
266270
];
267271

268-
for (query, expected_tab) in cases {
272+
for (query, expected_tabs) in cases {
269273
let (mut app, _task) = ConfiguratorApp::new_app();
270274
app.search_query = SearchQuery::new(query);
271275

@@ -275,7 +279,7 @@ fn parent_scoped_ui_queries_match_concrete_nested_tabs() {
275279
assert!(!ui.show_all(), "query should not show all UI tabs: {query}");
276280
assert_eq!(
277281
ui.ui_tabs(),
278-
&[expected_tab],
282+
expected_tabs,
279283
"query should show concrete nested UI tab: {query}",
280284
);
281285
}
@@ -285,6 +289,8 @@ fn parent_scoped_ui_queries_match_concrete_nested_tabs() {
285289
fn ui_nested_visible_control_labels_match_concrete_nested_tabs() {
286290
let cases = [
287291
("layout mode", UiTabId::Toolbar),
292+
("top.tool.blur", UiTabId::ToolbarVisibility),
293+
("side.group.presets", UiTabId::ToolbarVisibility),
288294
("status bar position", UiTabId::StatusBar),
289295
("click highlight radius", UiTabId::ClickHighlight),
290296
];

configurator/src/app/update/fields.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::models::{
1212
};
1313
#[cfg(feature = "tablet-input")]
1414
use crate::models::{PressureThicknessEditModeOption, PressureThicknessEntryModeOption};
15+
use wayscriber::config::ToolbarItemId;
1516

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

@@ -168,6 +169,17 @@ impl ConfiguratorApp {
168169
Task::none()
169170
}
170171

172+
pub(super) fn handle_toolbar_item_visibility_changed(
173+
&mut self,
174+
id: ToolbarItemId,
175+
visible: bool,
176+
) -> Task<Message> {
177+
self.status = StatusMessage::idle();
178+
self.draft.set_toolbar_item_visible(id, visible);
179+
self.refresh_dirty_flag();
180+
Task::none()
181+
}
182+
171183
pub(super) fn handle_session_storage_mode_changed(
172184
&mut self,
173185
option: SessionStorageModeOption,

configurator/src/app/update/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ impl ConfiguratorApp {
119119
Message::ToolbarOverrideChanged(field, option) => {
120120
self.handle_toolbar_override_changed(field, option)
121121
}
122+
Message::ToolbarItemVisibilityChanged(id, visible) => {
123+
self.handle_toolbar_item_visibility_changed(id, visible)
124+
}
122125
Message::BoardsAddItem => self.handle_boards_add_item(),
123126
Message::BoardsRemoveItem(index) => self.handle_boards_remove_item(index),
124127
Message::BoardsMoveItemUp(index) => self.handle_boards_move_item(index, true),

configurator/src/app/view/ui/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ impl ConfiguratorApp {
3838

3939
let content = match active_tab {
4040
Some(UiTabId::Toolbar) => Some(self.ui_toolbar_tab()),
41+
Some(UiTabId::ToolbarVisibility) => Some(self.ui_toolbar_visibility_tab()),
4142
Some(UiTabId::StatusBar) => Some(self.ui_status_bar_tab()),
4243
Some(UiTabId::HelpOverlay) => Some(self.ui_help_overlay_tab()),
4344
Some(UiTabId::ClickHighlight) => Some(self.ui_click_highlight_tab()),

configurator/src/app/view/ui/toolbar.rs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
use iced::widget::{column, pick_list, row, scrollable, text};
1+
use iced::widget::{checkbox, column, pick_list, row, scrollable, text};
22
use iced::{Element, Length};
3+
use wayscriber::config::{
4+
ResolvedToolbarItems, ToolbarItemCategory, ToolbarItemDefinition, ToolbarItemSurface,
5+
ToolbarItemsConfig, toolbar_item_definitions,
6+
};
37

48
use crate::app::scroll::CONTENT_SCROLL_ID;
59
use crate::app::state::ConfiguratorApp;
@@ -215,4 +219,109 @@ impl ConfiguratorApp {
215219

216220
scrollable(column).id(CONTENT_SCROLL_ID).into()
217221
}
222+
223+
pub(super) fn ui_toolbar_visibility_tab(&self) -> Element<'_, Message> {
224+
let column = column![
225+
text("Toolbar Visibility").size(18),
226+
text("Checked items are shown. Uncheck an item to hide it from toolbar sizing, drawing, and hit testing. Existing section toggles and mode overrides can still hide checked items.").size(12),
227+
toolbar_item_visibility_section(
228+
&self.draft.ui_toolbar_items,
229+
&self.defaults.ui_toolbar_items,
230+
),
231+
]
232+
.spacing(12);
233+
234+
scrollable(column).id(CONTENT_SCROLL_ID).into()
235+
}
236+
}
237+
238+
fn toolbar_item_visibility_section<'a>(
239+
items: &ToolbarItemsConfig,
240+
defaults: &ToolbarItemsConfig,
241+
) -> Element<'a, Message> {
242+
let resolved = items.resolved();
243+
let default_resolved = defaults.resolved();
244+
let mut rows = column![text("Items").size(16)].spacing(8);
245+
let mut current_surface = None;
246+
let mut current_category = None;
247+
248+
if !resolved.unknown_hidden.is_empty() {
249+
rows = rows.push(
250+
text(format!(
251+
"Preserving {} unknown toolbar item id(s) from config.",
252+
resolved.unknown_hidden.len()
253+
))
254+
.size(12),
255+
);
256+
}
257+
258+
for definition in toolbar_item_definitions() {
259+
if current_surface != Some(definition.surface) {
260+
current_surface = Some(definition.surface);
261+
current_category = None;
262+
rows = rows.push(text(toolbar_item_surface_label(definition.surface)).size(14));
263+
}
264+
if current_category != Some(definition.category) {
265+
current_category = Some(definition.category);
266+
rows = rows.push(text(toolbar_item_category_label(definition.category)).size(13));
267+
}
268+
269+
rows = rows.push(toolbar_item_visibility_row(
270+
definition,
271+
&resolved,
272+
&default_resolved,
273+
));
274+
}
275+
276+
rows.into()
277+
}
278+
279+
fn toolbar_item_visibility_row<'a>(
280+
definition: &ToolbarItemDefinition,
281+
resolved: &ResolvedToolbarItems,
282+
defaults: &ResolvedToolbarItems,
283+
) -> Element<'a, Message> {
284+
let id = definition.id;
285+
let visible = !resolved.is_hidden(id);
286+
let default = format!(
287+
"default: {}",
288+
visibility_override_label(!defaults.is_hidden(id))
289+
);
290+
291+
row![
292+
checkbox(visible)
293+
.label(definition.label)
294+
.on_toggle(move |value| Message::ToolbarItemVisibilityChanged(id, value)),
295+
text(definition.id.as_str()).size(12).width(Length::Fill),
296+
text(default).size(12),
297+
]
298+
.spacing(12)
299+
.align_y(iced::Alignment::Center)
300+
.into()
301+
}
302+
303+
fn visibility_override_label(visible: bool) -> &'static str {
304+
if visible { "shown" } else { "hidden" }
305+
}
306+
307+
fn toolbar_item_surface_label(surface: ToolbarItemSurface) -> &'static str {
308+
match surface {
309+
ToolbarItemSurface::Top => "Top toolbar",
310+
ToolbarItemSurface::Side => "Side toolbar",
311+
}
312+
}
313+
314+
fn toolbar_item_category_label(category: ToolbarItemCategory) -> &'static str {
315+
match category {
316+
ToolbarItemCategory::Chrome => "Toolbar controls",
317+
ToolbarItemCategory::Tool => "Tools",
318+
ToolbarItemCategory::Utility => "Utilities",
319+
ToolbarItemCategory::Group => "Sections",
320+
ToolbarItemCategory::Action => "Actions",
321+
ToolbarItemCategory::Page => "Pages",
322+
ToolbarItemCategory::Board => "Boards",
323+
ToolbarItemCategory::Setting => "Settings",
324+
ToolbarItemCategory::Session => "Sessions",
325+
ToolbarItemCategory::ToolOption => "Tool options",
326+
}
218327
}

configurator/src/messages.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::path::PathBuf;
22
use std::sync::Arc;
33

4-
use wayscriber::config::Config;
4+
use wayscriber::config::{Config, ToolbarItemId};
55

66
use crate::models::{
77
BoardBackgroundOption, BoardItemTextField, BoardItemToggleField, ColorMode, ColorPickerId,
@@ -73,6 +73,7 @@ pub enum Message {
7373
ToolbarLayoutModeChanged(ToolbarLayoutModeOption),
7474
ToolbarOverrideModeChanged(ToolbarLayoutModeOption),
7575
ToolbarOverrideChanged(ToolbarOverrideField, OverrideOption),
76+
ToolbarItemVisibilityChanged(ToolbarItemId, bool),
7677
BoardsAddItem,
7778
BoardsRemoveItem(usize),
7879
BoardsMoveItemUp(usize),

configurator/src/models/config/draft/from_config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ impl ConfigDraft {
101101
ui_toolbar_layout_mode: ToolbarLayoutModeOption::from_mode(
102102
config.ui.toolbar.layout_mode,
103103
),
104+
ui_toolbar_items: config.ui.toolbar.items.clone(),
104105
ui_toolbar_show_presets: config.ui.toolbar.show_presets,
105106
ui_toolbar_show_actions_section: config.ui.toolbar.show_actions_section,
106107
ui_toolbar_show_actions_advanced: config.ui.toolbar.show_actions_advanced,

0 commit comments

Comments
 (0)