Select Color: bottom-aligned field, list view, name sorting, book names

- True Color opens in RGB; the gradient field, hex and nearest index sit
  at the bottom so CMYK's fourth slider doesn't move them; slider rows
  closer together.
- Document tab: grid or list view (list drawn like the colour-book bars),
  and Name A–Z / Z–A sorting (translated in all locales). Swatches of
  colour-book colours show the colour's name.
- A colour-book colour picked as the current colour (nothing selected)
  keeps its BOOK$NAME on the objects drawn with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
This commit is contained in:
Claude committed 2026-10-14 13:13:00 +00:00
1 parent ecdead20c7
commit b039e3dc48
33 files changed
+221 -53

No files matched your search

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#B4B6B9" stroke-width="1.8" stroke-linejoin="round"><rect x="4" y="4" width="16" height="4" rx="1"/><rect x="4" y="10" width="16" height="4" rx="1"/><rect x="4" y="16" width="16" height="4" rx="1"/></svg>

After

Width:  |  Height:  |  Size: 285 B

+2
View File
@@ -1359,6 +1359,8 @@ common =
.color-recently-used = المستخدمة مؤخرًا .color-recently-used = المستخدمة مؤخرًا
.color-most-used = الأكثر استخدامًا .color-most-used = الأكثر استخدامًا
.color-number = رقم اللون .color-number = رقم اللون
.color-name-az = الاسم أ–ي
.color-name-za = الاسم ي–أ
.color-in-drawing = الألوان في هذا الرسم .color-in-drawing = الألوان في هذا الرسم
.color-sort = فرز .color-sort = فرز
.color-no-drawing-colors = لا توجد ألوان في هذا الرسم بعد .color-no-drawing-colors = لا توجد ألوان في هذا الرسم بعد
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = Наскоро използвани .color-recently-used = Наскоро използвани
.color-most-used = Най-често използвани .color-most-used = Най-често използвани
.color-number = Номер на цвят .color-number = Номер на цвят
.color-name-az = Име А–Я
.color-name-za = Име Я–А
.color-in-drawing = Цветове в този чертеж .color-in-drawing = Цветове в този чертеж
.color-sort = Сортиране .color-sort = Сортиране
.color-no-drawing-colors = Все още няма цветове в този чертеж .color-no-drawing-colors = Все още няма цветове в този чертеж
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = Naposledy použité .color-recently-used = Naposledy použité
.color-most-used = Nejpoužívanější .color-most-used = Nejpoužívanější
.color-number = Číslo barvy .color-number = Číslo barvy
.color-name-az = Název A–Z
.color-name-za = Název Z–A
.color-in-drawing = Barvy v tomto výkresu .color-in-drawing = Barvy v tomto výkresu
.color-sort = Řadit .color-sort = Řadit
.color-no-drawing-colors = V tomto výkresu zatím nejsou žádné barvy .color-no-drawing-colors = V tomto výkresu zatím nejsou žádné barvy
+2
View File
@@ -1347,6 +1347,8 @@ common =
.color-recently-used = Zuletzt verwendet .color-recently-used = Zuletzt verwendet
.color-most-used = Am häufigsten verwendet .color-most-used = Am häufigsten verwendet
.color-number = Farbnummer .color-number = Farbnummer
.color-name-az = Name A–Z
.color-name-za = Name Z–A
.color-in-drawing = Farben in dieser Zeichnung .color-in-drawing = Farben in dieser Zeichnung
.color-sort = Sortieren .color-sort = Sortieren
.color-no-drawing-colors = Noch keine Farben in dieser Zeichnung .color-no-drawing-colors = Noch keine Farben in dieser Zeichnung
+2
View File
@@ -1348,6 +1348,8 @@ common =
.color-recently-used = Πρόσφατα χρησιμοποιημένα .color-recently-used = Πρόσφατα χρησιμοποιημένα
.color-most-used = Πιο συχνά χρησιμοποιημένα .color-most-used = Πιο συχνά χρησιμοποιημένα
.color-number = Αριθμός χρώματος .color-number = Αριθμός χρώματος
.color-name-az = Όνομα Α–Ω
.color-name-za = Όνομα Ω–Α
.color-in-drawing = Χρώματα σε αυτό το σχέδιο .color-in-drawing = Χρώματα σε αυτό το σχέδιο
.color-sort = Ταξινόμηση .color-sort = Ταξινόμηση
.color-no-drawing-colors = Δεν υπάρχουν ακόμη χρώματα σε αυτό το σχέδιο .color-no-drawing-colors = Δεν υπάρχουν ακόμη χρώματα σε αυτό το σχέδιο
+2
View File
@@ -1347,6 +1347,8 @@ common =
.color-recently-used = Recently used .color-recently-used = Recently used
.color-most-used = Most used .color-most-used = Most used
.color-number = Color number .color-number = Color number
.color-name-az = Name A–Z
.color-name-za = Name Z–A
.color-in-drawing = Colors in this drawing .color-in-drawing = Colors in this drawing
.color-sort = Sort .color-sort = Sort
.color-no-drawing-colors = No colors in this drawing yet .color-no-drawing-colors = No colors in this drawing yet
+2
View File
@@ -1347,6 +1347,8 @@ common =
.color-recently-used = Usados recientemente .color-recently-used = Usados recientemente
.color-most-used = Más usados .color-most-used = Más usados
.color-number = Número de color .color-number = Número de color
.color-name-az = Nombre A–Z
.color-name-za = Nombre Z–A
.color-in-drawing = Colores de este dibujo .color-in-drawing = Colores de este dibujo
.color-sort = Ordenar .color-sort = Ordenar
.color-no-drawing-colors = Aún no hay colores en este dibujo .color-no-drawing-colors = Aún no hay colores en este dibujo
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = Viimeksi käytetyt .color-recently-used = Viimeksi käytetyt
.color-most-used = Eniten käytetyt .color-most-used = Eniten käytetyt
.color-number = Värin numero .color-number = Värin numero
.color-name-az = Nimi A–Ö
.color-name-za = Nimi Ö–A
.color-in-drawing = Tämän piirustuksen värit .color-in-drawing = Tämän piirustuksen värit
.color-sort = Lajittele .color-sort = Lajittele
.color-no-drawing-colors = Tässä piirustuksessa ei ole vielä värejä .color-no-drawing-colors = Tässä piirustuksessa ei ole vielä värejä
+2
View File
@@ -1347,6 +1347,8 @@ common =
.color-recently-used = Utilisées récemment .color-recently-used = Utilisées récemment
.color-most-used = Les plus utilisées .color-most-used = Les plus utilisées
.color-number = Numéro de couleur .color-number = Numéro de couleur
.color-name-az = Nom A–Z
.color-name-za = Nom Z–A
.color-in-drawing = Couleurs de ce dessin .color-in-drawing = Couleurs de ce dessin
.color-sort = Trier .color-sort = Trier
.color-no-drawing-colors = Aucune couleur dans ce dessin pour l'instant .color-no-drawing-colors = Aucune couleur dans ce dessin pour l'instant
+2
View File
@@ -1335,6 +1335,8 @@ common =
.color-recently-used = हाल ही में उपयोग किए गए .color-recently-used = हाल ही में उपयोग किए गए
.color-most-used = सबसे अधिक उपयोग किए गए .color-most-used = सबसे अधिक उपयोग किए गए
.color-number = रंग संख्या .color-number = रंग संख्या
.color-name-az = नाम A–Z
.color-name-za = नाम Z–A
.color-in-drawing = इस ड्रॉइंग के रंग .color-in-drawing = इस ड्रॉइंग के रंग
.color-sort = क्रमबद्ध करें .color-sort = क्रमबद्ध करें
.color-no-drawing-colors = इस ड्रॉइंग में अभी तक कोई रंग नहीं है .color-no-drawing-colors = इस ड्रॉइंग में अभी तक कोई रंग नहीं है
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = Legutóbb használt .color-recently-used = Legutóbb használt
.color-most-used = Leggyakrabban használt .color-most-used = Leggyakrabban használt
.color-number = Színszám .color-number = Színszám
.color-name-az = Név A–Z
.color-name-za = Név Z–A
.color-in-drawing = Színek ebben a rajzban .color-in-drawing = Színek ebben a rajzban
.color-sort = Rendezés .color-sort = Rendezés
.color-no-drawing-colors = Ebben a rajzban még nincsenek színek .color-no-drawing-colors = Ebben a rajzban még nincsenek színek
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = Usati di recente .color-recently-used = Usati di recente
.color-most-used = Più usati .color-most-used = Più usati
.color-number = Numero colore .color-number = Numero colore
.color-name-az = Nome A–Z
.color-name-za = Nome Z–A
.color-in-drawing = Colori in questo disegno .color-in-drawing = Colori in questo disegno
.color-sort = Ordina .color-sort = Ordina
.color-no-drawing-colors = Ancora nessun colore in questo disegno .color-no-drawing-colors = Ancora nessun colore in questo disegno
+2
View File
@@ -1337,6 +1337,8 @@ common =
.color-recently-used = 最近使用した色 .color-recently-used = 最近使用した色
.color-most-used = 使用頻度の高い色 .color-most-used = 使用頻度の高い色
.color-number = 色番号 .color-number = 色番号
.color-name-az = 名前 (昇順)
.color-name-za = 名前 (降順)
.color-in-drawing = この図面の色 .color-in-drawing = この図面の色
.color-sort = 並べ替え .color-sort = 並べ替え
.color-no-drawing-colors = この図面にはまだ色がありません .color-no-drawing-colors = この図面にはまだ色がありません
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = 최근 사용 .color-recently-used = 최근 사용
.color-most-used = 가장 많이 사용 .color-most-used = 가장 많이 사용
.color-number = 색상 번호 .color-number = 색상 번호
.color-name-az = 이름 오름차순
.color-name-za = 이름 내림차순
.color-in-drawing = 이 도면의 색상 .color-in-drawing = 이 도면의 색상
.color-sort = 정렬 .color-sort = 정렬
.color-no-drawing-colors = 이 도면에는 아직 색상이 없습니다 .color-no-drawing-colors = 이 도면에는 아직 색상이 없습니다
+2
View File
@@ -1352,6 +1352,8 @@ common =
.color-recently-used = ໃຊ້ຫຼ້າສຸດ .color-recently-used = ໃຊ້ຫຼ້າສຸດ
.color-most-used = ໃຊ້ຫຼາຍທີ່ສຸດ .color-most-used = ໃຊ້ຫຼາຍທີ່ສຸດ
.color-number = ເລກສີ .color-number = ເລກສີ
.color-name-az = ຊື່ A–Z
.color-name-za = ຊື່ Z–A
.color-in-drawing = ສີໃນແບບແຕ້ມນີ້ .color-in-drawing = ສີໃນແບບແຕ້ມນີ້
.color-sort = ຈັດລຽງ .color-sort = ຈັດລຽງ
.color-no-drawing-colors = ຍັງບໍ່ມີສີໃນແບບແຕ້ມນີ້ .color-no-drawing-colors = ຍັງບໍ່ມີສີໃນແບບແຕ້ມນີ້
+2
View File
@@ -1345,6 +1345,8 @@ common =
.color-recently-used = Recent gebruikt .color-recently-used = Recent gebruikt
.color-most-used = Meest gebruikt .color-most-used = Meest gebruikt
.color-number = Kleurnummer .color-number = Kleurnummer
.color-name-az = Naam A–Z
.color-name-za = Naam Z–A
.color-in-drawing = Kleuren in deze tekening .color-in-drawing = Kleuren in deze tekening
.color-sort = Sorteren .color-sort = Sorteren
.color-no-drawing-colors = Nog geen kleuren in deze tekening .color-no-drawing-colors = Nog geen kleuren in deze tekening
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = Ostatnio używane .color-recently-used = Ostatnio używane
.color-most-used = Najczęściej używane .color-most-used = Najczęściej używane
.color-number = Numer koloru .color-number = Numer koloru
.color-name-az = Nazwa A–Z
.color-name-za = Nazwa Z–A
.color-in-drawing = Kolory w tym rysunku .color-in-drawing = Kolory w tym rysunku
.color-sort = Sortuj .color-sort = Sortuj
.color-no-drawing-colors = Brak kolorów w tym rysunku .color-no-drawing-colors = Brak kolorów w tym rysunku
+2
View File
@@ -1347,6 +1347,8 @@ common =
.color-recently-used = Usadas recentemente .color-recently-used = Usadas recentemente
.color-most-used = Mais usadas .color-most-used = Mais usadas
.color-number = Número da cor .color-number = Número da cor
.color-name-az = Nome A–Z
.color-name-za = Nome Z–A
.color-in-drawing = Cores neste desenho .color-in-drawing = Cores neste desenho
.color-sort = Ordenar .color-sort = Ordenar
.color-no-drawing-colors = Ainda não há cores neste desenho .color-no-drawing-colors = Ainda não há cores neste desenho
+2
View File
@@ -1350,6 +1350,8 @@ common =
.color-recently-used = Недавно использованные .color-recently-used = Недавно использованные
.color-most-used = Чаще всего используемые .color-most-used = Чаще всего используемые
.color-number = Номер цвета .color-number = Номер цвета
.color-name-az = Имя А–Я
.color-name-za = Имя Я–А
.color-in-drawing = Цвета в этом чертеже .color-in-drawing = Цвета в этом чертеже
.color-sort = Сортировка .color-sort = Сортировка
.color-no-drawing-colors = В этом чертеже пока нет цветов .color-no-drawing-colors = В этом чертеже пока нет цветов
+2
View File
@@ -1333,6 +1333,8 @@ common =
.color-recently-used = Son kullanılanlar .color-recently-used = Son kullanılanlar
.color-most-used = En çok kullanılanlar .color-most-used = En çok kullanılanlar
.color-number = Renk numarası .color-number = Renk numarası
.color-name-az = Ad A–Z
.color-name-za = Ad Z–A
.color-in-drawing = Bu çizimdeki renkler .color-in-drawing = Bu çizimdeki renkler
.color-sort = Sırala .color-sort = Sırala
.color-no-drawing-colors = Bu çizimde henüz renk yok .color-no-drawing-colors = Bu çizimde henüz renk yok
+2
View File
@@ -1334,6 +1334,8 @@ common =
.color-recently-used = 最近使用 .color-recently-used = 最近使用
.color-most-used = 最常使用 .color-most-used = 最常使用
.color-number = 颜色编号 .color-number = 颜色编号
.color-name-az = 名称 A–Z
.color-name-za = 名称 Z–A
.color-in-drawing = 此图形中的颜色 .color-in-drawing = 此图形中的颜色
.color-sort = 排序 .color-sort = 排序
.color-no-drawing-colors = 此图形中还没有颜色 .color-no-drawing-colors = 此图形中还没有颜色
+2
View File
@@ -1346,6 +1346,8 @@ common =
.color-recently-used = 最近使用 .color-recently-used = 最近使用
.color-most-used = 最常使用 .color-most-used = 最常使用
.color-number = 顏色編號 .color-number = 顏色編號
.color-name-az = 名稱 A–Z
.color-name-za = 名稱 Z–A
.color-in-drawing = 此圖面中的顏色 .color-in-drawing = 此圖面中的顏色
.color-sort = 排序 .color-sort = 排序
.color-no-drawing-colors = 此圖面中還沒有顏色 .color-no-drawing-colors = 此圖面中還沒有顏色
+10 -2
View File
@@ -358,7 +358,11 @@ impl OpenCADStudio {
if layer != "0" || entity.as_entity().layer().is_empty() { if layer != "0" || entity.as_entity().layer().is_empty() {
entity.as_entity_mut().set_layer(layer); entity.as_entity_mut().set_layer(layer);
} }
crate::scene::view::dispatch::apply_color(&mut entity, self.ribbon.active_color); crate::scene::view::dispatch::apply_named_color(
&mut entity,
self.ribbon.active_color,
self.ribbon.active_color_name(),
);
crate::scene::view::dispatch::apply_common_prop( crate::scene::view::dispatch::apply_common_prop(
&mut entity, &mut entity,
"linetype", "linetype",
@@ -476,7 +480,11 @@ impl OpenCADStudio {
if layer != "0" || entity.as_entity().layer().is_empty() { if layer != "0" || entity.as_entity().layer().is_empty() {
entity.as_entity_mut().set_layer(layer); entity.as_entity_mut().set_layer(layer);
} }
crate::scene::view::dispatch::apply_color(&mut entity, self.ribbon.active_color); crate::scene::view::dispatch::apply_named_color(
&mut entity,
self.ribbon.active_color,
self.ribbon.active_color_name(),
);
crate::scene::view::dispatch::apply_common_prop( crate::scene::view::dispatch::apply_common_prop(
&mut entity, &mut entity,
"linetype", "linetype",
+5 -1
View File
@@ -3149,7 +3149,11 @@ handles={handles_ms:.1} panel={:.1} ribbon={ribbon_ms:.1} tail={:.1} selected={}
} }
if !preserve_entity_style { if !preserve_entity_style {
crate::scene::view::dispatch::apply_color(&mut entity, self.ribbon.active_color); crate::scene::view::dispatch::apply_named_color(
&mut entity,
self.ribbon.active_color,
self.ribbon.active_color_name(),
);
crate::scene::view::dispatch::apply_common_prop( crate::scene::view::dispatch::apply_common_prop(
&mut entity, &mut entity,
"linetype", "linetype",
+4
View File
@@ -11699,6 +11699,9 @@ impl OpenCADStudio {
}; };
match target { match target {
crate::app::ColorPickTarget::Properties => {} crate::app::ColorPickTarget::Properties => {}
crate::app::ColorPickTarget::Ribbon => {
return (color, false, self.ribbon.active_color_name());
}
crate::app::ColorPickTarget::Layer(layer) => { crate::app::ColorPickTarget::Layer(layer) => {
let name = document.layers.get(layer).and_then(|layer| { let name = document.layers.get(layer).and_then(|layer| {
match (&layer.book_name, &layer.color_name) { match (&layer.book_name, &layer.color_name) {
@@ -11767,6 +11770,7 @@ impl OpenCADStudio {
let handles = self.property_target_handles(i); let handles = self.property_target_handles(i);
if handles.is_empty() { if handles.is_empty() {
let task = self.on_ribbon_color_changed(color); let task = self.on_ribbon_color_changed(color);
self.ribbon.active_color_book = name.map(|name| (color, name));
self.refresh_properties(); self.refresh_properties();
return task; return task;
} }
+5 -1
View File
@@ -1215,7 +1215,11 @@ impl OpenCADStudio {
Some(Message::PropColorFieldChanged { field, color }) Some(Message::PropColorFieldChanged { field, color })
} }
Some(crate::app::ColorPickTarget::MText) => Some(Message::MTextColorChanged(color)), Some(crate::app::ColorPickTarget::MText) => Some(Message::MTextColorChanged(color)),
Some(crate::app::ColorPickTarget::Ribbon) => Some(Message::RibbonColorChanged(color)), Some(crate::app::ColorPickTarget::Ribbon) => {
let task = self.update(Message::RibbonColorChanged(color));
self.ribbon.active_color_book = name.map(|name| (color, name));
return task;
}
Some(crate::app::ColorPickTarget::Layer(layer)) => { Some(crate::app::ColorPickTarget::Layer(layer)) => {
let i = self.active_tab; let i = self.active_tab;
match self.layer_panel_index(i, &layer) { match self.layer_panel_index(i, &layer) {
+2
View File
@@ -4723,6 +4723,8 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Recently used" => Some(("common", "color-recently-used")), "Recently used" => Some(("common", "color-recently-used")),
"Most used" => Some(("common", "color-most-used")), "Most used" => Some(("common", "color-most-used")),
"Color number" => Some(("common", "color-number")), "Color number" => Some(("common", "color-number")),
"Name A–Z" => Some(("common", "color-name-az")),
"Name Z–A" => Some(("common", "color-name-za")),
"Colors in this drawing" => Some(("common", "color-in-drawing")), "Colors in this drawing" => Some(("common", "color-in-drawing")),
"Sort" => Some(("common", "color-sort")), "Sort" => Some(("common", "color-sort")),
"No colors in this drawing yet" => Some(("common", "color-no-drawing-colors")), "No colors in this drawing yet" => Some(("common", "color-no-drawing-colors")),
+35 -11
View File
@@ -21,21 +21,35 @@ pub struct DocColor {
} }
impl DocColor { impl DocColor {
/// The few characters drawn on the swatch: the index number, `RGB`, or /// The text drawn on the swatch: the index number, `RGB`, or the
/// the first word of the colour book. /// colour-book colour's own name.
pub fn tag(&self) -> String { pub fn tag(&self) -> String {
match (&self.name, self.color) { match (&self.name, self.color) {
(Some(name), _) => name (Some(_), _) => self.label(),
.split(['$', ' '])
.next()
.unwrap_or_default()
.chars()
.take(5)
.collect(),
(None, AcadColor::Index(i)) => i.to_string(), (None, AcadColor::Index(i)) => i.to_string(),
_ => "RGB".to_string(), _ => "RGB".to_string(),
} }
} }
/// The name in the list view: the colour-book colour's own name (without
/// the book), `ACI n` or `#RRGGBB`.
pub fn label(&self) -> String {
match &self.name {
Some(name) => name
.split_once('$')
.map_or(name.as_str(), |(_, name)| name)
.to_string(),
None => math::short_name(self.color),
}
}
}
/// Swatches or a list of named bars.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DocView {
#[default]
Grid,
List,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
@@ -45,14 +59,18 @@ pub enum DocSort {
Usage, Usage,
Hue, Hue,
Number, Number,
NameAsc,
NameDesc,
} }
impl DocSort { impl DocSort {
pub const ALL: [DocSort; 4] = [ pub const ALL: [DocSort; 6] = [
DocSort::Recent, DocSort::Recent,
DocSort::Usage, DocSort::Usage,
DocSort::Hue, DocSort::Hue,
DocSort::Number, DocSort::Number,
DocSort::NameAsc,
DocSort::NameDesc,
]; ];
} }
@@ -63,6 +81,8 @@ impl std::fmt::Display for DocSort {
DocSort::Usage => crate::t!("Most used"), DocSort::Usage => crate::t!("Most used"),
DocSort::Hue => crate::t!("Color"), DocSort::Hue => crate::t!("Color"),
DocSort::Number => crate::t!("Color number"), DocSort::Number => crate::t!("Color number"),
DocSort::NameAsc => crate::t!("Name A–Z"),
DocSort::NameDesc => crate::t!("Name Z–A"),
}) })
} }
} }
@@ -163,6 +183,10 @@ pub fn sort(colors: &mut [DocColor], by: DocSort, recent: &[AcadColor]) {
.then_with(|| a.name.cmp(&b.name)) .then_with(|| a.name.cmp(&b.name))
.then_with(|| key(a.color).cmp(&key(b.color))) .then_with(|| key(a.color).cmp(&key(b.color)))
}), }),
DocSort::NameAsc => colors.sort_by_cached_key(|c| c.label().to_lowercase()),
DocSort::NameDesc => {
colors.sort_by_cached_key(|c| std::cmp::Reverse(c.label().to_lowercase()))
}
} }
} }
@@ -201,7 +225,7 @@ mod tests {
assert_eq!(colors[0].uses, 3); assert_eq!(colors[0].uses, 3);
assert_eq!(colors[0].newest, Handle::new(3)); assert_eq!(colors[0].newest, Handle::new(3));
assert_eq!(colors[3].name, None, "an empty name is no name"); assert_eq!(colors[3].name, None, "an empty name is no name");
assert_eq!(colors[2].tag(), "RAL"); assert_eq!(colors[2].tag(), "RAL 5015");
assert_eq!(colors[0].tag(), "1"); assert_eq!(colors[0].tag(), "1");
assert_eq!(colors[1].tag(), "RGB"); assert_eq!(colors[1].tag(), "RGB");
} }
+10 -10
View File
@@ -15,15 +15,15 @@ use iced::{Background, Border, Color, Element, Gradient, Length, Radians, Theme}
const ROW_HEIGHT: f32 = 26.0; const ROW_HEIGHT: f32 = 26.0;
const TRACK: f32 = 18.0; const TRACK: f32 = 18.0;
const STRIP_WIDTH: f32 = 22.0; const STRIP_WIDTH: f32 = 22.0;
const ROW_GAP: f32 = 8.0; const ROW_GAP: f32 = 6.0;
const FIELD_HEIGHT: f32 = 96.0; const FIELD_HEIGHT: f32 = 96.0;
const MODE_WIDTH: f32 = 48.0; const MODE_WIDTH: f32 = 48.0;
const MODE_HEIGHT: f32 = 24.0; const MODE_HEIGHT: f32 = 24.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EditorMode { pub enum EditorMode {
Rgb,
#[default] #[default]
Rgb,
Hsb, Hsb,
Cmyk, Cmyk,
} }
@@ -151,19 +151,19 @@ impl ColorEditor {
/// `mode_end` sits at the far right of the mode buttons (the pipette); /// `mode_end` sits at the far right of the mode buttons (the pipette);
/// `hex_end` at the far right of the hex row (the nearest index colour). /// `hex_end` at the far right of the hex row (the nearest index colour).
/// With `fill` the field and hex row sit at the bottom of the space, so
/// they stay put when CMYK adds a fourth slider.
pub fn view<'a>( pub fn view<'a>(
&'a self, &'a self,
mode_end: Option<Element<'a, Message>>, mode_end: Option<Element<'a, Message>>,
hex_end: Option<Element<'a, Message>>, hex_end: Option<Element<'a, Message>>,
fill: bool,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
column![ let mut view = column![self.mode_row(mode_end), self.slider_rows()].spacing(12);
self.mode_row(mode_end), if fill {
self.slider_rows(), view = view.push(Space::new().height(Length::Fill));
self.field(), }
self.hex_row(hex_end), view.push(self.field()).push(self.hex_row(hex_end)).into()
]
.spacing(12)
.into()
} }
/// RGB, HSB and CMYK, with `end` at the far right. /// RGB, HSB and CMYK, with `end` at the far right.
+91 -28
View File
@@ -6,7 +6,7 @@
//! colour editor: no tabs, no index colours. //! colour editor: no tabs, no index colours.
use super::book::ColorBook; use super::book::ColorBook;
use super::document::{self, DocColor, DocSort}; use super::document::{self, DocColor, DocSort, DocView};
use super::editor::{rgb_color, ColorEditor, EditorMsg}; use super::editor::{rgb_color, ColorEditor, EditorMsg};
use super::eyedropper::{Eyedropper, EyedropperMsg}; use super::eyedropper::{Eyedropper, EyedropperMsg};
use super::math; use super::math;
@@ -54,6 +54,7 @@ pub enum ColorPickerMsg {
/// A colour of the Document tab, by its place in the list. /// A colour of the Document tab, by its place in the list.
PickDocument(usize), PickDocument(usize),
DocSort(DocSort), DocSort(DocSort),
DocView(DocView),
/// Show another loaded colour book. /// Show another loaded colour book.
Book(usize), Book(usize),
BookPage(usize), BookPage(usize),
@@ -94,6 +95,7 @@ pub struct ColorPicker {
eyedropper: Option<Eyedropper>, eyedropper: Option<Eyedropper>,
doc: Vec<DocColor>, doc: Vec<DocColor>,
doc_sort: DocSort, doc_sort: DocSort,
doc_view: DocView,
/// This drawing's colours picked this session, newest first. /// This drawing's colours picked this session, newest first.
recent: Vec<AcadColor>, recent: Vec<AcadColor>,
books: Arc<Vec<ColorBook>>, books: Arc<Vec<ColorBook>>,
@@ -118,6 +120,7 @@ impl ColorPicker {
eyedropper: None, eyedropper: None,
doc: Vec::new(), doc: Vec::new(),
doc_sort: DocSort::default(), doc_sort: DocSort::default(),
doc_view: DocView::default(),
recent: Vec::new(), recent: Vec::new(),
books: Arc::default(), books: Arc::default(),
book: 0, book: 0,
@@ -284,6 +287,7 @@ impl ColorPicker {
self.pending_name = name; self.pending_name = name;
} }
} }
ColorPickerMsg::DocView(view) => self.doc_view = view,
ColorPickerMsg::DocSort(by) => { ColorPickerMsg::DocSort(by) => {
self.doc_sort = by; self.doc_sort = by;
document::sort(&mut self.doc, by, &self.recent); document::sort(&mut self.doc, by, &self.recent);
@@ -358,8 +362,8 @@ impl ColorPicker {
// height across its tabs, with the info line at the bottom. // height across its tabs, with the info line at the bottom.
let mut body = column![body].spacing(8); let mut body = column![body].spacing(8);
if !editor_only { if !editor_only {
// Document and Color books fill the height themselves. // The other tabs fill the height themselves.
if matches!(self.tab, ColorPickerTab::Index | ColorPickerTab::TrueColor) { if self.tab == ColorPickerTab::Index {
body = body.push(Space::new().height(Length::Fill)); body = body.push(Space::new().height(Length::Fill));
} }
let fill = let fill =
@@ -578,6 +582,11 @@ impl ColorPicker {
.on_select(|by| picker_msg(ColorPickerMsg::DocSort(by))) .on_select(|by| picker_msg(ColorPickerMsg::DocSort(by)))
.text_size(12) .text_size(12)
.padding([3, 6]), .padding([3, 6]),
row![
self.view_button(crate::ui::icons::GRID, DocView::Grid),
self.view_button(crate::ui::icons::LIST, DocView::List),
]
.spacing(2),
] ]
.spacing(6) .spacing(6)
.align_y(iced::Center); .align_y(iced::Center);
@@ -589,9 +598,59 @@ impl ColorPicker {
.spacing(10) .spacing(10)
.into(); .into();
} }
let grid = self.doc.chunks(DOC_COLUMNS).enumerate().fold( let list: Element<'_, Message> = if self.doc_view == DocView::List {
column![].spacing(4), self.doc
|grid, (r, chunk)| { .iter()
.enumerate()
.fold(column![].spacing(2), |list, (i, entry)| {
let fill = math::color_rgb(entry.color)
.map_or(Color::from_rgb8(128, 128, 128), rgb_color);
let selected = self.pending == entry.color && self.pending_name == entry.name;
list.push(
color_bar(
entry.label(),
fill,
selected,
ColorPickerMsg::PickDocument(i),
)
.height(26),
)
})
.into()
} else {
self.doc_grid()
};
column![
header,
scrollable(list).height(Length::Fill).spacing(4).direction(
scrollable::Direction::Vertical(
scrollable::Scrollbar::new().width(4).scroller_width(4)
)
),
]
.spacing(10)
.into()
}
/// Grid or list toggle for the Document tab.
fn view_button(&self, icon: &'static [u8], view: DocView) -> Element<'_, Message> {
let active = self.doc_view == view;
button(
svg(crate::ui::icons::themed_handle(icon))
.width(14)
.height(14),
)
.on_press(picker_msg(ColorPickerMsg::DocView(view)))
.padding(5)
.style(move |theme: &Theme, status| toggle_style(theme, active, status))
.into()
}
fn doc_grid(&self) -> Element<'_, Message> {
self.doc
.chunks(DOC_COLUMNS)
.enumerate()
.fold(column![].spacing(4), |grid, (r, chunk)| {
let line = chunk let line = chunk
.iter() .iter()
.enumerate() .enumerate()
@@ -601,18 +660,8 @@ impl ColorPicker {
let line = (chunk.len()..DOC_COLUMNS) let line = (chunk.len()..DOC_COLUMNS)
.fold(line, |line, _| line.push(Space::new().width(Length::Fill))); .fold(line, |line, _| line.push(Space::new().width(Length::Fill)));
grid.push(line) grid.push(line)
}, })
); .into()
column![
header,
scrollable(grid).height(Length::Fill).spacing(4).direction(
scrollable::Direction::Vertical(
scrollable::Scrollbar::new().width(4).scroller_width(4)
)
),
]
.spacing(10)
.into()
} }
fn doc_swatch<'a>(&self, i: usize, entry: &'a DocColor) -> Element<'a, Message> { fn doc_swatch<'a>(&self, i: usize, entry: &'a DocColor) -> Element<'a, Message> {
@@ -627,7 +676,8 @@ impl ColorPicker {
) )
.align_y(iced::Bottom) .align_y(iced::Bottom)
.padding([1, 4]) .padding([1, 4])
.height(Length::Fill), .height(Length::Fill)
.clip(true),
) )
.on_press(picker_msg(ColorPickerMsg::PickDocument(i))) .on_press(picker_msg(ColorPickerMsg::PickDocument(i)))
.padding(0) .padding(0)
@@ -863,7 +913,7 @@ impl ColorPicker {
) )
}; };
let nearest = (!self.editor_only()).then(|| self.nearest_button()); let nearest = (!self.editor_only()).then(|| self.nearest_button());
self.editor.view(pipette, nearest) self.editor.view(pipette, nearest, !self.editor_only())
} }
fn nearest_button(&self) -> Element<'_, Message> { fn nearest_button(&self) -> Element<'_, Message> {
@@ -985,24 +1035,37 @@ fn book_bar(
color: &super::book::BookColor, color: &super::book::BookColor,
selected: Option<usize>, selected: Option<usize>,
) -> Element<'_, Message> { ) -> Element<'_, Message> {
let fill = rgb_color(color.rgb); color_bar(
&color.name,
rgb_color(color.rgb),
selected == Some(i),
ColorPickerMsg::PickBook(i),
)
.height(Length::Fill)
.into()
}
/// A full-width colour bar with a name: the Color books pages and the
/// Document tab's list.
fn color_bar<'a>(
name: impl text::IntoFragment<'a>,
fill: Color,
selected: bool,
on_press: ColorPickerMsg,
) -> button::Button<'a, Message> {
button( button(
container( container(
text(&color.name) text(name)
.size(12) .size(12)
.font(iced::Font::MONOSPACE) .font(iced::Font::MONOSPACE)
.color(ink(fill)), .color(ink(fill)),
) )
.center(Length::Fill), .center(Length::Fill),
) )
.on_press(picker_msg(ColorPickerMsg::PickBook(i))) .on_press(picker_msg(on_press))
.padding(0) .padding(0)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .style(move |theme: &Theme, status| swatch_button_style(theme, status, fill, selected))
.style(move |theme: &Theme, status| {
swatch_button_style(theme, status, fill, selected == Some(i))
})
.into()
} }
/// A short gray note in the middle of the free space. /// A short gray note in the middle of the free space.
+1
View File
@@ -81,6 +81,7 @@ pub static PIN_ACTIVE: &[u8] = include_bytes!("../../assets/icons/ui/pin_active.
pub static SPLIT_V: &[u8] = include_bytes!("../../assets/icons/ui/split_v.svg"); pub static SPLIT_V: &[u8] = include_bytes!("../../assets/icons/ui/split_v.svg");
pub static SPLIT_H: &[u8] = include_bytes!("../../assets/icons/ui/split_h.svg"); pub static SPLIT_H: &[u8] = include_bytes!("../../assets/icons/ui/split_h.svg");
pub static GRID: &[u8] = include_bytes!("../../assets/icons/ui/grid.svg"); pub static GRID: &[u8] = include_bytes!("../../assets/icons/ui/grid.svg");
pub static LIST: &[u8] = include_bytes!("../../assets/icons/ui/list.svg");
pub static SNAP: &[u8] = include_bytes!("../../assets/icons/ui/snap.svg"); pub static SNAP: &[u8] = include_bytes!("../../assets/icons/ui/snap.svg");
pub static DOC_NEW: &[u8] = include_bytes!("../../assets/icons/ui/doc_new.svg"); pub static DOC_NEW: &[u8] = include_bytes!("../../assets/icons/ui/doc_new.svg");
pub static DOC: &[u8] = include_bytes!("../../assets/icons/ui/doc.svg"); pub static DOC: &[u8] = include_bytes!("../../assets/icons/ui/doc.svg");
+13
View File
@@ -82,6 +82,9 @@ pub struct Ribbon {
pub layer_filter: String, pub layer_filter: String,
/// Active object color — ACI / ByLayer / ByBlock. /// Active object color — ACI / ByLayer / ByBlock.
pub active_color: AcadColor, pub active_color: AcadColor,
/// Colour-book identity (`BOOK$NAME`) of a colour picked from a colour
/// book, with the colour it was picked as; see `active_color_name`.
pub active_color_book: Option<(AcadColor, String)>,
/// Active linetype override ("ByLayer", "Continuous", …). /// Active linetype override ("ByLayer", "Continuous", …).
pub active_linetype: String, pub active_linetype: String,
/// Active lineweight. /// Active lineweight.
@@ -171,6 +174,15 @@ fn position_ribbon_dropdown<'a>(
} }
impl Ribbon { impl Ribbon {
/// The colour-book identity of the active colour, while the active colour
/// is still the one picked from the book.
pub fn active_color_name(&self) -> Option<String> {
self.active_color_book
.as_ref()
.filter(|(color, _)| *color == self.active_color)
.map(|(_, name)| name.clone())
}
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
modules: all_ribbon_modules(), modules: all_ribbon_modules(),
@@ -195,6 +207,7 @@ impl Ribbon {
layer_infos: vec![], layer_infos: vec![],
layer_filter: String::new(), layer_filter: String::new(),
active_color: AcadColor::ByLayer, active_color: AcadColor::ByLayer,
active_color_book: None,
active_linetype: "ByLayer".to_string(), active_linetype: "ByLayer".to_string(),
active_lineweight: LineWeight::ByLayer, active_lineweight: LineWeight::ByLayer,
available_linetypes: vec![LinetypeItem { available_linetypes: vec![LinetypeItem {