diff --git a/crates/hearthspace-settings/src/ui.rs b/crates/hearthspace-settings/src/ui.rs index 0dc2d77..d61fd4c 100644 --- a/crates/hearthspace-settings/src/ui.rs +++ b/crates/hearthspace-settings/src/ui.rs @@ -6,10 +6,7 @@ use xilem::{ core::fork, masonry::layout::AsUnit, style::Style, - view::{ - CrossAxisAlignment, checkbox, flex_col, flex_item, flex_row, label, radio_button, - radio_group, sized_box, text_button, worker, - }, + view::{CrossAxisAlignment, checkbox, flex_col, flex_item, flex_row, label, sized_box, worker}, }; use crate::{ @@ -168,32 +165,36 @@ fn appearance_settings_view(state: &SettingsApp) -> Box { - state.background_picker_pending = true; - state.status = "Opening background image picker".to_string(); - } - Err(error) => { - state.status = - format!("Failed to open background image picker: {error}"); + match sender.send(BackgroundPickerRequest::PickFile) { + Ok(()) => { + state.background_picker_pending = true; + state.status = "Opening background image picker".to_string(); + } + Err(error) => { + state.status = + format!("Failed to open background image picker: {error}"); + } } - } - }), - text_button("Use default background", |state: &mut SettingsApp| { + }, + ), + components::action_button("Use default background", |state: &mut SettingsApp| { save_background_image_path(state, None); }), - )), + )) + .gap(10.0.px()), )) .cross_axis_alignment(CrossAxisAlignment::Start) .gap(12.0.px()), @@ -251,36 +252,37 @@ fn status_footer(state: &SettingsApp) -> impl WidgetView + use<> { } fn background_mode_picker(selected_mode: BackgroundMode) -> impl WidgetView + use<> { - radio_group(flex_row(( - radio_button( + flex_row(( + components::segmented_option( "Repeat with canvas", selected_mode == BackgroundMode::RepeatWithCanvas, |state: &mut SettingsApp| { save_background_mode(state, BackgroundMode::RepeatWithCanvas); }, ), - radio_button( + components::segmented_option( "Stretch", selected_mode == BackgroundMode::Stretch, |state: &mut SettingsApp| { save_background_mode(state, BackgroundMode::Stretch); }, ), - radio_button( + components::segmented_option( "Contains", selected_mode == BackgroundMode::Contains, |state: &mut SettingsApp| { save_background_mode(state, BackgroundMode::Contains); }, ), - radio_button( + components::segmented_option( "Cover", selected_mode == BackgroundMode::Cover, |state: &mut SettingsApp| { save_background_mode(state, BackgroundMode::Cover); }, ), - ))) + )) + .gap(8.0.px()) } fn format_monitor_summary(monitors: &[MonitorConfig]) -> String { @@ -305,11 +307,16 @@ fn monitor_details_view(state: &SettingsApp) -> Box> return label("Select a monitor to edit its settings.").boxed(); }; + let selected_scale = monitor.scale; let scale_buttons = MONITOR_SCALE_OPTIONS.map(|scale| { let id = monitor.id.clone(); - text_button(format!("{}x", scale), move |state: &mut SettingsApp| { - save_monitor_scale(state, &id, scale); - }) + components::segmented_option( + format!("{}x", scale), + selected_scale == scale, + move |state: &mut SettingsApp| { + save_monitor_scale(state, &id, scale); + }, + ) }); flex_col(( diff --git a/crates/ui-components/src/lib.rs b/crates/ui-components/src/lib.rs index fa2ebc5..330f4a2 100644 --- a/crates/ui-components/src/lib.rs +++ b/crates/ui-components/src/lib.rs @@ -5,8 +5,8 @@ use xilem::{ layout::AsUnit, parley::{fontique::GenericFamily, style::FontWeight}, properties::{ - Background, BorderColor, BorderWidth, CaretColor, ContentColor, CornerRadius, Padding, - PlaceholderColor, + Background, BorderColor, BorderWidth, CaretColor, CheckmarkColor, CheckmarkStrokeWidth, + ContentColor, CornerRadius, Padding, PlaceholderColor, }, theme::default_property_set, widgets::{Button, Checkbox, Label, RadioButton, TextArea, TextInput}, @@ -49,8 +49,53 @@ pub fn hearthspace_light_properties() -> DefaultProperties { } properties.insert::(ContentColor::new(TEXT)); + properties.insert::(ContentColor::new(TEXT)); + properties.insert::(Background::Color(SURFACE)); + properties.insert::(BorderColor { color: BORDER }); + properties.insert::(BorderWidth { width: 1.0.px() }); + properties.insert::(CornerRadius { radius: 4.0.px() }); + properties.insert::(CheckmarkColor::new(ACCENT)); + properties.insert::(CheckmarkStrokeWidth { width: 2.0 }); + { + let mut stack = PropertyStack::new(); + stack.push( + Selector::new().with_hovered(true), + BorderColor { color: ACCENT }, + ); + stack.push( + Selector::new().with_focused(true), + BorderColor { color: ACCENT }, + ); + stack.push( + Selector::new().with_active(true), + Background::Color(ACCENT_SUBTLE), + ); + properties.insert_stack::(stack); + } + properties.insert::(ContentColor::new(TEXT)); + properties.insert::(Background::Color(SURFACE)); + properties.insert::(BorderColor { color: BORDER }); + properties.insert::(BorderWidth { width: 1.0.px() }); + properties.insert::(CheckmarkColor::new(ACCENT)); + { + let mut stack = PropertyStack::new(); + stack.push( + Selector::new().with_hovered(true), + BorderColor { color: ACCENT }, + ); + stack.push( + Selector::new().with_focused(true), + BorderColor { color: ACCENT }, + ); + stack.push( + Selector::new().with_active(true), + Background::Color(ACCENT_SUBTLE), + ); + properties.insert_stack::(stack); + } + properties.insert::, _>(ContentColor::new(TEXT)); properties.insert::, _>(ContentColor::new(TEXT)); @@ -161,3 +206,48 @@ where .border(border, 1.0.px()) .corner_radius(6.0.px()) } + +pub fn action_button( + text: T, + callback: F, +) -> impl WidgetView + use +where + T: Into, + F: Fn(&mut State) -> Action + Send + Sync + 'static, +{ + button( + label(text) + .font(GenericFamily::SystemUi) + .text_alignment(TextAlign::Center) + .color(TEXT), + callback, + ) + .background_color(SURFACE) + .border(BORDER, 1.0.px()) + .corner_radius(6.0.px()) +} + +pub fn segmented_option( + text: T, + selected: bool, + callback: F, +) -> impl WidgetView + use +where + T: Into, + F: Fn(&mut State) -> Action + Send + Sync + 'static, +{ + let background = if selected { ACCENT_SUBTLE } else { SURFACE }; + let border = if selected { ACCENT } else { BORDER }; + let text_color = if selected { ACCENT } else { TEXT }; + + button( + label(text) + .font(GenericFamily::SystemUi) + .text_alignment(TextAlign::Center) + .color(text_color), + callback, + ) + .background_color(background) + .border(border, 1.0.px()) + .corner_radius(6.0.px()) +}