diff --git a/src/ui/style/editor_table.rs b/src/ui/style/editor_table.rs new file mode 100644 index 000000000..94ffe2fcf --- /dev/null +++ b/src/ui/style/editor_table.rs @@ -0,0 +1,70 @@ +use iced::widget::{text, text_input}; +use iced::{Background, Theme}; + +/// Right-hand lane reserved for the scrollbar in editable command tables. +pub(crate) const EDITOR_TABLE_GUTTER: f32 = 16.0; + +/// Text style for a value flagged as invalid in an editable command table. +pub(crate) fn danger_text_style(theme: &Theme) -> text::Style { + text::Style { + color: Some(theme.palette().danger.base.color), + } +} + +/// Invalid-value field style shared by the Shortcuts and Aliases editors. +pub(crate) fn danger_input_style(theme: &Theme, status: text_input::Status) -> text_input::Style { + let danger = theme.palette().danger.base; + text_input::Style { + background: Background::Color(theme.palette().danger.weak.color), + border: iced::border::rounded(4).color(danger.color).width( + if matches!(status, text_input::Status::Focused { .. }) { + 1.5 + } else { + 1.0 + }, + ), + icon: danger.color, + placeholder: danger.color.scale_alpha(0.7), + value: danger.text, + selection: danger.color, + } +} + +#[cfg(test)] +mod tests { + use super::{danger_input_style, danger_text_style, EDITOR_TABLE_GUTTER}; + use iced::widget::text_input; + use iced::{Background, Theme}; + + #[test] + fn invalid_editor_styles_keep_danger_palette_and_focus_border() { + let theme = Theme::Dark; + let palette = theme.palette(); + let text_style = danger_text_style(&theme); + assert_eq!(text_style.color, Some(palette.danger.base.color)); + + let active = danger_input_style(&theme, text_input::Status::Active); + assert_eq!( + active.background, + Background::Color(palette.danger.weak.color) + ); + assert_eq!(active.border.color, palette.danger.base.color); + assert_eq!(active.border.width, 1.0); + assert_eq!(active.border.radius, 4.0.into()); + assert_eq!(active.icon, palette.danger.base.color); + assert_eq!( + active.placeholder, + palette.danger.base.color.scale_alpha(0.7) + ); + assert_eq!(active.value, palette.danger.base.text); + assert_eq!(active.selection, palette.danger.base.color); + + let focused = danger_input_style(&theme, text_input::Status::Focused { is_hovered: false }); + assert_eq!(focused.border.width, 1.5); + } + + #[test] + fn editor_table_gutter_is_sixteen_pixels() { + assert_eq!(EDITOR_TABLE_GUTTER, 16.0); + } +} diff --git a/src/ui/style/mod.rs b/src/ui/style/mod.rs index 281b0ca84..455fa76e2 100644 --- a/src/ui/style/mod.rs +++ b/src/ui/style/mod.rs @@ -1,6 +1,7 @@ pub mod anno_object_scale; pub mod common; pub mod dimstyle; +pub(crate) mod editor_table; pub mod form; pub mod fusion_theme; pub mod mleaderstyle; diff --git a/src/ui/window/alias_editor.rs b/src/ui/window/alias_editor.rs index 823c7334d..9e3ebc1f3 100644 --- a/src/ui/window/alias_editor.rs +++ b/src/ui/window/alias_editor.rs @@ -9,6 +9,7 @@ use crate::app::Message; use crate::t; use crate::ui::style::common::muted_style; +use crate::ui::style::editor_table::{danger_input_style, danger_text_style, EDITOR_TABLE_GUTTER}; use iced::widget::{button, column, container, row, scrollable, text, text_input, Space}; use iced::{Background, Element, Length, Theme}; @@ -19,41 +20,6 @@ pub enum AliasField { Command, } -/// Right-hand lane reserved for the scrollbar so it never overlaps the ✕ column. -const GUTTER: f32 = 16.0; - -fn danger_text_style(theme: &Theme) -> iced::widget::text::Style { - iced::widget::text::Style { - color: Some(theme.palette().danger.base.color), - } -} - -/// Text-input variant flagging an invalid value: danger border and value -/// color on the theme's danger weak background. -fn danger_input_style( - theme: &Theme, - status: iced::widget::text_input::Status, -) -> iced::widget::text_input::Style { - let danger = theme.palette().danger.base; - iced::widget::text_input::Style { - background: Background::Color(theme.palette().danger.weak.color), - border: iced::border::rounded(4).color(danger.color).width( - if matches!( - status, - iced::widget::text_input::Status::Focused { is_hovered: _ } - ) { - 1.5 - } else { - 1.0 - }, - ), - icon: danger.color, - placeholder: danger.color.scale_alpha(0.7), - value: danger.text, - selection: danger.color, - } -} - /// Build the alias editor content. `rows` is the live working buffer. pub fn view_window<'a>( rows: &'a [(String, String)], @@ -78,7 +44,7 @@ pub fn view_window<'a>( // scrollable rows so the columns stay aligned. let gutter = iced::Padding { top: 0.0, - right: GUTTER, + right: EDITOR_TABLE_GUTTER, bottom: 0.0, left: 0.0, }; diff --git a/src/ui/window/layer_state_manager.rs b/src/ui/window/layer_state_manager.rs index 5aff75420..80bc47d10 100644 --- a/src/ui/window/layer_state_manager.rs +++ b/src/ui/window/layer_state_manager.rs @@ -42,16 +42,6 @@ fn muted(theme: &Theme) -> iced::widget::text::Style { } } -fn button_style(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { - move |theme, status| { - if accent { - button::primary(theme, status) - } else { - button::secondary(theme, status) - } - } -} - fn list_style(selected: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |theme, status| { if selected { @@ -196,7 +186,10 @@ pub fn view_window<'a>( ] .spacing(8), row![ - text(t!("Current layer")).size(10).style(muted).width(LABEL_WIDTH), + text(t!("Current layer")) + .size(10) + .style(muted) + .width(LABEL_WIDTH), text(if state.current_layer.is_empty() { "—".to_string() } else { @@ -224,9 +217,9 @@ pub fn view_window<'a>( let restore = if selected_state.is_some() { button(text(t!("Restore")).size(11)) .on_press(Message::LayerStateManagerRestore) - .style(button_style(true)) + .style(button::primary) } else { - button(text(t!("Restore")).size(11)).style(button_style(true)) + button(text(t!("Restore")).size(11)).style(button::primary) }; let delete = if selected_state.is_some() { button(text(t!("Delete")).size(11)) @@ -238,9 +231,9 @@ pub fn view_window<'a>( let edit = if selected_state.is_some() { button(text(t!("Edit")).size(11)) .on_press(Message::LayerStateManagerEdit) - .style(button_style(false)) + .style(button::secondary) } else { - button(text(t!("Edit")).size(11)).style(button_style(false)) + button(text(t!("Edit")).size(11)).style(button::secondary) }; let right = container( @@ -248,7 +241,7 @@ pub fn view_window<'a>( row![ button(text(t!("New")).size(11)) .on_press(Message::LayerStateManagerNew) - .style(button_style(false)) + .style(button::secondary) .padding([5, 12]), edit.padding([5, 12]), Space::new().width(sizing.width), @@ -282,7 +275,7 @@ pub fn view_window<'a>( }) .size(11)) .on_press(Message::LayerStateManagerSave) - .style(button_style(true)) + .style(button::primary) .padding([6, 14]), ] .spacing(7) @@ -614,11 +607,11 @@ pub fn view_editor<'a>( Space::new().width(sizing.width), button(text(t!("Cancel")).size(11)) .on_press(Message::LayerStateEditorCancel) - .style(button_style(false)) + .style(button::secondary) .padding([5, 12]), button(text(t!("Save Changes")).size(11)) .on_press(Message::LayerStateEditorSave) - .style(button_style(true)) + .style(button::primary) .padding([5, 12]), ] .spacing(7) diff --git a/src/ui/window/layers.rs b/src/ui/window/layers.rs index 4b064d4f1..4e90db369 100644 --- a/src/ui/window/layers.rs +++ b/src/ui/window/layers.rs @@ -4,6 +4,7 @@ use crate::app::Message; use crate::t; use crate::ui::properties::{lw_options, LinetypeItem, LwItem}; use crate::ui::style::common::muted_style; +use crate::ui::style::form::field_style_with_radius; use crate::ui::style::row_button::{cell_button_style, header_button_style}; use crate::ui::ROW_H; use codec::tables::layer::Layer as DocLayer; @@ -53,29 +54,6 @@ const LINETYPE_MENU_W: f32 = 220.0; /// scrolled into view after it is added (#271). pub const LAYER_TABLE_SCROLL_ID: &str = "layer-manager-table-scroll"; -fn table_input_style( - theme: &Theme, - status: iced::widget::text_input::Status, -) -> iced::widget::text_input::Style { - let palette = theme.palette(); - let border = match status { - iced::widget::text_input::Status::Focused { .. } => palette.primary.base.color, - _ => palette.background.neutral.color, - }; - iced::widget::text_input::Style { - background: Background::Color(palette.background.base.color), - border: Border { - color: border, - width: 1.0, - radius: 2.0.into(), - }, - icon: palette.background.base.text, - placeholder: palette.background.base.text.scale_alpha(0.48), - value: palette.background.base.text, - selection: palette.primary.base.color.scale_alpha(0.5), - } -} - // ── Layer data ──────────────────────────────────────────────────────────── #[derive(Clone, Debug)] @@ -401,7 +379,7 @@ impl LayerPanel { .size(FONT_SZ) .padding([3, 6]) .width(Length::Fixed(180.0)) - .style(table_input_style), + .style(|theme, status| field_style_with_radius(theme, status, 2.0)), ] .spacing(2) .align_y(iced::Center), @@ -816,7 +794,7 @@ fn layer_row<'a>( left: 4.0, right: 4.0, }) - .style(table_input_style) + .style(|theme, status| field_style_with_radius(theme, status, 2.0)) .width(Length::Fixed(name_col_w)) .into() } else { @@ -892,7 +870,7 @@ fn layer_row<'a>( right: 4.0, }) .width(Length::Fixed(COL_LT)) - .input_style(combo_input_style), + .input_style(|theme, status| field_style_with_radius(theme, status, 2.0)), LINETYPE_MENU_W, ) } else { @@ -920,7 +898,7 @@ fn layer_row<'a>( right: 4.0, }) .width(Length::Fixed(COL_LW)) - .input_style(combo_input_style) + .input_style(|theme, status| field_style_with_radius(theme, status, 2.0)) .into() } else { text(cur_lw_item.to_string()) @@ -943,7 +921,7 @@ fn layer_row<'a>( right: 4.0, }) .style(|theme: &Theme, status| { - let mut style = table_input_style(theme, status); + let mut style = field_style_with_radius(theme, status, 2.0); style.background = iced::Background::Color(Color::TRANSPARENT); style }) @@ -1021,15 +999,6 @@ fn layer_row<'a>( .into() } -// ── Combo style ──────────────────────────────────────────────────────────── - -fn combo_input_style( - theme: &Theme, - status: iced::widget::text_input::Status, -) -> iced::widget::text_input::Style { - table_input_style(theme, status) -} - // ── Display helpers ─────────────────────────────────────────────────────── pub fn iced_color_from_acad(c: &AcadColor) -> Color { diff --git a/src/ui/window/print_all.rs b/src/ui/window/print_all.rs index 91ea8deb3..99b8e9db1 100644 --- a/src/ui/window/print_all.rs +++ b/src/ui/window/print_all.rs @@ -4,16 +4,6 @@ use crate::ui::style::common::muted_style as muted; use iced::widget::{button, checkbox, column, container, row, scrollable, text, Space}; use iced::{Background, Border, Element, Length, Theme}; -fn button_style(primary: bool) -> impl Fn(&Theme, button::Status) -> button::Style { - move |theme, status| { - if primary { - button::primary(theme, status) - } else { - button::secondary(theme, status) - } - } -} - pub fn view_window<'a>( layouts: &'a [(String, bool)], printer: Option<&'a str>, @@ -61,10 +51,10 @@ pub fn view_window<'a>( }; let mut pdf = button(text(t!("PDF")).size(11)) - .style(button_style(false)) + .style(button::secondary) .padding([5, 16]); let mut print = button(text(t!("Print")).size(11)) - .style(button_style(true)) + .style(button::primary) .padding([5, 18]); if selected > 0 { pdf = pdf.on_press(Message::PrintAllPdf); @@ -94,11 +84,11 @@ pub fn view_window<'a>( row![ button(text(t!("Select all")).size(11)) .on_press(Message::PrintAllSelectAll) - .style(button_style(false)) + .style(button::secondary) .padding([4, 10]), button(text(t!("Select none")).size(11)) .on_press(Message::PrintAllSelectNone) - .style(button_style(false)) + .style(button::secondary) .padding([4, 10]), Space::new().width(Length::Fill), text(summary).size(11).style(muted), @@ -113,7 +103,7 @@ pub fn view_window<'a>( Space::new().width(Length::Fill), button(text(t!("Options…")).size(11)) .on_press(Message::PrintAllOptions) - .style(button_style(false)) + .style(button::secondary) .padding([5, 12]), pdf, print, diff --git a/src/ui/window/shortcuts.rs b/src/ui/window/shortcuts.rs index 23855e82a..d14ff65ea 100644 --- a/src/ui/window/shortcuts.rs +++ b/src/ui/window/shortcuts.rs @@ -3,6 +3,7 @@ use crate::app::Message; use crate::t; use crate::ui::style::common::muted_style; +use crate::ui::style::editor_table::{danger_input_style, danger_text_style, EDITOR_TABLE_GUTTER}; use iced::widget::{button, column, container, row, scrollable, text, text_input, Space}; use iced::{Background, Element, Length, Theme}; @@ -12,40 +13,6 @@ pub enum ShortcutField { Command, } -const GUTTER: f32 = 16.0; - -fn danger_text_style(theme: &Theme) -> iced::widget::text::Style { - iced::widget::text::Style { - color: Some(theme.palette().danger.base.color), - } -} - -/// Text-input variant flagging an invalid value: danger border and value -/// color on the theme's danger weak background. -fn danger_input_style( - theme: &Theme, - status: iced::widget::text_input::Status, -) -> iced::widget::text_input::Style { - let danger = theme.palette().danger.base; - iced::widget::text_input::Style { - background: Background::Color(theme.palette().danger.weak.color), - border: iced::border::rounded(4).color(danger.color).width( - if matches!( - status, - iced::widget::text_input::Status::Focused { is_hovered: _ } - ) { - 1.5 - } else { - 1.0 - }, - ), - icon: danger.color, - placeholder: danger.color.scale_alpha(0.7), - value: danger.text, - selection: danger.color, - } -} - pub fn view_window<'a>( rows: &'a [(String, String)], capture_row: Option, @@ -65,7 +32,7 @@ pub fn view_window<'a>( .style(muted_style); let gutter = iced::Padding { top: 0.0, - right: GUTTER, + right: EDITOR_TABLE_GUTTER, bottom: 0.0, left: 0.0, }; diff --git a/src/ui/window/xref_attach.rs b/src/ui/window/xref_attach.rs index 923fef154..58f27f365 100644 --- a/src/ui/window/xref_attach.rs +++ b/src/ui/window/xref_attach.rs @@ -13,7 +13,7 @@ use crate::app::Message; use crate::io::xref_model::Pathtype; use crate::t; use crate::ui::style::common::muted_style; -use crate::ui::style::form::{button_style, dialog_button, field_style, form_radio}; +use crate::ui::style::form::{button_style, dialog_button, field_style, form_radio, LABEL_WIDTH}; use crate::ui::window::block_definition::{group, labeled_checkbox}; /// One edit in the dialog. @@ -366,8 +366,12 @@ pub fn view_window<'a>( group( t!("Location").into_owned(), column![ - read_only_field(t!("Found in:").into_owned(), &state.found_in, 92.0), - read_only_field(t!("Saved path:").into_owned(), &state.saved_path, 92.0), + read_only_field(t!("Found in:").into_owned(), &state.found_in, LABEL_WIDTH,), + read_only_field( + t!("Saved path:").into_owned(), + &state.saved_path, + LABEL_WIDTH, + ), ] .spacing(5), Length::Shrink, @@ -383,10 +387,11 @@ pub fn view_window<'a>( t!("Show Details") }; let footer = row![ - button(text(details_label).size(12)) - .on_press(msg(XrefAttachMsg::Details(!state.details))) - .style(button::secondary) - .padding([6, 18]), + dialog_button( + details_label, + msg(XrefAttachMsg::Details(!state.details)), + false, + ), Space::new().width(Fill), dialog_button(t!("OK"), msg(XrefAttachMsg::Apply), true), dialog_button(t!("Cancel"), Message::CloseModal, false),