From 6b37476687ff1e5cc02ed5085e22d225c319fc11 Mon Sep 17 00:00:00 2001 From: Karim Jerbi Date: Thu, 24 Sep 2026 17:07:14 +0100 Subject: [PATCH] feat(ui): migrate dialogs across application to shared dialog_button and form_radio builders --- locales/ar-SA/opencadstudio.ftl | 1 + locales/bg-BG/opencadstudio.ftl | 1 + locales/cs-CZ/opencadstudio.ftl | 1 + locales/de-DE/opencadstudio.ftl | 1 + locales/el-GR/opencadstudio.ftl | 1 + locales/es-ES/opencadstudio.ftl | 1 + locales/fi-FI/opencadstudio.ftl | 1 + locales/fr-FR/opencadstudio.ftl | 1 + locales/hi-IN/opencadstudio.ftl | 1 + locales/hu-HU/opencadstudio.ftl | 1 + locales/it-IT/opencadstudio.ftl | 1 + locales/ja-JP/opencadstudio.ftl | 1 + locales/ko-KR/opencadstudio.ftl | 1 + locales/nl-NL/opencadstudio.ftl | 1 + locales/pl-PL/opencadstudio.ftl | 1 + locales/pt-BR/opencadstudio.ftl | 1 + locales/ru-RU/opencadstudio.ftl | 1 + locales/tr-TR/opencadstudio.ftl | 1 + locales/zh-CN/opencadstudio.ftl | 1 + locales/zh-TW/opencadstudio.ftl | 1 + src/app/view/modal.rs | 6 +-- src/app/view/overlay.rs | 33 ++++++---------- src/ui/style/form.rs | 32 ++++++++++++--- src/ui/style/point_style.rs | 22 ++++------- src/ui/window/about.rs | 8 ++-- src/ui/window/annotation_data.rs | 50 ++++++++++++++++++------ src/ui/window/auto_constrain_settings.rs | 16 ++------ src/ui/window/drafting_settings.rs | 24 +++++------- src/ui/window/drawing_units.rs | 14 +++---- src/ui/window/find_replace.rs | 33 ++++++++-------- src/ui/window/geometric_tolerance.rs | 26 ++++++------ src/ui/window/layer_translator.rs | 16 ++++---- src/ui/window/missing_fonts.rs | 24 ++++++------ src/ui/window/options.rs | 24 ++++++------ src/ui/window/xref_attach.rs | 46 +++++++++------------- src/ui/window/xref_help.rs | 17 ++++---- 36 files changed, 214 insertions(+), 197 deletions(-) diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index d9b145dcb..63a632194 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -2324,6 +2324,7 @@ common = .wblock-err-no-block-name = يرجى تحديد أو إدخال اسم الكتلة. .wblock-err-block-not-found = الكتلة "__ocs_fmt_0__" غير موجودة في الرسم. .wblock-err-no-path = يرجى تحديد اسم الملف والمسار. + .wblock-err-current-drawing = لا يمكن الكتابة إلى ملف الرسم الحالي. .wblock-help-info = يقوم WBLOCK بكتابة كائنات أو كتلة أو الرسم بالكامل إلى ملف رسم جديد. errors = .save-warning = تحذير الحفظ diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index dbb285280..3c2daed2b 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = Моля, изберете или въведете име на блок. .wblock-err-block-not-found = Блокът "__ocs_fmt_0__" не съществува в чертежа. .wblock-err-no-path = Моля, задайте име и път на файла. + .wblock-err-current-drawing = Не може да се записва в текущия файл с чертеж. .wblock-help-info = WBLOCK записва обекти, блок или целия чертеж в нов чертожен файл. errors = .save-warning = Предупреждение при запазване diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 577daa7f7..8b099c672 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = Vyberte nebo zadejte název bloku. .wblock-err-block-not-found = Blok "__ocs_fmt_0__" ve výkresu neexistuje. .wblock-err-no-path = Zadejte název souboru a cestu. + .wblock-err-current-drawing = Nelze zapisovat do aktuálního souboru výkresu. .wblock-help-info = WBLOCK zapíše objekty, blok nebo celý výkres do nového výkresového souboru. errors = .save-warning = Uložit varování diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 179e5ad89..225f32ba6 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -2312,6 +2312,7 @@ common = .wblock-err-no-block-name = Bitte wählen Sie einen Blocknamen aus oder geben Sie ihn ein. .wblock-err-block-not-found = Block "__ocs_fmt_0__" ist in der Zeichnung nicht vorhanden. .wblock-err-no-path = Bitte geben Sie einen Dateinamen und Pfad an. + .wblock-err-current-drawing = In die aktuelle Zeichnungsdatei kann nicht geschrieben werden. .wblock-help-info = WBLOCK schreibt Objekte, einen Block oder die gesamte Zeichnung in eine neue Zeichnungsdatei. errors = .save-warning = Warnhinweise speichern diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index b3c9add94..ef73bc35e 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = Επιλέξτε ή πληκτρολογήστε ένα όνομα μπλοκ. .wblock-err-block-not-found = Το μπλοκ "__ocs_fmt_0__" δεν υπάρχει στο σχέδιο. .wblock-err-no-path = Καθορίστε ένα όνομα και διαδρομή αρχείου. + .wblock-err-current-drawing = Δεν είναι δυνατή η εγγραφή στο τρέχον αρχείο σχεδίου. .wblock-help-info = Η εντολή WBLOCK εγγράφει αντικείμενα, ένα μπλοκ ή ολόκληρο το σχέδιο σε ένα νέο αρχείο σχεδίου. errors = .save-warning = Προειδοποίηση αποθήκευσης diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 1ac9bbb7c..7faf157ce 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -2312,6 +2312,7 @@ common = .wblock-err-no-block-name = Seleccione o escriba un nombre de bloque. .wblock-err-block-not-found = El bloque "__ocs_fmt_0__" no existe en el dibujo. .wblock-err-no-path = Especifique un nombre de archivo y una ruta. + .wblock-err-current-drawing = No se puede escribir en el archivo de dibujo actual. .wblock-help-info = WBLOCK escribe objetos, un bloque o el dibujo completo en un nuevo archivo de dibujo. errors = .save-warning = Advertencia de guardado diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index e0dbb5dfb..917ad12ac 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = Valitse tai anna lohkon nimi. .wblock-err-block-not-found = Lohkoa "__ocs_fmt_0__" ei ole piirustuksessa. .wblock-err-no-path = Määritä tiedoston nimi ja polku. + .wblock-err-current-drawing = Nykyiseen piirustustiedostoon ei voi kirjoittaa. .wblock-help-info = WBLOCK kirjoittaa kohteet, lohkon tai koko piirustuksen uuteen piirustustiedostoon. errors = .save-warning = Tallenna varoitus diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index d538ca9e5..cb1d177e9 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -2312,6 +2312,7 @@ common = .wblock-err-no-block-name = Veuillez sélectionner ou entrer un nom de bloc. .wblock-err-block-not-found = Le bloc « __ocs_fmt_0__ » n'existe pas dans le dessin. .wblock-err-no-path = Veuillez spécifier un nom de fichier et un chemin d'accès. + .wblock-err-current-drawing = Impossible d'écrire dans le fichier de dessin actuel. .wblock-help-info = WBLOCK écrit des objets, un bloc ou l'ensemble du dessin dans un nouveau fichier de dessin. errors = .save-warning = Enregistrer l' avertissement diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 32af12c50..03319c8b6 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -2300,6 +2300,7 @@ common = .wblock-err-no-block-name = कृपया एक ब्लॉक नाम चुनें या दर्ज करें। .wblock-err-block-not-found = ब्लॉक "__ocs_fmt_0__" रेखाचित्र में मौजूद नहीं है। .wblock-err-no-path = कृपया फ़ाइल का नाम और पथ निर्दिष्ट करें। + .wblock-err-current-drawing = वर्तमान आरेखण फ़ाइल में नहीं लिखा जा सकता। .wblock-help-info = WBLOCK ऑब्जेक्ट, एक ब्लॉक, या संपूर्ण रेखाचित्र को एक नई रेखाचित्र फ़ाइल में लिखता है। errors = .save-warning = चेतावनी सहेजें diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 73628dd4a..534a1264c 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = Válasszon vagy adjon meg egy blokknevet. .wblock-err-block-not-found = A(z) "__ocs_fmt_0__" blokk nem létezik a rajzban. .wblock-err-no-path = Adja meg a fájlnevet és az elérési utat. + .wblock-err-current-drawing = Nem lehet írni az aktuális rajzfájlba. .wblock-help-info = A WBLOCK objektumokat, egy blokkot vagy a teljes rajzot új rajzfájlba menti. errors = .save-warning = Figyelmeztetés mentése diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index c92c3f40a..325895258 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = Selezionare o immettere il nome di un blocco. .wblock-err-block-not-found = Il blocco "__ocs_fmt_0__" non esiste nel disegno. .wblock-err-no-path = Specificare un nome file e un percorso. + .wblock-err-current-drawing = Impossibile scrivere nel file di disegno corrente. .wblock-help-info = WBLOCK scrive gli oggetti, un blocco o l'intero disegno in un nuovo file di disegno. errors = .save-warning = Salva avviso diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 6c46e9cf1..03cdaf6dc 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -2302,6 +2302,7 @@ common = .wblock-err-no-block-name = ブロック名を選択または入力してください。 .wblock-err-block-not-found = ブロック "__ocs_fmt_0__" は図面に存在しません。 .wblock-err-no-path = ファイル名とパスを指定してください。 + .wblock-err-current-drawing = 現在の図面ファイルには書き込めません。 .wblock-help-info = WBLOCK はオブジェクト、ブロック、または図面全体を新しい図面ファイルに書き出します。 errors = .save-warning = 保存の警告 diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index ee36b001b..d01f1d748 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = 블록 이름을 선택하거나 입력하십시오. .wblock-err-block-not-found = 블록 "__ocs_fmt_0__"이(가) 도면에 존재하지 않습니다. .wblock-err-no-path = 파일 이름과 경로를 지정하십시오. + .wblock-err-current-drawing = 현재 도면 파일에는 쓸 수 없습니다. .wblock-help-info = WBLOCK은 객체, 블록 또는 도면 전체를 새 도면 파일로 내보냅니다. errors = .save-warning = 경고 저장 diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index f162e7301..bce553628 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -2312,6 +2312,7 @@ common = .wblock-err-no-block-name = Selecteer of voer een bloknaam in. .wblock-err-block-not-found = Blok "__ocs_fmt_0__" bestaat niet in de tekening. .wblock-err-no-path = Geef een bestandsnaam en pad op. + .wblock-err-current-drawing = Kan niet schrijven naar het huidige tekeningbestand. .wblock-help-info = WBLOCK schrijft objecten, een blok of de volledige tekening naar een nieuw tekeningbestand. errors = .save-warning = Waarschuwing opslaan diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 1937bc168..9ac4ad83b 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = Wybierz lub wprowadź nazwę bloku. .wblock-err-block-not-found = Blok „__ocs_fmt_0__” nie istnieje w rysunku. .wblock-err-no-path = Podaj nazwę pliku i ścieżkę. + .wblock-err-current-drawing = Nie można zapisać do bieżącego pliku rysunku. .wblock-help-info = WBLOCK zapisuje obiekty, blok lub cały rysunek do nowego pliku rysunku. errors = .save-warning = Zapisz ostrzeżenie diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index 7612ea5f1..385759cd2 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -2312,6 +2312,7 @@ common = .wblock-err-no-block-name = Selecione ou insira o nome de um bloco. .wblock-err-block-not-found = O bloco "__ocs_fmt_0__" não existe no desenho. .wblock-err-no-path = Especifique um nome de arquivo e caminho. + .wblock-err-current-drawing = Não é possível gravar no arquivo de desenho atual. .wblock-help-info = O WBLOCK grava objetos, um bloco ou o desenho inteiro em um novo arquivo de desenho. errors = .save-warning = Aviso de salvamento diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index ab6a4a674..05ec1405c 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -2315,6 +2315,7 @@ common = .wblock-err-no-block-name = Выберите или введите имя блока. .wblock-err-block-not-found = Блок "__ocs_fmt_0__" отсутствует в чертеже. .wblock-err-no-path = Укажите имя файла и путь к нему. + .wblock-err-current-drawing = Невозможно записать в текущий файл чертежа. .wblock-help-info = WBLOCK записывает объекты, блок или весь чертеж в новый файл чертежа. errors = .save-warning = Спасти предостережение diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 42c37f657..22abcc5ce 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -2296,6 +2296,7 @@ common = .wblock-err-no-block-name = Lütfen bir blok adı seçin veya girin. .wblock-err-block-not-found = "__ocs_fmt_0__" bloğu çizimde mevcut değil. .wblock-err-no-path = Lütfen bir dosya adı ve yolu belirtin. + .wblock-err-current-drawing = Geçerli çizim dosyasına yazılamıyor. .wblock-help-info = WBLOCK nesneleri, bir bloğu veya tüm çizimi yeni bir çizim dosyasına yazar. errors = .save-warning = Kaydetme Uyarısı diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 1dccd7669..a1fb404ee 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -2299,6 +2299,7 @@ common = .wblock-err-no-block-name = 请选择或输入块名称。 .wblock-err-block-not-found = 图形中不存在块“__ocs_fmt_0__”。 .wblock-err-no-path = 请指定文件名和路径。 + .wblock-err-current-drawing = 无法写入当前图形文件。 .wblock-help-info = WBLOCK 将对象、块或整个图形写入新的图形文件。 errors = .save-warning = 保存警告 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 8edf0dacf..ad0d17c01 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -2311,6 +2311,7 @@ common = .wblock-err-no-block-name = 請選取或輸入圖塊名稱。 .wblock-err-block-not-found = 圖面中不存在圖塊「__ocs_fmt_0__」。 .wblock-err-no-path = 請指定檔名和路徑。 + .wblock-err-current-drawing = 無法寫入目前圖面檔案。 .wblock-help-info = WBLOCK 將物件、圖塊或整個圖面寫入新的圖面檔案。 errors = .save-warning = 儲存警告 diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index 51c36a53d..f6c95bf07 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -1751,11 +1751,7 @@ fn dialog_button<'a>( message: Message, style: fn(&Theme, button::Status) -> button::Style, ) -> Element<'a, Message> { - button(text(label.into()).size(13)) - .on_press(message) - .style(style) - .padding([6, 18]) - .into() + crate::ui::style::form::dialog_button_styled(label.into(), message, style).into() } fn dialog_body_style(theme: &Theme) -> container::Style { diff --git a/src/app/view/overlay.rs b/src/app/view/overlay.rs index 413dfd4e9..630721df4 100644 --- a/src/app/view/overlay.rs +++ b/src/app/view/overlay.rs @@ -1498,7 +1498,8 @@ fn qselect_content<'a>( candidate_count: usize, sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { - use iced::widget::{checkbox, radio, rule}; + use iced::widget::{checkbox, rule}; + use crate::ui::style::form::{dialog_button, dialog_button_styled_opt, form_radio}; let mut type_options: Vec = vec![QSELECT_ANY_TYPE.to_string()]; type_options.extend(types.iter().cloned()); @@ -1627,18 +1628,12 @@ fn qselect_content<'a>( append = append.on_toggle(Message::QSelectSetAppend); } - let cancel = button(text(t!("Cancel")).size(12)) - .on_press(Message::QSelectClose) - .style(button::subtle) - .padding([5, 16]); - let apply = button(text(t!("Apply")).size(12)) - .style(button::primary) - .padding([5, 18]); - let apply = if error.is_none() { - apply.on_press(Message::QSelectApply) - } else { - apply - }; + let cancel = dialog_button(t!("Cancel"), Message::QSelectClose, false); + let apply = dialog_button_styled_opt( + t!("Apply"), + error.is_none().then_some(Message::QSelectApply), + button::primary, + ); let panel_body = column![ section_label(t!("Scope")), @@ -1722,22 +1717,18 @@ fn qselect_content<'a>( section_label(t!("Result")), Space::new().height(5), column![ - radio( + form_radio( t!("Include matching objects"), crate::app::QSelectMode::Include, Some(state.mode), Message::QSelectSetMode, - ) - .size(14) - .text_size(12), - radio( + ), + form_radio( t!("Exclude matching objects"), crate::app::QSelectMode::Exclude, Some(state.mode), Message::QSelectSetMode, - ) - .size(14) - .text_size(12), + ), ] .spacing(5), Space::new().height(8), diff --git a/src/ui/style/form.rs b/src/ui/style/form.rs index 71edfe5ed..91136217d 100644 --- a/src/ui/style/form.rs +++ b/src/ui/style/form.rs @@ -19,14 +19,36 @@ pub fn dialog_button<'a>( on_press: Message, primary: bool, ) -> button::Button<'a, Message> { - button(text(label).size(12)) - .on_press(on_press) - .padding([6, 18]) - .style(if primary { + dialog_button_styled( + label, + on_press, + if primary { button::primary } else { button::secondary - }) + }, + ) +} + +/// Standard dialog action button with an explicit button style and optional on_press action. +pub fn dialog_button_styled_opt<'a>( + label: impl text::IntoFragment<'a>, + on_press: Option, + style: fn(&Theme, button::Status) -> button::Style, +) -> button::Button<'a, Message> { + button(text(label).size(12)) + .on_press_maybe(on_press) + .padding([6, 18]) + .style(style) +} + +/// Standard dialog action button with an explicit button style (primary, secondary, danger, warning, etc.). +pub fn dialog_button_styled<'a>( + label: impl text::IntoFragment<'a>, + on_press: Message, + style: fn(&Theme, button::Status) -> button::Style, +) -> button::Button<'a, Message> { + dialog_button_styled_opt(label, Some(on_press), style) } /// Unified radio button helper ensuring consistent 16px size for exact pixel-grid concentric alignment. diff --git a/src/ui/style/point_style.rs b/src/ui/style/point_style.rs index a5c817f44..16e598c4d 100644 --- a/src/ui/style/point_style.rs +++ b/src/ui/style/point_style.rs @@ -6,9 +6,10 @@ //! does the same without the dialog. use crate::app::Message; -use iced::widget::{button, canvas, column, container, radio, row, text, text_input, Space}; -use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Size, Theme}; use crate::ui::style::common::muted_style; +use crate::ui::style::form::{dialog_button, form_radio}; +use iced::widget::{button, canvas, column, container, row, text, text_input, Space}; +use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Size, Theme}; use crate::t; use std::borrow::Cow; @@ -162,29 +163,22 @@ pub fn view_window<'a>( .align_y(iced::Center); let radios = column![ - radio( + form_radio( t!("Set Size Relative to Screen"), true, Some(relative), Message::PointStyleSizeRelative, - ) - .size(16) - .text_size(13), - radio( + ), + form_radio( t!("Set Size in Absolute Units"), false, Some(relative), Message::PointStyleSizeRelative, - ) - .size(16) - .text_size(13), + ), ] .spacing(6); - let ok = button(text(t!("OK")).size(13)) - .padding([5, 22]) - .on_press(Message::PointStyleOk) - .style(button::primary); + let ok = dialog_button(t!("OK"), Message::PointStyleOk, true); container( column![ diff --git a/src/ui/window/about.rs b/src/ui/window/about.rs index a9e4fc809..9d48336ba 100644 --- a/src/ui/window/about.rs +++ b/src/ui/window/about.rs @@ -1,7 +1,8 @@ use crate::app::Message; use crate::t; use crate::ui::style::common::muted_style; -use iced::widget::{button, column, container, row, svg, text, Space}; +use crate::ui::style::form::dialog_button; +use iced::widget::{column, container, row, svg, text, Space}; use iced::{Background, Border, Element, Fill, Length, Shrink, Theme}; fn primary_style(theme: &Theme) -> iced::widget::text::Style { @@ -136,10 +137,7 @@ pub fn view_window( .spacing(8) .width(content_width); - let copy = button(text(t!("Copy Info")).size(11)) - .on_press(Message::AboutCopyInfo) - .style(button::primary) - .padding([6, 16]); + let copy = dialog_button(t!("Copy Info"), Message::AboutCopyInfo, true); container( column![ diff --git a/src/ui/window/annotation_data.rs b/src/ui/window/annotation_data.rs index 63e965511..f35fc78db 100644 --- a/src/ui/window/annotation_data.rs +++ b/src/ui/window/annotation_data.rs @@ -13,6 +13,7 @@ use iced::{Border, Element, Fill, Length, Theme}; use crate::app::Message; use crate::t; +use crate::ui::style::form::{dialog_button, dialog_button_styled_opt, form_radio}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum TableSource { @@ -435,9 +436,7 @@ fn cell_style_picker<'a>( } fn radio_button<'a>(selected: bool, label: impl Into, msg: Message) -> Element<'a, Message> { - let mark = if selected { "◉" } else { "○" }; - button(row![text(mark).size(13), text(label.into()).size(12)].spacing(6)) - .on_press(msg).padding([4, 7]).style(button::text).into() + form_radio(label, true, selected.then_some(true), move |_| msg.clone()).into() } fn preview_grid<'a>(rows: &'a [Vec]) -> Element<'a, Message> { @@ -484,7 +483,11 @@ pub fn table_insert_view(state: &TableInsertState, sizing: crate::ui::modal::Mod group(t!("Set cell styles"), cell_styles.into()), checkbox(state.preview).label(t!("Preview")).on_toggle(|v| Message::TableInsertField(TableInsertField::Preview(v))).size(14), text(&state.error).size(11), - row![Space::new().width(Fill), button(text(t!("Cancel")).size(12)).on_press(Message::CloseModal).padding([5, 12]), button(text(t!("OK")).size(12)).on_press(Message::TableInsertApply).style(button::primary).padding([5, 12])].spacing(7), + row![ + Space::new().width(Fill), + dialog_button(t!("Cancel"), Message::CloseModal, false), + dialog_button(t!("OK"), Message::TableInsertApply, true), + ].spacing(8), ].spacing(8).padding(10).width(sizing.width).height(sizing.height).into() } @@ -542,22 +545,45 @@ pub fn data_link_view(state: &DataLinkManagerState, sizing: crate::ui::modal::Mo group(t!("Details"), text(t!("Select a data link or create a new one.")).size(11).style(muted).into()) }; let actions = if state.editing { - row![Space::new().width(Fill), button(text(t!("Cancel")).size(12)).on_press(Message::DataLinkEditCancel).padding([5, 11]), button(text(t!("OK")).size(12)).on_press(Message::DataLinkSave).style(button::primary).padding([5, 11])].spacing(7) + row![ + Space::new().width(Fill), + dialog_button(t!("Cancel"), Message::DataLinkEditCancel, false), + dialog_button(t!("OK"), Message::DataLinkSave, true), + ] + .spacing(8) } else { let enabled = state.selected.is_some(); - let edit = button(text(t!("Edit")).size(12)).padding([5, 11]); - let delete = button(text(t!("Delete")).size(12)).padding([5, 11]); - let insert = button(text(t!("Insert Table")).size(12)).padding([5, 11]); - row![if enabled { edit.on_press(Message::DataLinkEdit) } else { edit }, if enabled { delete.on_press(Message::DataLinkDelete) } else { delete }, Space::new().width(Fill), if enabled { insert.on_press(Message::DataLinkInsert) } else { insert }, button(text(t!("Close")).size(12)).on_press(Message::DataLinkClose).padding([5, 11])].spacing(7) + let edit = dialog_button_styled_opt(t!("Edit"), enabled.then_some(Message::DataLinkEdit), button::secondary); + let delete = dialog_button_styled_opt(t!("Delete"), enabled.then_some(Message::DataLinkDelete), button::secondary); + let insert = dialog_button_styled_opt(t!("Insert Table"), enabled.then_some(Message::DataLinkInsert), button::primary); + let close = dialog_button(t!("Close"), Message::DataLinkClose, false); + row![edit, delete, Space::new().width(Fill), insert, close].spacing(8) }; column![row![tree, container(right).width(Fill).height(Fill)].spacing(8).height(Fill), text(&state.status).size(11), actions] .spacing(7).padding(10).width(sizing.width).height(sizing.height).into() } fn wizard_nav(state: &DataExtractionState) -> iced::widget::Row<'_, Message> { - let back = button(text(t!("Back")).size(12)).padding([5, 12]); - let next = button(text(if state.page == ExtractionPage::Finish { t!("Finish") } else { t!("Next") }).size(12)).padding([5, 12]).style(button::primary); - row![Space::new().width(Fill), if state.page == ExtractionPage::Begin { back } else { back.on_press(Message::DataExtractionBack) }, button(text(t!("Cancel")).size(12)).on_press(Message::CloseModal).padding([5, 12]), if state.error.is_empty() { next.on_press(if state.page == ExtractionPage::Finish { Message::DataExtractionFinish } else { Message::DataExtractionNext }) } else { next }].spacing(7) + let back = dialog_button_styled_opt( + t!("Back"), + (state.page != ExtractionPage::Begin).then_some(Message::DataExtractionBack), + button::secondary, + ); + let cancel = dialog_button(t!("Cancel"), Message::CloseModal, false); + let next = dialog_button_styled_opt( + if state.page == ExtractionPage::Finish { + t!("Finish") + } else { + t!("Next") + }, + state.error.is_empty().then_some(if state.page == ExtractionPage::Finish { + Message::DataExtractionFinish + } else { + Message::DataExtractionNext + }), + button::primary, + ); + row![Space::new().width(Fill), back, cancel, next].spacing(8) } pub fn data_extraction_view(state: &DataExtractionState, sizing: crate::ui::modal::ModalSizing) -> Element<'_, Message> { diff --git a/src/ui/window/auto_constrain_settings.rs b/src/ui/window/auto_constrain_settings.rs index 2a5afa621..fc0eed97b 100644 --- a/src/ui/window/auto_constrain_settings.rs +++ b/src/ui/window/auto_constrain_settings.rs @@ -1,5 +1,6 @@ use crate::app::settings::AutoConstrainSettings; use crate::app::Message; +use crate::ui::style::form::dialog_button; use iced::widget::{button, checkbox, column, container, row, scrollable, text, text_input, Space}; use iced::{Element, Fill}; @@ -106,18 +107,9 @@ pub fn view_window<'a>( let actions = row![ Space::new().width(Fill), - button(text(crate::t!("OK"))) - .on_press(Message::AutoConstrainOk) - .style(button::primary) - .padding([6, 18]), - button(text(crate::t!("Apply"))) - .on_press(Message::AutoConstrainApply) - .style(button::secondary) - .padding([6, 18]), - button(text(crate::t!("Cancel"))) - .on_press(Message::AutoConstrainCancel) - .style(button::secondary) - .padding([6, 18]), + dialog_button(crate::t!("OK"), Message::AutoConstrainOk, true), + dialog_button(crate::t!("Apply"), Message::AutoConstrainApply, false), + dialog_button(crate::t!("Cancel"), Message::AutoConstrainCancel, false), ] .spacing(8) .align_y(iced::Center); diff --git a/src/ui/window/drafting_settings.rs b/src/ui/window/drafting_settings.rs index 91d32051c..55f654d27 100644 --- a/src/ui/window/drafting_settings.rs +++ b/src/ui/window/drafting_settings.rs @@ -1,6 +1,7 @@ use crate::app::settings::IsoPlane; use crate::app::Message; use crate::snap::{SnapType, ALL_3D_SNAP_MODES, ALL_SNAP_MODES}; +use crate::ui::style::form::{dialog_button, dialog_button_styled_opt}; use iced::widget::{button, checkbox, column, container, row, scrollable, text, text_input, Space}; use iced::{Background, Border, Element, Fill, Theme}; use std::borrow::Cow; @@ -664,28 +665,23 @@ pub fn view_window<'a>( }; // ── Bottom Action Buttons ──────────────────────────────────────────── - let ok_button = button(text(crate::t!("OK")).size(12)) - .on_press(Message::DraftingSettingsOk) - .style(button::primary) - .padding([6, 18]); + let ok_button = dialog_button(crate::t!("OK"), Message::DraftingSettingsOk, true); - let apply_button = button(text(crate::t!("Apply")).size(12)) - .on_press_maybe(if dirty { + let apply_button = dialog_button_styled_opt( + crate::t!("Apply"), + if dirty { Some(Message::DraftingSettingsApply) } else { None - }) - .style(if dirty { + }, + if dirty { button::secondary } else { button::text - }) - .padding([6, 18]); + }, + ); - let close_button = button(text(crate::t!("Close")).size(12)) - .on_press(Message::DraftingSettingsClose) - .style(button::secondary) - .padding([6, 18]); + let close_button = dialog_button(crate::t!("Close"), Message::DraftingSettingsClose, false); let action_row = row![ Space::new().width(Fill), diff --git a/src/ui/window/drawing_units.rs b/src/ui/window/drawing_units.rs index 461901d2e..4241765a1 100644 --- a/src/ui/window/drawing_units.rs +++ b/src/ui/window/drawing_units.rs @@ -12,12 +12,13 @@ use std::fmt; -use iced::widget::{button, checkbox, column, container, row, text, text_input, Space}; +use iced::widget::{checkbox, column, container, row, text, text_input, Space}; use iced::{Border, Element, Fill, Length, Theme}; use crate::app::Message; use crate::modules::draw::units; use crate::t; +use crate::ui::style::form::dialog_button; /// The dialog's working copy. Nothing reaches the drawing until OK, so a format /// can be tried against the sample and abandoned. @@ -265,15 +266,10 @@ pub fn view_window<'a>( let actions = row![ Space::new().width(Fill), - button(text(t!("Cancel")).size(12)) - .on_press(Message::CloseModal) - .padding([4, 12]), - button(text(t!("OK")).size(12)) - .on_press(Message::DrawingUnitsApply) - .style(button::primary) - .padding([4, 12]), + dialog_button(t!("OK"), Message::DrawingUnitsApply, true), + dialog_button(t!("Cancel"), Message::CloseModal, false), ] - .spacing(6) + .spacing(8) .align_y(iced::Center); column![row![length, angle].spacing(8), insertion, sample, actions,] diff --git a/src/ui/window/find_replace.rs b/src/ui/window/find_replace.rs index a1422a498..dbdd11496 100644 --- a/src/ui/window/find_replace.rs +++ b/src/ui/window/find_replace.rs @@ -1,8 +1,8 @@ use crate::app::Message; use crate::t; +use crate::ui::style::form::{dialog_button, dialog_button_styled_opt}; use iced::widget::{button, column, row, text, text_input, Space}; use iced::{Element, Fill, Length, Shrink}; -use std::borrow::Cow; pub const FIND_INPUT_ID: &str = "find-replace-search"; @@ -29,14 +29,6 @@ pub fn view_window<'a>( .size(13); let enabled = !search.trim().is_empty(); - let action = |label: Cow<'static, str>, message: Message| { - let button = button(text(label).size(12)).padding([6, 12]); - if enabled { - button.on_press(message) - } else { - button - } - }; column![ row![ @@ -56,13 +48,22 @@ pub fn view_window<'a>( text(status).size(11), row![ Space::new().width(field_width), - button(text(t!("Close")).size(12)) - .on_press(Message::CloseModal) - .padding([6, 12]) - .style(button::secondary), - action(t!("Replace"), Message::FindReplaceOne).style(button::secondary), - action(t!("Replace All"), Message::FindReplaceAll).style(button::danger), - action(t!("Find Next"), Message::FindReplaceNext).style(button::primary), + dialog_button(t!("Close"), Message::CloseModal, false), + dialog_button_styled_opt( + t!("Replace"), + enabled.then_some(Message::FindReplaceOne), + button::secondary, + ), + dialog_button_styled_opt( + t!("Replace All"), + enabled.then_some(Message::FindReplaceAll), + button::danger, + ), + dialog_button_styled_opt( + t!("Find Next"), + enabled.then_some(Message::FindReplaceNext), + button::primary, + ), ] .spacing(8) .align_y(iced::Center), diff --git a/src/ui/window/geometric_tolerance.rs b/src/ui/window/geometric_tolerance.rs index d3e93c154..faea5c737 100644 --- a/src/ui/window/geometric_tolerance.rs +++ b/src/ui/window/geometric_tolerance.rs @@ -13,6 +13,7 @@ use iced::{Border, Element, Fill, Length, Theme}; use crate::app::Message; use crate::t; +use crate::ui::style::form::{dialog_button, dialog_button_styled_opt}; const EMPTY: &str = ""; @@ -539,25 +540,22 @@ pub fn view_window<'a>( let mut actions = row![ Space::new().width(Fill), - button(text(t!("Cancel")).size(12)) - .on_press(Message::CloseModal) - .padding([4, 12]), + dialog_button(t!("Cancel"), Message::CloseModal, false), ] .spacing(6) .align_y(iced::Center); if state.editing.is_some() { - actions = actions.push( - button(text(t!("Apply")).size(12)) - .on_press_maybe(state.is_valid().then_some(Message::ToleranceDialogApply)) - .padding([4, 12]), - ); + actions = actions.push(dialog_button_styled_opt( + t!("Apply"), + state.is_valid().then_some(Message::ToleranceDialogApply), + button::secondary, + )); } - actions = actions.push( - button(text(t!("OK")).size(12)) - .on_press_maybe(state.is_valid().then_some(Message::ToleranceDialogOk)) - .style(button::primary) - .padding([4, 12]), - ); + actions = actions.push(dialog_button_styled_opt( + t!("OK"), + state.is_valid().then_some(Message::ToleranceDialogOk), + button::primary, + )); column![symbol, tolerances, datums, additions, actions] .spacing(8) diff --git a/src/ui/window/layer_translator.rs b/src/ui/window/layer_translator.rs index 7ff70cc05..30252978f 100644 --- a/src/ui/window/layer_translator.rs +++ b/src/ui/window/layer_translator.rs @@ -12,6 +12,7 @@ use iced::{Background, Border, Element, Fill, Length, Theme}; use crate::app::Message; use crate::modules::draw::layers::laytrans::{Mapping, TargetLayer}; use crate::t; +use crate::ui::style::form::{dialog_button, dialog_button_styled_opt}; /// The dialog's working state: what was loaded, what is selected, and the /// mappings built so far. None of it touches the drawing until Translate. @@ -183,15 +184,12 @@ pub fn view_window<'a>( .on_press(Message::LayerTranslatorLoadMappings) .padding([4, 10]), Space::new().width(Fill), - button(text(t!("Cancel")).size(12)) - .on_press(Message::CloseModal) - .padding([4, 12]), - button(text(t!("Translate")).size(12)) - .on_press_maybe( - (!state.mappings.is_empty()).then_some(Message::LayerTranslatorTranslate) - ) - .style(button::primary) - .padding([4, 12]), + dialog_button(t!("Cancel"), Message::CloseModal, false), + dialog_button_styled_opt( + t!("Translate"), + (!state.mappings.is_empty()).then_some(Message::LayerTranslatorTranslate), + button::primary, + ), ] .spacing(6) .align_y(iced::Center); diff --git a/src/ui/window/missing_fonts.rs b/src/ui/window/missing_fonts.rs index 875dedc62..9eab4921f 100644 --- a/src/ui/window/missing_fonts.rs +++ b/src/ui/window/missing_fonts.rs @@ -4,6 +4,7 @@ // mismatched substitute. use crate::app::Message; +use crate::ui::style::form::dialog_button_styled_opt; use iced::widget::{button, column, container, row, scrollable, text, text_input, Space}; use iced::{Element, Fill, Length, Shrink, Theme}; @@ -34,23 +35,22 @@ pub fn view_window<'a>( .iter() .map(|name| text(format!(" {name}")).size(12).into()) .collect(); - let download = button( - text(if downloading { + let download = dialog_button_styled_opt( + if downloading { crate::t!("Downloading...") } else { crate::t!("Download available") - }) - .size(12), - ) - .on_press_maybe((!downloading).then_some(Message::MissingFontsDownload)) - .style(button::primary) - .padding([6, 14]); + }, + (!downloading).then_some(Message::MissingFontsDownload), + button::primary, + ); let actions = row![ Space::new().width(content_width), - button(text(crate::t!("Skip")).size(12)) - .on_press_maybe((!downloading).then_some(Message::MissingFontsDismiss)) - .style(button::secondary) - .padding([6, 14]), + dialog_button_styled_opt( + crate::t!("Skip"), + (!downloading).then_some(Message::MissingFontsDismiss), + button::secondary, + ), download, ] .spacing(8); diff --git a/src/ui/window/options.rs b/src/ui/window/options.rs index 5e7cbdd9f..0e6d109f5 100644 --- a/src/ui/window/options.rs +++ b/src/ui/window/options.rs @@ -2,6 +2,7 @@ pub(crate) mod spacemouse; use crate::app::config::UiThemeConfig; use crate::app::settings::{CursorType, RightClickMode}; use crate::app::Message; +use crate::ui::style::form::{dialog_button, dialog_button_styled_opt}; use iced::widget::{ button, column, container, row, scrollable, slider, text, text_input, Space, }; @@ -315,18 +316,17 @@ pub fn view_window<'a>( // Changes show at once but are committed by OK / Apply; Close puts them // back (asking first when there is something to lose). - let ok = button(text(crate::t!("OK")).size(12)) - .on_press(Message::OptionsOk) - .padding([6, 18]) - .style(button::primary); - let apply = button(text(crate::t!("Apply")).size(12)) - .on_press_maybe(dirty.then_some(Message::OptionsApply)) - .padding([6, 18]) - .style(if dirty { button::secondary } else { button::text }); - let close = button(text(crate::tr!("action", "close")).size(12)) - .on_press(Message::OptionsClose) - .padding([6, 18]) - .style(button::secondary); + let ok = dialog_button(crate::t!("OK"), Message::OptionsOk, true); + let apply = dialog_button_styled_opt( + crate::t!("Apply"), + dirty.then_some(Message::OptionsApply), + if dirty { + button::secondary + } else { + button::text + }, + ); + let close = dialog_button(crate::tr!("action", "close"), Message::OptionsClose, false); let general = column![ text(crate::tr!("options", "language-section")).size(15), diff --git a/src/ui/window/xref_attach.rs b/src/ui/window/xref_attach.rs index 30251a6d7..47911ff02 100644 --- a/src/ui/window/xref_attach.rs +++ b/src/ui/window/xref_attach.rs @@ -5,7 +5,7 @@ use std::fmt; use iced::widget::{ - button, column, combo_box, container, image, pick_list, radio, row, text, text_input, Space, + button, column, combo_box, container, image, pick_list, row, text, text_input, Space, }; use iced::{Element, Fill, Length, Theme}; @@ -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, field_style}; +use crate::ui::style::form::{button_style, dialog_button, field_style, form_radio}; use crate::ui::window::block_definition::{group, labeled_checkbox}; /// One edit in the dialog. @@ -236,19 +236,18 @@ pub fn view_window<'a>( let reference_type = group( t!("Reference Type").into_owned(), column![ - radio( + form_radio( t!("Attachment"), false, Some(state.overlay), - |v| msg(XrefAttachMsg::Overlay(v)) - ) - .size(14) - .text_size(11), - radio(t!("Overlay"), true, Some(state.overlay), |v| msg( - XrefAttachMsg::Overlay(v) - )) - .size(14) - .text_size(11), + |v| msg(XrefAttachMsg::Overlay(v)), + ), + form_radio( + t!("Overlay"), + true, + Some(state.overlay), + |v| msg(XrefAttachMsg::Overlay(v)), + ), ] .spacing(6), Length::Shrink, @@ -385,25 +384,16 @@ pub fn view_window<'a>( t!("Show Details") }; let footer = row![ - button(text(details_label).size(11)) + button(text(details_label).size(12)) .on_press(msg(XrefAttachMsg::Details(!state.details))) - .style(button_style(false)) - .padding([4, 12]), + .style(button::secondary) + .padding([6, 18]), Space::new().width(Fill), - button(text(t!("OK")).size(11)) - .on_press(msg(XrefAttachMsg::Apply)) - .style(button_style(true)) - .padding([4, 16]), - button(text(t!("Cancel")).size(11)) - .on_press(Message::CloseModal) - .style(button_style(false)) - .padding([4, 12]), - button(text(t!("Help")).size(11)) - .on_press(msg(XrefAttachMsg::Help)) - .style(button_style(false)) - .padding([4, 12]), + dialog_button(t!("OK"), msg(XrefAttachMsg::Apply), true), + dialog_button(t!("Cancel"), Message::CloseModal, false), + dialog_button(t!("Help"), msg(XrefAttachMsg::Help), false), ] - .spacing(6) + .spacing(8) .align_y(iced::Center); column![name_section, body, details, footer] diff --git a/src/ui/window/xref_help.rs b/src/ui/window/xref_help.rs index 09e07490c..86a101170 100644 --- a/src/ui/window/xref_help.rs +++ b/src/ui/window/xref_help.rs @@ -6,7 +6,8 @@ use crate::app::Message; use crate::ui::style::common::muted_style; -use iced::widget::{button, column, container, row, text, Space}; +use crate::ui::style::form::dialog_button; +use iced::widget::{column, container, row, text, Space}; use iced::{Background, Element, Fill, Length, Theme}; /// GitHub issues page linked from the help window, so web users can report @@ -41,14 +42,12 @@ pub fn view_window(sizing: crate::ui::modal::ModalSizing) -> Element<'static, Me .into_owned(), ); - let report_button = button(text(crate::t!("Report an issue")).size(12)) - .on_press(Message::OpenUrl(XREF_ISSUES_URL.to_string())) - .style(button::primary) - .padding([6, 18]); - let close_button = button(text(crate::t!("Close")).size(12)) - .on_press(Message::CloseModal) - .style(button::secondary) - .padding([6, 18]); + let report_button = dialog_button( + crate::t!("Report an issue"), + Message::OpenUrl(XREF_ISSUES_URL.to_string()), + true, + ); + let close_button = dialog_button(crate::t!("Close"), Message::CloseModal, false); let action_row: Element<'static, Message> = row![ Space::new().width(Fill), report_button,