diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 643e7dab9..c601eb213 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -1372,6 +1372,8 @@ common = .color-not-a-book = ليس كتاب ألوان .color-book-encrypted = كتاب الألوان هذا مشفر ولا يمكن قراءته .color-book-empty = لا يحتوي كتاب الألوان على ألوان + .color-book-locations = مواقع كتب الألوان + .color-book-locations-hint = المجلدات التي يُبحث فيها عن كتب الألوان ‎.acb. يُنسخ الكتاب الذي يُحمَّل في اختيار اللون إلى المجلد الأول. .cad-color-index-aci-10-249 = فهرس ألوان ACI 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = الرسم متساوي القياس diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 8bc2bce14..ed8c27b53 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = Не е цветови каталог .color-book-encrypted = Този цветови каталог е шифрован и не може да бъде прочетен .color-book-empty = Цветовият каталог няма цветове + .color-book-locations = Местоположения на цветови каталози + .color-book-locations-hint = Папки, в които се търсят цветови каталози .acb. Каталог, зареден в „Избор на цвят“, се копира в първата папка. .cad-color-index-aci-10-249 = ACI Цветови индекс 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Изометрично чертане diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index a10b59019..51e95ebef 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = Nejde o barevný vzorník .color-book-encrypted = Tento barevný vzorník je zašifrovaný a nelze jej přečíst .color-book-empty = Barevný vzorník neobsahuje žádné barvy + .color-book-locations = Umístění barevných vzorníků + .color-book-locations-hint = Složky, ve kterých se hledají barevné vzorníky .acb. Vzorník načtený ve Výběru barvy se zkopíruje do první složky. .cad-color-index-aci-10-249 = Barevný index ACI 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Izometrické kreslení diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 7ec27fa3c..03da184fe 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -1360,6 +1360,8 @@ common = .color-not-a-book = Kein Farbbuch .color-book-encrypted = Dieses Farbbuch ist verschlüsselt und kann nicht gelesen werden .color-book-empty = Das Farbbuch enthält keine Farben + .color-book-locations = Speicherorte für Farbbücher + .color-book-locations-hint = Ordner, in denen nach .acb-Farbbüchern gesucht wird. Ein in „Farbe wählen“ geladenes Farbbuch wird in den ersten Ordner kopiert. .cad-color-index-aci-10-249 = ACI Farbindex 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Isometrisches Zeichnen diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index 2d739abfd..26ac610cd 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -1361,6 +1361,8 @@ common = .color-not-a-book = Δεν είναι βιβλίο χρωμάτων .color-book-encrypted = Αυτό το βιβλίο χρωμάτων είναι κρυπτογραφημένο και δεν μπορεί να διαβαστεί .color-book-empty = Το βιβλίο χρωμάτων δεν έχει χρώματα + .color-book-locations = Θέσεις βιβλίων χρωμάτων + .color-book-locations-hint = Φάκελοι στους οποίους αναζητούνται βιβλία χρωμάτων .acb. Ένα βιβλίο που φορτώνεται στην Επιλογή χρώματος αντιγράφεται στον πρώτο φάκελο. .cad-color-index-aci-10-249 = Δείκτης χρώματος ACI 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Ισομετρική σχεδίαση diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index de4a4cd78..ee3cb8fc8 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -1360,6 +1360,8 @@ common = .color-not-a-book = Not a color book .color-book-encrypted = This color book is encrypted and cannot be read .color-book-empty = The color book has no colors + .color-book-locations = Color book locations + .color-book-locations-hint = Folders searched for .acb color books. A book loaded in Select Color is copied into the first folder. .cad-color-index-aci-10-249 = CAD Color Index (ACI) 10–249 .grayscale-250-255 = Grayscale 250–255 .isometric-drafting = Isometric drafting diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 6ccf8988e..233b63454 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -1360,6 +1360,8 @@ common = .color-not-a-book = No es un libro de colores .color-book-encrypted = Este libro de colores está cifrado y no se puede leer .color-book-empty = El libro de colores no tiene colores + .color-book-locations = Ubicaciones de libros de colores + .color-book-locations-hint = Carpetas en las que se buscan libros de colores .acb. Un libro cargado en Seleccionar color se copia en la primera carpeta. .cad-color-index-aci-10-249 = Índice de color ACI 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Dibujo isométrico diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 7dff34f02..0d856e192 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = Ei värikirja .color-book-encrypted = Tämä värikirja on salattu eikä sitä voi lukea .color-book-empty = Värikirjassa ei ole värejä + .color-book-locations = Värikirjojen sijainnit + .color-book-locations-hint = Kansiot, joista etsitään .acb-värikirjoja. Värin valinnassa ladattu värikirja kopioidaan ensimmäiseen kansioon. .cad-color-index-aci-10-249 = ACI väriindeksi 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Isometrinen piirtäminen diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 6e514a155..de4e2a87e 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -1360,6 +1360,8 @@ common = .color-not-a-book = Ce n'est pas un carnet de couleurs .color-book-encrypted = Ce carnet de couleurs est chiffré et ne peut pas être lu .color-book-empty = Le carnet de couleurs ne contient aucune couleur + .color-book-locations = Emplacements des carnets de couleurs + .color-book-locations-hint = Dossiers où rechercher les carnets de couleurs .acb. Un carnet chargé dans Sélectionner une couleur est copié dans le premier dossier. .cad-color-index-aci-10-249 = ACI Indice de couleur 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Dessin isométrique diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index d5d58eb92..5745a31fe 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -1348,6 +1348,8 @@ common = .color-not-a-book = यह रंग पुस्तक नहीं है .color-book-encrypted = यह रंग पुस्तक एन्क्रिप्टेड है और पढ़ी नहीं जा सकती .color-book-empty = रंग पुस्तक में कोई रंग नहीं है + .color-book-locations = रंग पुस्तकों के स्थान + .color-book-locations-hint = वे फ़ोल्डर जिनमें .acb रंग पुस्तकें खोजी जाती हैं। रंग चुनें में लोड की गई पुस्तक पहले फ़ोल्डर में कॉपी की जाती है। .cad-color-index-aci-10-249 = ACI रंग सूचकांक 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = सममितीय आरेखन diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index ae28ca775..cf22bb3fb 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = Nem színkönyv .color-book-encrypted = Ez a színkönyv titkosított, és nem olvasható .color-book-empty = A színkönyv nem tartalmaz színeket + .color-book-locations = Színkönyvek helye + .color-book-locations-hint = Mappák, amelyekben a program .acb színkönyveket keres. A Szín kiválasztása ablakban betöltött színkönyv az első mappába másolódik. .cad-color-index-aci-10-249 = ACI színindex 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Izometrikus rajzolás diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index d19c64fd4..77bc17df5 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = Non è un album colori .color-book-encrypted = Questo album colori è crittografato e non può essere letto .color-book-empty = L'album colori non contiene colori + .color-book-locations = Percorsi degli album colori + .color-book-locations-hint = Cartelle in cui cercare gli album colori .acb. Un album caricato in Seleziona colore viene copiato nella prima cartella. .cad-color-index-aci-10-249 = ACI Indice colori 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Disegno isometrico diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 1d3cc642d..c16fa3b08 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -1350,6 +1350,8 @@ common = .color-not-a-book = カラー ブックではありません .color-book-encrypted = このカラー ブックは暗号化されているため読み込めません .color-book-empty = カラー ブックに色がありません + .color-book-locations = カラー ブックの場所 + .color-book-locations-hint = .acb カラー ブックを検索するフォルダです。[色を選択] で読み込んだブックは最初のフォルダにコピーされます。 .cad-color-index-aci-10-249 = ACI カラーインデックス 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = アイソメ図作成 diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 81b836734..1bbaea5c3 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = 색상표가 아닙니다 .color-book-encrypted = 이 색상표는 암호화되어 있어 읽을 수 없습니다 .color-book-empty = 색상표에 색상이 없습니다 + .color-book-locations = 색상표 위치 + .color-book-locations-hint = .acb 색상표를 찾는 폴더입니다. 색상 선택에서 로드한 색상표는 첫 번째 폴더에 복사됩니다. .cad-color-index-aci-10-249 = ACI 컬러 인덱스 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = 등각 제도 diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index e0a414a24..f8527bb9f 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -1365,6 +1365,8 @@ common = .color-not-a-book = ບໍ່ແມ່ນປຶ້ມສີ .color-book-encrypted = ປຶ້ມສີນີ້ຖືກເຂົ້າລະຫັດ ແລະອ່ານບໍ່ໄດ້ .color-book-empty = ປຶ້ມສີບໍ່ມີສີ + .color-book-locations = ທີ່ຕັ້ງປຶ້ມສີ + .color-book-locations-hint = ໂຟນເດີທີ່ໃຊ້ຊອກຫາປຶ້ມສີ .acb. ປຶ້ມທີ່ໂຫຼດໃນເລືອກສີຈະຖືກສຳເນົາໄປໃສ່ໂຟນເດີທຳອິດ. .cad-color-index-aci-10-249 = CAD Color Index (ACI) 10–249 .grayscale-250-255 = ລະດັບສີເທົາ 250–255 .isometric-drafting = ການຂຽນແບບໄອໂຊເມຕຣິກ diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 2da6d4c6d..730ea01da 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -1358,6 +1358,8 @@ common = .color-not-a-book = Geen kleurboek .color-book-encrypted = Dit kleurboek is versleuteld en kan niet worden gelezen .color-book-empty = Het kleurboek bevat geen kleuren + .color-book-locations = Locaties van kleurboeken + .color-book-locations-hint = Mappen waarin naar .acb-kleurboeken wordt gezocht. Een kleurboek dat in Kleur selecteren wordt geladen, wordt naar de eerste map gekopieerd. .cad-color-index-aci-10-249 = ACI Kleurindex 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Isometrisch tekenen diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 1874e7b4e..92554bcf4 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = To nie jest wzornik kolorów .color-book-encrypted = Ten wzornik kolorów jest zaszyfrowany i nie można go odczytać .color-book-empty = Wzornik kolorów nie zawiera kolorów + .color-book-locations = Lokalizacje wzorników kolorów + .color-book-locations-hint = Foldery przeszukiwane w poszukiwaniu wzorników kolorów .acb. Wzornik wczytany w oknie Wybierz kolor jest kopiowany do pierwszego folderu. .cad-color-index-aci-10-249 = Indeks kolorów ACI 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Rysowanie izometryczne diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index a879ecaac..e31741d24 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -1360,6 +1360,8 @@ common = .color-not-a-book = Não é um catálogo de cores .color-book-encrypted = Este catálogo de cores é criptografado e não pode ser lido .color-book-empty = O catálogo de cores não tem cores + .color-book-locations = Locais dos catálogos de cores + .color-book-locations-hint = Pastas pesquisadas em busca de catálogos de cores .acb. Um catálogo carregado em Selecionar cor é copiado para a primeira pasta. .cad-color-index-aci-10-249 = Índice de cor ACI 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Desenho isométrico diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index bd1b67d28..e00da7b3c 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -1363,6 +1363,8 @@ common = .color-not-a-book = Это не альбом цветов .color-book-encrypted = Этот альбом цветов зашифрован и не может быть прочитан .color-book-empty = В альбоме цветов нет цветов + .color-book-locations = Расположение альбомов цветов + .color-book-locations-hint = Папки, в которых ищутся альбомы цветов .acb. Альбом, загруженный в окне «Выбор цвета», копируется в первую папку. .cad-color-index-aci-10-249 = Индекс цвета ACI 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = Изометрическое черчение diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 6f8efd839..37fbeb6ca 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -1346,6 +1346,8 @@ common = .color-not-a-book = Bir renk kitabı değil .color-book-encrypted = Bu renk kitabı şifreli ve okunamıyor .color-book-empty = Renk kitabında renk yok + .color-book-locations = Renk kitabı konumları + .color-book-locations-hint = Bu klasörlerde .acb renk kitapları aranır. Renk Seç'te yüklenen bir kitap ilk klasöre kopyalanır. .cad-color-index-aci-10-249 = ACI renk dizini 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = İzometrik çizim diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 797197fdd..85e50033a 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -1347,6 +1347,8 @@ common = .color-not-a-book = 不是配色系统 .color-book-encrypted = 此配色系统已加密,无法读取 .color-book-empty = 配色系统中没有颜色 + .color-book-locations = 配色系统位置 + .color-book-locations-hint = 搜索 .acb 配色系统的文件夹。在“选择颜色”中加载的配色系统会复制到第一个文件夹。 .cad-color-index-aci-10-249 = ACI 颜色索引 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = 等轴测绘图 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index b08b84662..2d89f57eb 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -1359,6 +1359,8 @@ common = .color-not-a-book = 不是配色系統 .color-book-encrypted = 此配色系統已加密,無法讀取 .color-book-empty = 配色系統中沒有顏色 + .color-book-locations = 配色系統位置 + .color-book-locations-hint = 搜尋 .acb 配色系統的資料夾。在「選取顏色」中載入的配色系統會複製到第一個資料夾。 .cad-color-index-aci-10-249 = ACI 顏色索引 10–249 .grayscale-250-255 = { common.grayscale } 250–255 .isometric-drafting = 等角製圖 diff --git a/src/app/mod.rs b/src/app/mod.rs index 3906e3dc1..e8435b03d 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -965,9 +965,10 @@ pub(super) struct OpenCADStudio { // ── Floating panel windows ──────────────────────────────────────────── /// The open Select Color panel, if any. color_picker: Option, - /// Colour books loaded this session, read lazily from `color_book_paths`. + /// The built-in colour books and those in `color_book_dirs`, read + /// lazily when the Select Color panel opens. color_books: std::sync::Arc>, - color_book_paths: Vec, + color_book_dirs: Vec, color_books_read: bool, /// Recently committed real colours, newest first. /// ACI and True Color remain semantically distinct even when their RGB matches. @@ -4174,8 +4175,13 @@ pub enum Message { /// Commit the colour chosen in the shared picker, with its `BOOK$NAME` /// when it comes from a colour book. ColorWindowPick(codec::types::Color, Option), - /// An `.acb` file was read: the book and, on desktop, where it lives. - ColorBookLoaded(Result<(crate::ui::color_select::book::ColorBook, Option), String>), + /// An `.acb` file was read (and, on desktop, copied into the first + /// colour book folder). + ColorBookLoaded(Result), + /// Options › Files: pick a folder to search for colour books. + ColorBookDirBrowse, + ColorBookDirAdd(String), + ColorBookDirRemove(usize), /// Set a block/linetype Handle field on the selected dim style from a /// dropdown of available block-records / linetypes (by name). DsSetHandle { @@ -4608,7 +4614,7 @@ impl OpenCADStudio { web_autosave_clear_after: Vec::new(), color_picker: None, color_books: Default::default(), - color_book_paths: Vec::new(), + color_book_dirs: Vec::new(), color_books_read: false, active_modal: None, hyperlink_editor_handles: Vec::new(), diff --git a/src/app/settings.rs b/src/app/settings.rs index 2a3795ce2..a70db4532 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -619,9 +619,10 @@ pub struct UserSettings { /// Blocks palette: library folders or drawings, last used first. #[serde(default)] pub block_libraries: Vec, - /// Colour book (.acb) files loaded in the Select Color panel. - #[serde(default)] - pub color_books: Vec, + /// Folders searched for `.acb` colour books. Books loaded in the Select + /// Color panel are copied into the first one. + #[serde(default = "default_color_book_dirs")] + pub color_book_dirs: Vec, /// Blocks palette view: 0 extra large, 1 large, 2 medium, 3 small icons, /// 4 details, 5 list. #[serde(default = "default_block_view")] @@ -749,6 +750,15 @@ fn default_grid_major() -> u32 { 5 } +/// One folder in the config directory; none on the web, which has no folders. +fn default_color_book_dirs() -> Vec { + #[cfg(not(target_arch = "wasm32"))] + if let Some(dir) = crate::config::config_dir() { + return vec![dir.join("color_books").display().to_string()]; + } + Vec::new() +} + fn default_true() -> bool { true } @@ -904,7 +914,7 @@ impl Default for UserSettings { block_recent: Vec::new(), block_favorites: Vec::new(), block_libraries: Vec::new(), - color_books: Vec::new(), + color_book_dirs: default_color_book_dirs(), block_palette_view: 1, block_insert: BlockInsertOptions::default(), block_mru_list: 50, diff --git a/src/app/update/file.rs b/src/app/update/file.rs index 082199c2a..a38f941e7 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -860,7 +860,7 @@ impl OpenCADStudio { block_recent: self.block_palette.recent.clone(), block_favorites: self.block_palette.favorites.clone(), block_libraries: self.block_palette.libraries.clone(), - color_books: self.color_book_paths.clone(), + color_book_dirs: self.color_book_dirs.clone(), block_palette_view: self.block_palette.view as u8, block_insert: self.block_palette.options.clone(), block_mru_list: self.block_mru_list, @@ -1026,7 +1026,10 @@ impl OpenCADStudio { self.block_palette.recent = s.block_recent.clone(); self.block_palette.favorites = s.block_favorites.clone(); self.block_palette.libraries = s.block_libraries.clone(); - self.color_book_paths = s.color_books.clone(); + if self.color_book_dirs != s.color_book_dirs { + self.color_book_dirs = s.color_book_dirs.clone(); + self.color_books_read = false; + } self.block_palette.view = crate::ui::window::block_palette::ViewMode::from_u8(s.block_palette_view); self.block_palette.set_options(s.block_insert.clone()); diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 01630caea..8e8f1fda0 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -11596,25 +11596,38 @@ impl OpenCADStudio { Some(crate::ui::color_select::Outcome::Commit(color, name)) => { self.update(Message::ColorWindowPick(color, name)) } - Some(crate::ui::color_select::Outcome::LoadBook) => Task::perform( - async { - let file = crate::sys::file_dialog() - .add_filter(crate::t!("Color book").as_ref(), &["acb", "ACB"]) - .pick_file() - .await - .ok_or_else(|| "Cancelled".to_string())?; - let book = crate::ui::color_select::book::parse( - &String::from_utf8_lossy(&file.read().await), - )?; - // The web build has no paths to read again later. - #[cfg(not(target_arch = "wasm32"))] - let path = Some(crate::sys::handle_path(&file).display().to_string()); - #[cfg(target_arch = "wasm32")] - let path = None; - Ok((book, path)) - }, - Message::ColorBookLoaded, - ), + Some(crate::ui::color_select::Outcome::LoadBook) => { + // Copied into the first colour book folder so it stays + // when the original moves; the web keeps it for the + // session only. + let folder = self.color_book_dirs.first().cloned(); + Task::perform( + async move { + let file = crate::sys::file_dialog() + .add_filter(crate::t!("Color book").as_ref(), &["acb", "ACB"]) + .pick_file() + .await + .ok_or_else(|| "Cancelled".to_string())?; + let bytes = file.read().await; + let book = crate::ui::color_select::book::parse( + &String::from_utf8_lossy(&bytes), + )?; + #[cfg(not(target_arch = "wasm32"))] + if let Some(folder) = folder { + let folder = std::path::PathBuf::from(folder); + std::fs::create_dir_all(&folder) + .and_then(|()| { + std::fs::write(folder.join(file.file_name()), &bytes) + }) + .map_err(|e| e.to_string())?; + } + #[cfg(target_arch = "wasm32")] + let _ = folder; + Ok(book) + }, + Message::ColorBookLoaded, + ) + } Some(crate::ui::color_select::Outcome::Cancel) => { self.color_picker = None; Task::none() @@ -11651,7 +11664,7 @@ impl OpenCADStudio { } self.on_color_window_pick(color, name) } - Message::ColorBookLoaded(Ok((book, path))) => { + Message::ColorBookLoaded(Ok(book)) => { // A book loaded again replaces the earlier copy. let books = std::sync::Arc::make_mut(&mut self.color_books); let index = match books.iter().position(|b| b.name == book.name) { @@ -11664,10 +11677,6 @@ impl OpenCADStudio { books.len() - 1 } }; - if let Some(path) = path.filter(|path| !self.color_book_paths.contains(path)) { - self.color_book_paths.push(path); - self.save_config(); - } if let Some(picker) = self.color_picker.as_mut() { picker.show_book(std::sync::Arc::clone(&self.color_books), index); } @@ -11679,6 +11688,35 @@ impl OpenCADStudio { } Task::none() } + #[cfg(not(target_arch = "wasm32"))] + Message::ColorBookDirBrowse => Task::perform( + async { + crate::sys::file_dialog() + .pick_folder() + .await + .map(|handle| crate::sys::handle_path(&handle).display().to_string()) + }, + |dir| dir.map_or(Message::Noop, Message::ColorBookDirAdd), + ), + // Browsers expose no folder picker. + #[cfg(target_arch = "wasm32")] + Message::ColorBookDirBrowse => Task::none(), + Message::ColorBookDirAdd(dir) => { + if !self.color_book_dirs.contains(&dir) { + self.color_book_dirs.push(dir); + self.color_books_read = false; + self.persist_settings_if_changed(); + } + Task::none() + } + Message::ColorBookDirRemove(i) => { + if i < self.color_book_dirs.len() { + self.color_book_dirs.remove(i); + self.color_books_read = false; + self.persist_settings_if_changed(); + } + Task::none() + } Message::DsSetHandle { field, value } => self.on_ds_set_handle(field, value), } } @@ -11759,9 +11797,9 @@ impl OpenCADStudio { document.layers.get(layer).map(|layer| layer.color) } - /// Read the built-in colour books and the ones remembered in the - /// settings, once per session. A loaded book replaces a built-in one of - /// the same name. + /// Read the built-in colour books and every `.acb` in the colour book + /// folders, once until the folders change. A book in a folder replaces a + /// built-in one of the same name. fn read_color_books(&mut self) { if std::mem::replace(&mut self.color_books_read, true) { return; @@ -11771,8 +11809,21 @@ impl OpenCADStudio { let mut books: Vec<_> = book::builtin().collect(); #[cfg(not(target_arch = "wasm32"))] for loaded in self - .color_book_paths + .color_book_dirs .iter() + .flat_map(|dir| { + let mut files: Vec<_> = std::fs::read_dir(dir) + .into_iter() + .flatten() + .filter_map(|entry| entry.ok().map(|entry| entry.path())) + .filter(|path| { + path.extension() + .is_some_and(|ext| ext.eq_ignore_ascii_case("acb")) + }) + .collect(); + files.sort(); + files + }) .filter_map(|path| std::fs::read_to_string(path).ok()) .filter_map(|text| book::parse(&text).ok()) { diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index 348c4bd97..57a88e393 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -367,6 +367,7 @@ impl OpenCADStudio { } }, &self.qnew_template, + &self.color_book_dirs, self.double_click_block_refedit, self.double_click_block_attedit, self.cursor_type, diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 3403cd85c..9ce668e84 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -4736,6 +4736,8 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "Not a color book" => Some(("common", "color-not-a-book")), "This color book is encrypted and cannot be read" => Some(("common", "color-book-encrypted")), "The color book has no colors" => Some(("common", "color-book-empty")), + "Color book locations" => Some(("common", "color-book-locations")), + "Folders searched for .acb color books. A book loaded in Select Color is copied into the first folder." => Some(("common", "color-book-locations-hint")), "CAD Color Index (ACI) 10–249" => Some(("common", "cad-color-index-aci-10-249")), "Grayscale 250–255" => Some(("common", "grayscale-250-255")), "Isometric drafting" => Some(("common", "isometric-drafting")), diff --git a/src/ui/color_select/picker.rs b/src/ui/color_select/picker.rs index 6882ae6c6..157c139c0 100644 --- a/src/ui/color_select/picker.rs +++ b/src/ui/color_select/picker.rs @@ -672,8 +672,9 @@ impl ColorPicker { .into() } - /// Grid or list toggle for the Document tab: dark on the accent colour - /// when active, like the OK button. + /// Grid or list toggle for the Document tab, styled like the app's other + /// toggles: light accent when active, its icon in that accent's text + /// colour. fn view_button(&self, icon: &'static [u8], view: DocView) -> Element<'_, Message> { let active = self.doc_view == view; button( @@ -683,7 +684,7 @@ impl ColorPicker { .height(18) .style(move |theme: &Theme, _status| svg::Style { color: Some(if active { - theme.palette().primary.base.text + theme.palette().primary.weak.text } else { theme.palette().background.base.text }), @@ -695,13 +696,7 @@ impl ColorPicker { .width(CONTROL_HEIGHT) .height(CONTROL_HEIGHT) .padding(0) - .style(move |theme: &Theme, status| { - if active { - button::primary(theme, status) - } else { - toggle_style(theme, false, status) - } - }) + .style(move |theme: &Theme, status| toggle_style(theme, active, status)) .into() } @@ -750,21 +745,20 @@ impl ColorPicker { .center(Length::Fill) .into(); }; - // The book list as wide as the colour bars; the search box and the - // load button over the page strip and the RGB values. + // The book list and its load button over the colour bars; the + // search box over the page strip and the RGB values, to the end. let books = Arc::clone(&self.books); let top = row![ - pick_list( - Some(self.book), - (0..self.books.len()).collect::>(), - move |&i| books[i].name.clone(), - ) - .on_select(|i| picker_msg(ColorPickerMsg::Book(i))) - .text_size(12) - .padding([6, 8]) - .width(Length::Fill), row![ - search_box(&self.book_query, |query| ColorPickerMsg::BookQuery(query)), + pick_list( + Some(self.book), + (0..self.books.len()).collect::>(), + move |&i| books[i].name.clone(), + ) + .on_select(|i| picker_msg(ColorPickerMsg::Book(i))) + .text_size(12) + .padding([6, 8]) + .width(Length::Fill), load_button( container( secondary_icon(crate::ui::icons::themed_handle( @@ -781,6 +775,11 @@ impl ColorPicker { .padding(0), ] .spacing(6) + .align_y(iced::Center) + .width(Length::Fill), + container(search_box(&self.book_query, |query| { + ColorPickerMsg::BookQuery(query) + })) .width(SIDE_WIDTH), ] .spacing(10) diff --git a/src/ui/window/options.rs b/src/ui/window/options.rs index 5b1ea49f9..e91aabe35 100644 --- a/src/ui/window/options.rs +++ b/src/ui/window/options.rs @@ -204,6 +204,7 @@ pub fn view_window<'a>( drawing_prefs: DrawingPrefs, folders: Folders, qnew_template: &'a str, + color_book_dirs: &'a [String], double_click_block_refedit: bool, double_click_block_attedit: bool, cursor_type: CursorType, @@ -1697,6 +1698,49 @@ pub fn view_window<'a>( .align_y(iced::Center) }; + // Searched for `.acb` colour books; editable, unlike the folders above. + // Browsers have no folders, so the web build leaves it out. + let color_book_locations = |dirs: &'a [String]| -> Element<'a, Message> { + if cfg!(target_arch = "wasm32") { + return Space::new().into(); + } + let list = dirs + .iter() + .enumerate() + .fold(column![].spacing(6), |list, (i, dir)| { + list.push( + row![ + text(dir.as_str()).size(11).width(Fill), + button(text(crate::t!("Open folder")).size(11)) + .padding([4, 10]) + .style(button::secondary) + .on_press(Message::OpenFolder(dir.clone())), + button(text(crate::t!("Remove")).size(11)) + .padding([4, 10]) + .style(button::secondary) + .on_press(Message::ColorBookDirRemove(i)), + ] + .spacing(10) + .align_y(iced::Center), + ) + }); + column![ + text(crate::t!("Color book locations")).size(12), + text(crate::t!( + "Folders searched for .acb color books. A book loaded in Select Color is copied into the first folder." + )) + .size(11), + list, + button(text(crate::t!("Add folder…")).size(11)) + .padding([4, 10]) + .style(button::secondary) + .on_press(Message::ColorBookDirBrowse), + Space::new().height(16), + ] + .spacing(6) + .into() + }; + let files = column![ text(crate::t!("Files")).size(15), Space::new().height(6), @@ -1716,6 +1760,7 @@ pub fn view_window<'a>( Space::new().height(12), folder_row(crate::t!("Fonts"), folders.fonts.clone()), Space::new().height(16), + color_book_locations(color_book_dirs), text(crate::t!("Default template for new drawings")).size(12), Space::new().height(4), row![