Add preference section navigation

This commit is contained in:
Georg Bauer
2026-07-27 15:26:25 +02:00
parent c96675c462
commit c4e92f6206
4 changed files with 212 additions and 65 deletions

View File

@@ -312,13 +312,23 @@ fn update_runtime_config(runtime_config: &RwLock<Config>, config: &Config) {
}
impl App {
pub(super) fn open_preferences(&mut self) {
pub(super) fn open_preferences(&mut self) -> Task<Message> {
if let Some(id) = self.preferences_window {
return window::gain_focus(id);
}
if self.database.is_none() || self.pending_project_path.is_some() || self.choosing_folder {
return;
return Task::none();
}
self.preference_draft = PreferenceDraft::from_saved(&self.config);
self.preference_error = None;
self.preferences_open = true;
let (id, open) = window::open(window::Settings {
size: Size::new(920.0, 700.0),
min_size: Some(Size::new(720.0, 480.0)),
icon: Some(app_icon()),
..Default::default()
});
self.preferences_window = Some(id);
open.map(Message::PreferencesOpened)
}
pub(super) fn save_preferences(&mut self) {
@@ -413,7 +423,6 @@ impl App {
self._endpoint = Some(endpoint);
}
self.preference_draft = PreferenceDraft::from_saved(&self.config);
self.preferences_open = false;
self.preference_error = None;
self.error = None;
}

View File

@@ -8,7 +8,8 @@ use model_manager::{download_status_bar, format_bytes, format_duration};
use super::{
ActiveDownload, App, DetailTab, MAX_SIDEBAR_WIDTH, MIN_SIDEBAR_WIDTH, Message, MetricsPoint,
ModelDownload, ModelOperation, chat_scroll_id, composer_id, models_path, preferences_scroll_id,
ModelDownload, ModelOperation, PreferenceSection, chat_scroll_id, composer_id, models_path,
preferences_scroll_id,
};
use crate::database::{ProjectWithSessions, Session, SessionState};
use crate::model::{
@@ -53,7 +54,9 @@ const TRAFFIC_LIGHT_WIDTH: f32 = 78.0;
impl App {
pub(crate) fn view(&self, id: window::Id) -> Element<'_, Message> {
let content = if self.model_manager_window == Some(id) {
let content = if self.preferences_window == Some(id) {
self.preferences_panel()
} else if self.model_manager_window == Some(id) {
self.model_manager()
} else if self.help_window == Some(id) {
self.help_view()
@@ -98,8 +101,7 @@ impl App {
/// Whether a dialog covers the window. Focus moves through the whole widget
/// tree, so the layers below have to stay out of the dialog's field order.
pub(super) fn modal_open(&self) -> bool {
self.preferences_open
|| self.pending_project_path.is_some()
self.pending_project_path.is_some()
|| self.pending_session_delete.is_some()
|| self.session_rename.is_some()
|| self.menu_session().is_some()
@@ -161,8 +163,6 @@ impl App {
#[cfg(target_os = "macos")]
if let Some((prompt, _)) = &self.pending_tool_approval {
layers.push(self.tool_approval_panel(prompt));
} else if self.preferences_open {
layers.push(self.preferences_panel());
} else if let Some(path) = &self.pending_project_path {
layers.push(self.project_dialog(path));
} else if let Some((_, title)) = &self.session_rename {
@@ -177,9 +177,7 @@ impl App {
layers.push(panel);
}
#[cfg(not(target_os = "macos"))]
if self.preferences_open {
layers.push(self.preferences_panel());
} else if let Some(path) = &self.pending_project_path {
if let Some(path) = &self.pending_project_path {
layers.push(self.project_dialog(path));
} else if let Some((_, title)) = &self.session_rename {
layers.push(self.rename_dialog(title));
@@ -780,10 +778,12 @@ fn hint<'a>(title: impl Into<Element<'a, Message>>, description: &'a str) -> Too
}
fn preference_group<'a>(
section: PreferenceSection,
title: &'a str,
content: impl Into<Element<'a, Message>>,
) -> Element<'a, Message> {
container(column![text(title).size(11).color(muted_text()), content.into(),].spacing(10))
.id(iced::widget::Id::new(section.anchor()))
.width(Length::Fill)
.padding(14)
.style(preference_group_style)

View File

@@ -81,6 +81,7 @@ impl App {
}
let model_group = preference_group(
PreferenceSection::Model,
"MODEL & LIFECYCLE",
column![
hint(
@@ -129,6 +130,7 @@ impl App {
.spacing(10),
);
let endpoint_group = preference_group(
PreferenceSection::Endpoint,
"LOCAL ENDPOINT",
column![
hint(
@@ -155,6 +157,7 @@ impl App {
.spacing(10),
);
let generation_group = preference_group(
PreferenceSection::Generation,
"GENERATION",
column![
preference_input_row(
@@ -239,6 +242,7 @@ impl App {
.spacing(10),
);
let execution_group = preference_group(
PreferenceSection::Execution,
"EXECUTION",
column![
preference_input_row(
@@ -295,6 +299,7 @@ impl App {
.spacing(10),
);
let acceleration_group = preference_group(
PreferenceSection::Acceleration,
"ACCELERATION & MEMORY",
column![
text("SPECULATIVE DECODING").size(11).color(muted_text()),
@@ -424,6 +429,7 @@ impl App {
.spacing(10),
);
let steering_group = preference_group(
PreferenceSection::Steering,
"STEERING & DIAGNOSTICS",
column![
text("DIRECTIONAL STEERING").size(11).color(muted_text()),
@@ -493,6 +499,7 @@ impl App {
.spacing(10),
);
let kv_cache_group = preference_group(
PreferenceSection::KvCache,
"KV CACHE",
column![
preference_input_row(
@@ -556,45 +563,60 @@ impl App {
if let Some(error) = &self.preference_error {
fields = fields.push(text(error).style(iced::widget::text::danger));
}
let header = row![
icon(ICON_SETTINGS, 22),
text("Preferences").size(24),
Space::new().width(Length::Fill),
text("⌘,").size(12),
]
.spacing(10)
.align_y(Alignment::Center);
let navigation = PreferenceSection::ALL.iter().fold(
column![
row![icon(ICON_SETTINGS, 20), text("Preferences").size(20)]
.spacing(9)
.align_y(Alignment::Center),
text("Jump to section").size(12).color(muted_text()),
rule::horizontal(1),
]
.spacing(8),
|navigation, section| {
navigation.push(
button(text(section.label()).size(13))
.on_press(Message::ScrollPreferences(*section))
.width(Length::Fill)
.padding([8, 10])
.style(button::text),
)
},
);
let footer = row![
action_button("Reset DS4 defaults").on_press(Message::ResetPreferences),
Space::new().width(Length::Fill),
action_button("Cancel").on_press(Message::DismissPanel),
action_button("Cancel").on_press(Message::ClosePreferences),
action_button("Save").on_press(Message::SavePreferences),
]
.spacing(8);
let panel = container(
column![
header,
scrollable(container(fields).padding(iced::Padding::ZERO.right(18)))
.id(preferences_scroll_id())
.height(Length::Fill),
footer
]
.spacing(16),
)
.padding(24)
.width(700)
container(row![
container(navigation)
.width(210)
.height(Length::Fill)
.padding(20)
.style(sidebar_style),
container(
column![
row![
text("Settings").size(24),
Space::new().width(Length::Fill),
text("⌘,").size(12).color(muted_text()),
]
.align_y(Alignment::Center),
scrollable(container(fields).padding(iced::Padding::ZERO.right(18)))
.id(preferences_scroll_id())
.height(Length::Fill),
footer,
]
.spacing(16),
)
.padding(24)
.width(Length::Fill)
.height(Length::Fill),
])
.width(Length::Fill)
.height(Length::Fill)
.max_height(660)
.style(overview_style);
opaque(
container(panel)
.padding(24)
.center_x(Length::Fill)
.center_y(Length::Fill)
.style(|_| {
container::Style::default().background(Color::from_rgba8(0, 0, 0, 0.68))
}),
)
.into()
}
}