system settings
git clone https://git.lucas.co/cce-system-interface.git
feat: a Navigation Bar Position dropdown on the Browser page
Picks which window edge cce-browser's floating utility bar sits against,
writing browser.bar-position ("top" | "bottom") into the browser's own
app config the way the search-engine menu writes browser.search. Wired
the same way throughout: a key/label table, a SetBarPosition message, the
field on BrowserConfig so the watcher refresh carries it, and the widget
in both section_widgets and the section's vstack.
Verified by injection in a headless session: the menu opens with the
configured value selected, and picking Bottom lands bar-position "bottom"
in ~/.config/cce/cce-browser/config.kdl.
Co-Authored-By: Claude Opus 5 <[email protected]>
src/pages/browser.rs | 43 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/src/pages/browser.rs b/src/pages/browser.rs
index 6a58861..528e3e7 100644
--- a/src/pages/browser.rs
+++ b/src/pages/browser.rs
@@ -1,7 +1,8 @@
//! Browser (cce-browser) settings: homepage, search engine, download
-//! directory, history recording. Edits the browser's own app config
-//! (`~/.config/cce/cce-browser/config.kdl`, section "browser") — the
-//! browser reloads it when its window regains focus.
+//! directory, history recording, navigation-bar position. Edits the
+//! browser's own app config (`~/.config/cce/cce-browser/config.kdl`,
+//! section "browser") — the browser reloads it when its window regains
+//! focus.
use std::fs;
@@ -20,6 +21,10 @@ pub const SEARCH_ENGINES: [(&str, &str); 4] = [
("wikipedia", "Wikipedia"),
];
+/// config key, menu label — the window edge the browser's floating
+/// navigation bar is anchored to.
+pub const BAR_POSITIONS: [(&str, &str); 2] = [("top", "Top"), ("bottom", "Bottom")];
+
const DEFAULT_HOMEPAGE: &str = "https://servo.org";
#[derive(Debug, Clone)]
@@ -28,6 +33,7 @@ pub struct BrowserConfig {
pub search: String,
pub download_dir: String,
pub history: bool,
+ pub bar_position: String,
}
pub struct BrowserState {
@@ -36,8 +42,10 @@ pub struct BrowserState {
pub search: String,
pub download_dir: String,
pub history: bool,
+ pub bar_position: String,
pub homepage_box: cce_ui::widget::Adapted<TextBox>,
pub search_menu: cce_ui::widget::Adapted<Dropdown>,
+ pub bar_position_menu: cce_ui::widget::Adapted<Dropdown>,
pub download_dir_box: cce_ui::widget::Adapted<TextBox>,
pub history_toggle: cce_ui::widget::Adapted<Toggle>,
}
@@ -59,12 +67,18 @@ impl Default for BrowserState {
search: config.search.clone(),
download_dir: config.download_dir,
history: config.history,
+ bar_position: config.bar_position.clone(),
homepage_box,
search_menu: Dropdown::new(
SEARCH_ENGINES.iter().map(|(_, label)| label.to_string()).collect(),
search_index(&config.search),
)
.with_label("Search Engine"),
+ bar_position_menu: Dropdown::new(
+ BAR_POSITIONS.iter().map(|(_, label)| label.to_string()).collect(),
+ bar_position_index(&config.bar_position),
+ )
+ .with_label("Navigation Bar Position"),
download_dir_box,
history_toggle: Toggle::new().with_label("Record History"),
}
@@ -75,9 +89,14 @@ fn search_index(key: &str) -> usize {
SEARCH_ENGINES.iter().position(|(k, _)| *k == key).unwrap_or(0)
}
+fn bar_position_index(key: &str) -> usize {
+ BAR_POSITIONS.iter().position(|(k, _)| *k == key).unwrap_or(0)
+}
+
#[derive(Debug, Clone)]
pub enum BrowserMessage {
SetSearch(String),
+ SetBarPosition(String),
ToggleHistory,
/// Commit the homepage / download-dir text fields.
Apply,
@@ -100,6 +119,10 @@ pub fn update(state: &mut BrowserState, msg: BrowserMessage) {
state.search = key.clone();
write_config_value("search", &key);
}
+ BrowserMessage::SetBarPosition(key) => {
+ state.bar_position = key.clone();
+ write_config_value("bar-position", &key);
+ }
BrowserMessage::ToggleHistory => {
state.history = !state.history;
write_config_value("history", &state.history.to_string());
@@ -119,6 +142,7 @@ pub fn update(state: &mut BrowserState, msg: BrowserMessage) {
state.loaded = true;
state.search = new.search;
state.history = new.history;
+ state.bar_position = new.bar_position;
// Don't clobber fields mid-edit with watcher refreshes.
if !state.homepage_box.editing && state.homepage != new.homepage {
state.homepage = new.homepage.clone();
@@ -151,6 +175,7 @@ pub fn read_browser_config() -> BrowserConfig {
search: val["browser"]["search"].as_str().unwrap_or("duckduckgo").to_string(),
download_dir: val["browser"]["download-dir"].as_str().unwrap_or("").to_string(),
history: val["browser"]["history"].as_bool().unwrap_or(true),
+ bar_position: val["browser"]["bar-position"].as_str().unwrap_or("top").to_string(),
}
}
@@ -171,6 +196,7 @@ impl AppPage for BrowserState {
vec![vec![
self.homepage_box.id(),
self.search_menu.id(),
+ self.bar_position_menu.id(),
self.download_dir_box.id(),
self.history_toggle.id(),
]]
@@ -203,6 +229,10 @@ impl AppPage for BrowserState {
self.search_menu.set_row_rect(stack.context.left + 14.0, row_w);
stack.add_widget(&mut self.search_menu, row_w, 44.0, ctx);
+ self.bar_position_menu.selected = bar_position_index(&self.bar_position);
+ self.bar_position_menu.set_row_rect(stack.context.left + 14.0, row_w);
+ stack.add_widget(&mut self.bar_position_menu, row_w, 44.0, ctx);
+
self.download_dir_box.set_row_rect(stack.context.left + 14.0, row_w);
stack.add_widget(&mut self.download_dir_box, row_w, 44.0, ctx);
@@ -236,6 +266,13 @@ impl AppPage for BrowserState {
.unwrap_or_else(|| "duckduckgo".to_string());
actions.push(AppAction::Browser(BrowserMessage::SetSearch(key)));
}
+ if self.bar_position_menu.take_change() {
+ let key = BAR_POSITIONS
+ .get(self.bar_position_menu.selected)
+ .map(|(k, _)| k.to_string())
+ .unwrap_or_else(|| "top".to_string());
+ actions.push(AppAction::Browser(BrowserMessage::SetBarPosition(key)));
+ }
if self.history_toggle.take_change() {
actions.push(AppAction::Browser(BrowserMessage::ToggleHistory));
}