mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 00:17:42 +00:00
Merge pull request #5249 from dotCooCoo/feat/recolor-wheel-display
Recolor Artwork: the Edit tab shows the colors on a smooth wheel, a segmented wheel or as color bars
This commit is contained in:
@@ -15,7 +15,11 @@
|
||||
//! saturation wheel with a marker per new colour (drag one; with linked handles they all turn and
|
||||
//! scale together), a brightness slider, the harmony rules, and the color sliders with their mode
|
||||
//! menu, whose Global Adjust shows Saturation, Brightness, Temperature and Luminosity (-100% to
|
||||
//! 100%, 0% changes nothing), which change every new color at once. The preset menu sets up a 1, 2 or 3
|
||||
//! 100%, 0% changes nothing), which change every new color at once. The buttons beside the wheel
|
||||
//! show the new colors on the smooth wheel, on a segmented wheel (solid patches of 24 hues by 5
|
||||
//! saturations; its markers move as freely) or as color bars (one per new color, in row order: click
|
||||
//! one to select it for the sliders, drag one to move its row, which changes no color); the dialog
|
||||
//! opens on the smooth wheel. The preset menu sets up a 1, 2 or 3
|
||||
//! colour job or a library. Opened on a colour group (Edit or Apply Color Group), OK also rewrites
|
||||
//! the group with the new colours (renamed by `groupName`); without art only the group changes.
|
||||
//! Opened on colours alone (the Color Guide's Edit or Apply Colors without art), OK saves the new
|
||||
@@ -28,8 +32,8 @@
|
||||
//! `sliderMode` (`hsb`, `rgb`, `cmyk`, `lab` or `tint`; unset: the row color's own model, HSB for
|
||||
//! a gray and Tint for a spot color; `global`: Global Adjust, on the Edit tab), `globalAdjust`
|
||||
//! (`{saturation, brightness, temperature, luminosity}` in percent, sent as `recolor.apply`'s
|
||||
//! `globalAdjust` in every slider mode). Changing `colors`, a preserve flag or `limitTo` reduces
|
||||
//! the rows again.
|
||||
//! `globalAdjust` in every slider mode), `display` (the Edit tab's `smooth`, `segmented` or `bars`;
|
||||
//! unset: `smooth`). Changing `colors`, a preserve flag or `limitTo` reduces the rows again.
|
||||
|
||||
use egui::{Rect, Sense, Ui, pos2, vec2};
|
||||
use serde_json::{Value, json};
|
||||
@@ -65,6 +69,9 @@ const CMD: &str = "recolor.apply";
|
||||
const CURRENT_W: f32 = 250.0;
|
||||
/// Diameter of the Edit tab's colour wheel.
|
||||
const WHEEL: f32 = 220.0;
|
||||
/// Height of the Edit tab's color bars area (the wheel with its Brightness slider), and of a bar.
|
||||
const BARS_H: f32 = WHEEL + 31.0;
|
||||
const BAR_H: f32 = 22.0;
|
||||
/// Height of the rows table, and of either tab (so switching tabs doesn't resize the dialog).
|
||||
const TABLE_H: f32 = 182.0;
|
||||
const TAB_H: f32 = 400.0;
|
||||
@@ -971,7 +978,29 @@ fn edit_tab(app: &VectorcraftApp, ui: &mut Ui, d: &mut Dialog, rows: &mut [Value
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
let colors: Vec<Color> = active.iter().filter_map(|&i| to_color(&rows[i])).collect();
|
||||
let w = widgets::harmony_wheel(ui, "recolor-wheel", WHEEL, &colors, base.and_then(|b| active.iter().position(|&i| i == b)));
|
||||
let marked = base.and_then(|b| active.iter().position(|&i| i == b));
|
||||
let display = d.str("display");
|
||||
let mut w = widgets::WheelResponse::default();
|
||||
ui.horizontal(|ui| {
|
||||
// The display buttons, beside the wheel so the tab keeps its height.
|
||||
ui.vertical(|ui| {
|
||||
for (id, icon, tip) in [
|
||||
("smooth", "circle", tl!("Show smooth color wheel")),
|
||||
("segmented", "dc-polar-grid", tl!("Show segmented color wheel")),
|
||||
("bars", "dc-list-view", tl!("Show color bars")),
|
||||
] {
|
||||
let on = display == id || (id == "smooth" && !matches!(display.as_str(), "segmented" | "bars"));
|
||||
if widgets::icon_button(ui, icon, tip, on, 24.0).clicked() {
|
||||
d.fields.insert("display".into(), json!(id));
|
||||
}
|
||||
}
|
||||
});
|
||||
ui.vertical(|ui| match display.as_str() {
|
||||
"segmented" => w = widgets::segmented_wheel(ui, "recolor-wheel", WHEEL, &colors, marked),
|
||||
"bars" => changed |= color_bars(ui, d, rows, &active, base),
|
||||
_ => w = widgets::harmony_wheel(ui, "recolor-wheel", WHEEL, &colors, marked),
|
||||
});
|
||||
});
|
||||
if let Some(k) = w.pressed {
|
||||
d.fields.insert("__sel".into(), json!([active[k]]));
|
||||
}
|
||||
@@ -1023,6 +1052,85 @@ fn edit_tab(app: &VectorcraftApp, ui: &mut Ui, d: &mut Dialog, rows: &mut [Value
|
||||
changed
|
||||
}
|
||||
|
||||
/// The Edit tab's color bars: a solid bar per row of `active` with its new color, in row order, the
|
||||
/// base row's framed, scrolling past the wheel's height. A click on a bar selects its row (the
|
||||
/// sliders under it edit that row's new color); a drag moves the bar's row to where the line under
|
||||
/// the pointer shows, keeping every new color. Returns true when the rows moved.
|
||||
fn color_bars(ui: &mut Ui, d: &mut Dialog, rows: &mut [Value], active: &[usize], base: Option<usize>) -> bool {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let mut rects: Vec<Rect> = Vec::with_capacity(active.len());
|
||||
let (mut dragging, mut released, mut dropped) = (None, None, None);
|
||||
// The area is given its full height first: in the row beside the display buttons a scroll area
|
||||
// alone is only as tall as the room the row has.
|
||||
let area = vec2(ui.available_width(), BARS_H);
|
||||
ui.allocate_ui_with_layout(area, egui::Layout::top_down(egui::Align::Min), |ui| {
|
||||
ui.set_min_size(area);
|
||||
egui::ScrollArea::vertical().id_salt("recolor-bars").max_height(BARS_H).show(ui, |ui| {
|
||||
ui.set_min_height(BARS_H);
|
||||
ui.set_width(ui.available_width());
|
||||
if active.is_empty() {
|
||||
ui.add_space(8.0);
|
||||
widgets::dim_label(ui, tl!("No colors to recolor (preserved colors are left out)."));
|
||||
}
|
||||
for (n, &i) in active.iter().enumerate() {
|
||||
let (r, resp) = ui.allocate_exact_size(vec2(ui.available_width() - 4.0, BAR_H), Sense::click_and_drag());
|
||||
chip(ui, r, to_color(&rows[i]).unwrap_or_default(), false);
|
||||
if Some(i) == base {
|
||||
ui.painter().rect_stroke(r, 2.0, egui::Stroke::new(2.0, t.accent), egui::StrokeKind::Inside);
|
||||
}
|
||||
let name = format!("{} {}", tl!("Color"), i + 1);
|
||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, ui.is_enabled(), &name));
|
||||
if resp.clicked() || resp.drag_started() {
|
||||
d.fields.insert("__sel".into(), json!([i]));
|
||||
}
|
||||
if resp.dragged() {
|
||||
dragging = Some(n);
|
||||
}
|
||||
if resp.drag_stopped() {
|
||||
released = Some(n);
|
||||
}
|
||||
rects.push(r);
|
||||
resp.on_hover_text(rows[i]["to"].as_str().unwrap_or_default());
|
||||
}
|
||||
// The place a dragged bar goes: before the first bar whose middle is below the pointer.
|
||||
let at = ui.input(|i| i.pointer.latest_pos()).map(|p| rects.iter().filter(|r| r.center().y < p.y).count());
|
||||
if let (Some(n), Some(to)) = (dragging, at)
|
||||
&& to != n
|
||||
&& to != n + 1
|
||||
{
|
||||
let y = rects.get(to).map_or_else(|| rects.last().map_or(0.0, |r| r.bottom() + 1.5), |r| r.top() - 1.5);
|
||||
let x = rects.first().map_or(ui.max_rect().x_range(), |r| r.x_range());
|
||||
ui.painter().hline(x, y, egui::Stroke::new(2.0, t.accent));
|
||||
}
|
||||
dropped = released.zip(at);
|
||||
});
|
||||
});
|
||||
let Some(moved) = dropped.and_then(|(n, to)| move_bar(rows, active, n, to)) else { return false };
|
||||
d.fields.insert("__sel".into(), json!([moved]));
|
||||
// The slider values held are those of a row index that may now hold another row.
|
||||
d.fields.remove(COMPS);
|
||||
true
|
||||
}
|
||||
|
||||
/// Move the row of the `from`th active row (of `active`, ascending row indexes) to before the
|
||||
/// `to`th, or after the last for `active.len()`; the rows between shift by one. Returns the row's
|
||||
/// new index, or `None` when it stays where it is.
|
||||
fn move_bar(rows: &mut [Value], active: &[usize], from: usize, to: usize) -> Option<usize> {
|
||||
let &src = active.get(from)?;
|
||||
if to == from || to == from + 1 || to > active.len() {
|
||||
return None;
|
||||
}
|
||||
if to > from {
|
||||
let dst = active[to - 1];
|
||||
rows[src..=dst].rotate_left(1);
|
||||
Some(dst)
|
||||
} else {
|
||||
let dst = active[to];
|
||||
rows[dst..=src].rotate_right(1);
|
||||
Some(dst)
|
||||
}
|
||||
}
|
||||
|
||||
/// Give the rows `active` the colours of `rule` from the base colour `b` of row `base` (the base row
|
||||
/// first, then the others in order, the rule's colours repeating), each in its row's model. A row
|
||||
/// whose new color changes loses its tint.
|
||||
|
||||
@@ -1340,6 +1340,10 @@ font-technology Variable Променливи
|
||||
Hard Light Твърда светлина
|
||||
Hard Mix Твърдо смесване
|
||||
Harmony Rules: Правила за хармония:
|
||||
Segmented color wheel Сегментирано цветово колело
|
||||
Show smooth color wheel Показване на плавно цветово колело
|
||||
Show segmented color wheel Показване на сегментирано цветово колело
|
||||
Show color bars Показване на цветни ленти
|
||||
Harmony Sets Набори от хармонии
|
||||
Height Височина
|
||||
Height: Височина:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2626,6 +2626,10 @@ digits Malayalam Malajalam
|
||||
Current Colors Trenutne boje
|
||||
Current Colors (0) Trenutne boje (0)
|
||||
Harmony Rules: Pravila harmonije:
|
||||
Segmented color wheel Segmentirani kotač boja
|
||||
Show smooth color wheel Prikaži glatki kotač boja
|
||||
Show segmented color wheel Prikaži segmentirani kotač boja
|
||||
Show color bars Prikaži trake boja
|
||||
Harmony Rules Pravila harmonije
|
||||
Keep this row's colors (exclude) Zadrži boje ovog reda (isključi)
|
||||
Limit to Library: Ograniči na biblioteku:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1805,6 +1805,10 @@ digits Malayalam Malaiàlam
|
||||
Hard Mix Mescla dura
|
||||
Harmony Rules Regles d’harmonia
|
||||
Harmony Rules: Regles d’harmonia:
|
||||
Segmented color wheel Roda de color segmentada
|
||||
Show smooth color wheel Mostra la roda de color contínua
|
||||
Show segmented color wheel Mostra la roda de color segmentada
|
||||
Show color bars Mostra les barres de color
|
||||
Harmony Sets Conjunts d’harmonia
|
||||
HDTV 1080p HDTV 1080p
|
||||
HDTV 720p HDTV 720p
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2657,6 +2657,10 @@ digits Malayalam Malajálamské
|
||||
Current Colors Aktuální barvy
|
||||
Current Colors (0) Aktuální barvy (0)
|
||||
Harmony Rules: Pravidla harmonie:
|
||||
Segmented color wheel Segmentované barevné kolo
|
||||
Show smooth color wheel Zobrazit plynulé barevné kolo
|
||||
Show segmented color wheel Zobrazit segmentované barevné kolo
|
||||
Show color bars Zobrazit barevné pruhy
|
||||
Harmony Rules Pravidla harmonie
|
||||
Keep this row's colors (exclude) Ponechat barvy tohoto řádku (vyloučit)
|
||||
Limit to Library: Omezit na knihovnu:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2444,6 +2444,10 @@ digits Malayalam Malayalam
|
||||
Current Colors Aktuelle farver
|
||||
Current Colors (0) Aktuelle farver (0)
|
||||
Harmony Rules: Harmoniregler:
|
||||
Segmented color wheel Segmenteret farvehjul
|
||||
Show smooth color wheel Vis glat farvehjul
|
||||
Show segmented color wheel Vis segmenteret farvehjul
|
||||
Show color bars Vis farvebjælker
|
||||
Harmony Rules Harmoniregler
|
||||
Keep this row's colors (exclude) Behold farverne i denne række (ekskludér)
|
||||
Limit to Library: Begræns til bibliotek:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1371,6 +1371,10 @@ file-filter Templates Vorlagen
|
||||
Hard Light Hartes Licht
|
||||
Hard Mix Hart mischen
|
||||
Harmony Rules: Harmonieregeln:
|
||||
Segmented color wheel Segmentierter Farbkreis
|
||||
Show smooth color wheel Stufenlosen Farbkreis anzeigen
|
||||
Show segmented color wheel Segmentierten Farbkreis anzeigen
|
||||
Show color bars Farbbalken anzeigen
|
||||
Harmony Sets Harmoniesätze
|
||||
Height Höhe
|
||||
Height: Höhe:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1310,6 +1310,10 @@ font-technology Variable Μεταβλητές
|
||||
Hard Light Έντονο φως
|
||||
Hard Mix Έντονη ανάμειξη
|
||||
Harmony Rules: Κανόνες χρωματικής αρμονίας:
|
||||
Segmented color wheel Τμηματοποιημένος χρωματικός τροχός
|
||||
Show smooth color wheel Εμφάνιση ομαλού χρωματικού τροχού
|
||||
Show segmented color wheel Εμφάνιση τμηματοποιημένου χρωματικού τροχού
|
||||
Show color bars Εμφάνιση χρωματικών ράβδων
|
||||
Harmony Sets Σύνολα χρωματικής αρμονίας
|
||||
Height Ύψος
|
||||
Height: Ύψος:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 163 and column 5.
|
@@ -1389,6 +1389,10 @@ font-technology Variable Variables
|
||||
Hard Light Luz fuerte
|
||||
Hard Mix Mezcla definida
|
||||
Harmony Rules: Reglas de armonía:
|
||||
Segmented color wheel Rueda de color segmentada
|
||||
Show smooth color wheel Mostrar rueda de color continua
|
||||
Show segmented color wheel Mostrar rueda de color segmentada
|
||||
Show color bars Mostrar barras de color
|
||||
Harmony Sets Conjuntos de armonía
|
||||
Height Altura
|
||||
Height: Altura:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2601,6 +2601,10 @@ digits Malayalam Malajalam
|
||||
Current Colors Nykyiset värit
|
||||
Current Colors (0) Nykyiset värit (0)
|
||||
Harmony Rules: Harmoniasäännöt:
|
||||
Segmented color wheel Segmentoitu väriympyrä
|
||||
Show smooth color wheel Näytä liukuva väriympyrä
|
||||
Show segmented color wheel Näytä segmentoitu väriympyrä
|
||||
Show color bars Näytä väripalkit
|
||||
Harmony Rules Harmoniasäännöt
|
||||
Keep this row's colors (exclude) Säilytä tämän rivin värit (jätä pois)
|
||||
Limit to Library: Rajoita kirjastoon:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1382,6 +1382,10 @@ font-technology Variable Variables
|
||||
Handles Poignées
|
||||
Hard Light Lumière crue
|
||||
Hard Mix Mélange maximal
|
||||
Segmented color wheel Roue chromatique segmentée
|
||||
Show smooth color wheel Afficher la roue chromatique continue
|
||||
Show segmented color wheel Afficher la roue chromatique segmentée
|
||||
Show color bars Afficher les barres de couleur
|
||||
Harmony Rules: Règles d’harmonie :
|
||||
Harmony Sets Ensembles d’harmonies
|
||||
Height Hauteur
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1771,6 +1771,10 @@ file-filter Templates Sablonok
|
||||
Hard Mix Kemény keverés
|
||||
Harmony Rules Harmóniaszabályok
|
||||
Harmony Rules: Harmóniaszabályok:
|
||||
Segmented color wheel Szegmentált színkör
|
||||
Show smooth color wheel Folytonos színkör megjelenítése
|
||||
Show segmented color wheel Szegmentált színkör megjelenítése
|
||||
Show color bars Színsávok megjelenítése
|
||||
Harmony Sets Harmóniakészletek
|
||||
HDTV 1080p HDTV 1080p
|
||||
HDTV 720p HDTV 720p
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2444,6 +2444,10 @@ digits Malayalam Malayalam
|
||||
Current Colors Warna saat ini
|
||||
Current Colors (0) Warna saat ini (0)
|
||||
Harmony Rules: Aturan harmoni:
|
||||
Segmented color wheel Roda warna bersegmen
|
||||
Show smooth color wheel Tampilkan roda warna halus
|
||||
Show segmented color wheel Tampilkan roda warna bersegmen
|
||||
Show color bars Tampilkan bilah warna
|
||||
Harmony Rules Aturan harmoni
|
||||
Keep this row's colors (exclude) Pertahankan warna baris ini (kecualikan)
|
||||
Limit to Library: Batasi ke pustaka:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2210 and column 5.
|
@@ -1401,6 +1401,10 @@ file-filter Templates Modelli
|
||||
Hard Light Luce intensa
|
||||
Hard Mix Miscela intensa
|
||||
Harmony Rules: Regole di armonia:
|
||||
Segmented color wheel Ruota dei colori segmentata
|
||||
Show smooth color wheel Mostra ruota dei colori continua
|
||||
Show segmented color wheel Mostra ruota dei colori segmentata
|
||||
Show color bars Mostra barre di colore
|
||||
Harmony Sets Gruppi di armonia
|
||||
Height Altezza
|
||||
Height: Altezza:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1726,6 +1726,10 @@ digits Malayalam マラヤーラム数字
|
||||
Hard Light ハードライト
|
||||
Hard Mix ハードミックス
|
||||
Harmony Rules: ハーモニールール:
|
||||
Segmented color wheel 分割カラーホイール
|
||||
Show smooth color wheel 滑らかなカラーホイールを表示
|
||||
Show segmented color wheel 分割カラーホイールを表示
|
||||
Show color bars カラーバーを表示
|
||||
Height 高さ
|
||||
Height: 高さ:
|
||||
Help › Search Commands… ヘルプ › コマンドを検索…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2335,6 +2335,10 @@ digits Malayalam მალაიალამური
|
||||
Colors: ფერები:
|
||||
Current Colors ({total}) მიმდინარე ფერები ({total})
|
||||
Harmony Rules: ჰარმონიის წესები:
|
||||
Segmented color wheel სეგმენტირებული ფერთა წრე
|
||||
Show smooth color wheel გლუვი ფერთა წრის ჩვენება
|
||||
Show segmented color wheel სეგმენტირებული ფერთა წრის ჩვენება
|
||||
Show color bars ფერთა ზოლების ჩვენება
|
||||
Keep this row's colors (exclude) ამ სტრიქონის ფერების შენარჩუნება (გამორიცხვა)
|
||||
Limit to Library: ბიბლიოთეკით შეზღუდვა:
|
||||
Link harmony colors ჰარმონიის ფერების დაკავშირება
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -1716,6 +1716,10 @@ digits Malayalam 말라얄람어
|
||||
Hard Light 하드 라이트
|
||||
Hard Mix 하드 믹스
|
||||
Harmony Rules: 하모니 규칙:
|
||||
Segmented color wheel 분할 색상환
|
||||
Show smooth color wheel 부드러운 색상환 표시
|
||||
Show segmented color wheel 분할 색상환 표시
|
||||
Show color bars 색상 막대 표시
|
||||
Height 높이
|
||||
Height: 높이:
|
||||
Help › Search Commands… 도움말 › 명령 검색…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2458,6 +2458,10 @@ digits Malayalam Malayalam
|
||||
Current Colors Huidige kleuren
|
||||
Current Colors (0) Huidige kleuren (0)
|
||||
Harmony Rules: Harmonieregels:
|
||||
Segmented color wheel Gesegmenteerd kleurenwiel
|
||||
Show smooth color wheel Vloeiend kleurenwiel tonen
|
||||
Show segmented color wheel Gesegmenteerd kleurenwiel tonen
|
||||
Show color bars Kleurbalken tonen
|
||||
Harmony Rules Harmonieregels
|
||||
Keep this row's colors (exclude) De kleuren van deze rij behouden (uitsluiten)
|
||||
Limit to Library: Beperken tot bibliotheek:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2567,6 +2567,10 @@ digits Malayalam Malajalam
|
||||
Colors: Kolory:
|
||||
Current Colors ({total}) Bieżące kolory ({total})
|
||||
Harmony Rules: Reguły harmonii:
|
||||
Segmented color wheel Segmentowe koło kolorów
|
||||
Show smooth color wheel Pokaż płynne koło kolorów
|
||||
Show segmented color wheel Pokaż segmentowe koło kolorów
|
||||
Show color bars Pokaż paski kolorów
|
||||
Keep this row's colors (exclude) Zachowaj kolory tego wiersza (wyklucz)
|
||||
Limit to Library: Ogranicz do biblioteki:
|
||||
Link harmony colors Połącz kolory harmonii
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1215,6 +1215,10 @@
|
||||
Hard Light Luz Forte
|
||||
Hard Mix Mistura definida
|
||||
Harmony Rules: Regras de Harmonia:
|
||||
Segmented color wheel Roda de cores segmentada
|
||||
Show smooth color wheel Mostrar roda de cores contínua
|
||||
Show segmented color wheel Mostrar roda de cores segmentada
|
||||
Show color bars Mostrar barras de cores
|
||||
Harmony Sets Conjuntos de Harmonia
|
||||
Height Altura
|
||||
Height: Altura:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1398,6 +1398,10 @@ file-filter Templates Шаблоны
|
||||
Hard Light Жёсткий свет
|
||||
Hard Mix Жёсткое наложение
|
||||
Harmony Rules: Правила гармонии:
|
||||
Segmented color wheel Сегментированный цветовой круг
|
||||
Show smooth color wheel Показать плавный цветовой круг
|
||||
Show segmented color wheel Показать сегментированный цветовой круг
|
||||
Show color bars Показать цветовые полосы
|
||||
Harmony Sets Наборы гармонии
|
||||
Height Высота
|
||||
Height: Высота:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1348,6 +1348,10 @@ font-technology Variable Variabla
|
||||
Hard Light Hårt ljus
|
||||
Hard Mix Hård blandning
|
||||
Harmony Rules: Harmoniregler:
|
||||
Segmented color wheel Segmenterat färghjul
|
||||
Show smooth color wheel Visa steglöst färghjul
|
||||
Show segmented color wheel Visa segmenterat färghjul
|
||||
Show color bars Visa färgstaplar
|
||||
Harmony Sets Harmoniuppsättningar
|
||||
Height Höjd
|
||||
Height: Höjd:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -1311,6 +1311,10 @@ font-technology Variable Değişken
|
||||
Handles Kollar
|
||||
Hard Light Sert Işık
|
||||
Harmony Rules: Uyum Kuralları:
|
||||
Segmented color wheel Bölümlü renk tekerleği
|
||||
Show smooth color wheel Kesintisiz renk tekerleğini göster
|
||||
Show segmented color wheel Bölümlü renk tekerleğini göster
|
||||
Show color bars Renk çubuklarını göster
|
||||
Harmony Sets Armoni Setleri
|
||||
Height Yükseklik
|
||||
Height: Yükseklik:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -1395,6 +1395,10 @@ file-filter Templates Шаблони
|
||||
Hard Light Жорстке світло
|
||||
Hard Mix Жорстке накладання
|
||||
Harmony Rules: Правила гармонії:
|
||||
Segmented color wheel Сегментоване колірне коло
|
||||
Show smooth color wheel Показати плавне колірне коло
|
||||
Show segmented color wheel Показати сегментоване колірне коло
|
||||
Show color bars Показати колірні смуги
|
||||
Harmony Sets Набори гармонії
|
||||
Height Висота
|
||||
Height: Висота:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 177 and column 5.
|
@@ -1610,6 +1610,10 @@ file-filter Templates Mẫu
|
||||
Hard Light Ánh sáng mạnh
|
||||
Hard Mix Trộn cứng
|
||||
Harmony Rules: Quy tắc hài hòa màu:
|
||||
Segmented color wheel Bánh xe màu phân đoạn
|
||||
Show smooth color wheel Hiện bánh xe màu liền mạch
|
||||
Show segmented color wheel Hiện bánh xe màu phân đoạn
|
||||
Show color bars Hiện các thanh màu
|
||||
Harmony Sets Bộ hài hòa màu
|
||||
Height Chiều cao
|
||||
Height: Chiều cao:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 199 and column 5.
|
@@ -1338,6 +1338,10 @@ file-filter Templates 模板
|
||||
Hard Light 强光
|
||||
Hard Mix 实色混合
|
||||
Harmony Rules: 协调规则:
|
||||
Segmented color wheel 分段色轮
|
||||
Show smooth color wheel 显示平滑色轮
|
||||
Show segmented color wheel 显示分段色轮
|
||||
Show color bars 显示颜色条
|
||||
Harmony Sets 色彩调和组
|
||||
Height 高度
|
||||
Height: 高度:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1366,6 +1366,10 @@ file-filter Templates 範本
|
||||
Hard Light 實光
|
||||
Hard Mix 實色混合
|
||||
Harmony Rules: 色彩調和規則:
|
||||
Segmented color wheel 分段色輪
|
||||
Show smooth color wheel 顯示平滑色輪
|
||||
Show segmented color wheel 顯示分段色輪
|
||||
Show color bars 顯示顏色條
|
||||
Harmony Sets 色彩調和組
|
||||
Height 高度
|
||||
Height: 高度:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -576,7 +576,10 @@ fn switching_tabs_or_slider_modes_keeps_the_dialog_height() {
|
||||
("sliderMode", json!("hsb")),
|
||||
("sliderMode", json!("lab")),
|
||||
("tab", json!("edit")),
|
||||
("display", json!("segmented")),
|
||||
("display", json!("bars")),
|
||||
("sliderMode", json!("global")),
|
||||
("display", json!("smooth")),
|
||||
("sliderMode", json!("rgb")),
|
||||
("tab", json!("assign")),
|
||||
] {
|
||||
@@ -939,3 +942,233 @@ fn the_last_edit_color_can_be_selected_edited_and_undone() {
|
||||
assert_eq!(h.state().session.doc().unwrap().doc, before);
|
||||
}
|
||||
}
|
||||
|
||||
/// The height of the open Recolor Artwork dialog.
|
||||
fn dialog_height(h: &egui_kittest::Harness<'_, VectorcraftApp>) -> f32 {
|
||||
h.ctx.memory(|m| m.area_rect(egui::Id::unique(("dialog", "recolor")))).unwrap().height()
|
||||
}
|
||||
|
||||
/// Clicks the Edit tab's display button named `name` and lets the dialog settle.
|
||||
fn show_display(h: &mut egui_kittest::Harness<'_, VectorcraftApp>, name: &str) {
|
||||
h.get_by_role_and_label(egui::accesskit::Role::Button, name).click();
|
||||
h.run_steps(6);
|
||||
}
|
||||
|
||||
/// The Edit tab opens on the smooth color wheel and its buttons switch to the segmented wheel and to
|
||||
/// the color bars and back: the dialog keeps its size, and the rows and the selected row stay. The
|
||||
/// choice stays while the dialog is open; the next opening starts on the smooth wheel again.
|
||||
#[test]
|
||||
fn the_edit_tab_shows_a_smooth_or_segmented_wheel_or_color_bars() {
|
||||
let mut h = edit_harness(&["#ff0000", "#0000ff", "#00ff00"]);
|
||||
set(h.state_mut(), "__sel", json!([1]));
|
||||
h.run_steps(4);
|
||||
let (rows, height) = (field(h.state(), "rows"), dialog_height(&h));
|
||||
let slider = |h: &egui_kittest::Harness<'_, VectorcraftApp>, name: &str| h.query_by_role_and_label(egui::accesskit::Role::Slider, name).is_some();
|
||||
let bar = |h: &egui_kittest::Harness<'_, VectorcraftApp>, name: &str| h.query_by_role_and_label(egui::accesskit::Role::Button, name).is_some();
|
||||
assert!(slider(&h, "Color wheel") && !slider(&h, "Segmented color wheel"), "the smooth wheel first");
|
||||
assert!(!bar(&h, "Color 1"));
|
||||
show_display(&mut h, "Show segmented color wheel");
|
||||
assert_eq!(field(h.state(), "display"), json!("segmented"));
|
||||
assert!(slider(&h, "Segmented color wheel") && !slider(&h, "Color wheel"));
|
||||
assert_eq!(widget_of_size(&h, vec2(220.0, 220.0)).size(), vec2(220.0, 220.0), "the same size of wheel");
|
||||
assert!(slider(&h, "Brightness"));
|
||||
show_display(&mut h, "Show color bars");
|
||||
assert_eq!(field(h.state(), "display"), json!("bars"));
|
||||
assert!(!slider(&h, "Color wheel") && !slider(&h, "Segmented color wheel"));
|
||||
assert!(["Color 1", "Color 2", "Color 3"].iter().all(|n| bar(&h, n)), "a bar per color");
|
||||
assert_eq!(dialog_height(&h), height, "the bars keep the dialog's height");
|
||||
show_display(&mut h, "Show smooth color wheel");
|
||||
assert!(slider(&h, "Color wheel"));
|
||||
assert_eq!(dialog_height(&h), height);
|
||||
assert_eq!(field(h.state(), "rows"), rows, "switching changes no color");
|
||||
assert_eq!(field(h.state(), "__sel"), json!([1]), "and keeps the selected row");
|
||||
show_display(&mut h, "Show color bars");
|
||||
set(h.state_mut(), "tab", json!("assign"));
|
||||
h.run_steps(3);
|
||||
set(h.state_mut(), "tab", json!("edit"));
|
||||
h.run_steps(3);
|
||||
assert!(bar(&h, "Color 1"), "the choice stays while the dialog is open");
|
||||
let app = h.state_mut();
|
||||
dialogs::cancel(app);
|
||||
app.run("select.all", json!({})).unwrap();
|
||||
app.run("ui.recolorDialog", json!({})).unwrap();
|
||||
assert_eq!(field(app, "display"), Value::Null, "a new opening starts on the smooth wheel");
|
||||
}
|
||||
|
||||
/// The segmented wheel's patches: 24 hues, centered on red, by 5 saturations, at the brightness asked.
|
||||
#[test]
|
||||
fn the_segmented_wheel_has_24_hues_by_5_saturations() {
|
||||
let seg = |h, s, v| crate::widgets::segment_color(h, s, v).to_hsb();
|
||||
let near = |a: [f32; 3], b: [f32; 3]| a.iter().zip(b).all(|(x, y)| (x - y).abs() < 1e-3);
|
||||
assert!(near(seg(7.0, 0.95, 1.0), [0.0, 0.9, 1.0]), "{:?}", seg(7.0, 0.95, 1.0));
|
||||
assert!(near(seg(8.0, 0.0, 0.5), [15.0, 0.1, 0.5]), "{:?}", seg(8.0, 0.0, 0.5));
|
||||
assert!(near(seg(355.0, 1.0, 1.0), [0.0, 0.9, 1.0]), "{:?}", seg(355.0, 1.0, 1.0));
|
||||
assert!(near(seg(120.0, 0.5, 1.0), [120.0, 0.5, 1.0]), "{:?}", seg(120.0, 0.5, 1.0));
|
||||
let mut patches = std::collections::BTreeSet::new();
|
||||
for h in 0..360 {
|
||||
for s in 0..=100 {
|
||||
patches.insert(ColorKey::of(&crate::widgets::segment_color(h as f32, s as f32 / 100.0, 1.0)).to_string());
|
||||
}
|
||||
}
|
||||
assert_eq!(patches.len(), 24 * 5);
|
||||
}
|
||||
|
||||
/// On the segmented wheel a marker moves as freely as on the smooth one, past patch edges and
|
||||
/// without snapping to a patch; linked colors turn with it from where the drag started, an excluded
|
||||
/// row keeps its color, and the smooth wheel shows the result. Its Brightness slider works too.
|
||||
#[test]
|
||||
fn a_segmented_wheel_marker_moves_freely() {
|
||||
let mut h = edit_harness(&["rgb 200 100 100", "rgb 100 200 200", "rgb 20 160 20"]);
|
||||
let mut rows = field(h.state(), "rows");
|
||||
let green = (0..3).find(|i| rows[*i]["from"] == json!(["rgb 20 160 20"])).unwrap();
|
||||
let red = (0..3).find(|i| rows[*i]["from"] == json!(["rgb 200 100 100"])).unwrap();
|
||||
rows[green]["exclude"] = json!(true);
|
||||
set(h.state_mut(), "rows", rows);
|
||||
set(h.state_mut(), "__sel", json!([red]));
|
||||
h.run_steps(4);
|
||||
show_display(&mut h, "Show segmented color wheel");
|
||||
let start = new_hsb(h.state());
|
||||
let wheel = widget_of_size(&h, vec2(220.0, 220.0));
|
||||
let (c, r) = (wheel.center(), 100.0);
|
||||
let at = |hue: f32, s: f32| c + vec2(hue.to_radians().cos(), -hue.to_radians().sin()) * r * s;
|
||||
let [hue, s, _] = start[red];
|
||||
h.hover_at(at(hue, s));
|
||||
h.run_steps(1);
|
||||
h.drag_at(at(hue, s));
|
||||
h.run_steps(1);
|
||||
// 37° on in small steps, across two patch edges, ending inside a patch away from its center.
|
||||
for k in 1..=10 {
|
||||
h.hover_at(at(hue + 3.7 * k as f32, s));
|
||||
h.run_steps(1);
|
||||
}
|
||||
h.drop_at(at(hue + 37.0, s));
|
||||
h.run_steps(2);
|
||||
let moved = new_hsb(h.state());
|
||||
let gap = |a: f32, b: f32| (a - b).rem_euclid(360.0).min((b - a).rem_euclid(360.0));
|
||||
let want = (hue + 37.0).rem_euclid(360.0);
|
||||
assert!(gap(moved[red][0], want) < 1.0, "the marker follows the pointer: {} -> {}", hue, moved[red][0]);
|
||||
assert!(gap(moved[red][0], (want / 15.0).round() * 15.0) > 2.0, "not snapped to a patch center: {}", moved[red][0]);
|
||||
let other = 3 - red - green;
|
||||
assert!((gap(moved[other][0], start[other][0]) - 37.0).abs() < 1.5, "linked: {:?} -> {:?}", start[other], moved[other]);
|
||||
assert_eq!(field(h.state(), "rows")[green]["to"], json!("rgb 20 160 20"), "the excluded row stays");
|
||||
let rows = field(h.state(), "rows");
|
||||
show_display(&mut h, "Show smooth color wheel");
|
||||
assert_eq!(field(h.state(), "rows"), rows, "the smooth wheel shows the same colors");
|
||||
show_display(&mut h, "Show segmented color wheel");
|
||||
click_slider(&mut h, "Brightness", 0.25);
|
||||
assert!((new_hsb(h.state())[red][2] - 0.25).abs() < 0.02, "the Brightness slider: {:?}", new_hsb(h.state()));
|
||||
}
|
||||
|
||||
/// The rows as `(current colors, new color)` pairs, sorted.
|
||||
fn row_pairs(app: &VectorcraftApp) -> Vec<String> {
|
||||
let mut v: Vec<String> = field(app, "rows").as_array().unwrap().iter().map(|r| format!("{} {}", r["from"], r["to"])).collect();
|
||||
v.sort();
|
||||
v
|
||||
}
|
||||
|
||||
/// The color bars: one per recolored row (none for an excluded row), the last one selected with a
|
||||
/// click and edited with the H slider, then dragged to the top, which moves its row and changes no
|
||||
/// color. Preview shows the edit, Cancel restores the art and OK keeps it as one undo step.
|
||||
#[test]
|
||||
fn color_bars_select_edit_and_reorder_the_rows() {
|
||||
for confirm in [false, true] {
|
||||
let mut h = edit_harness(&["#ff0000", "#0000ff", "#00ff00", "#ffff00"]);
|
||||
let before = h.state().session.doc().unwrap().doc.clone();
|
||||
let undo = h.state().session.doc().unwrap().history.undo.len();
|
||||
let mut rows = field(h.state(), "rows");
|
||||
rows[1]["exclude"] = json!(true);
|
||||
set(h.state_mut(), "rows", rows);
|
||||
set(h.state_mut(), "linked", json!(false));
|
||||
set(h.state_mut(), "sliderMode", json!("hsb"));
|
||||
show_display(&mut h, "Show color bars");
|
||||
let bar = |h: &egui_kittest::Harness<'_, VectorcraftApp>, name: &str| {
|
||||
h.query_by_role_and_label(egui::accesskit::Role::Button, name).map(|n| n.rect())
|
||||
};
|
||||
assert!(bar(&h, "Color 2").is_none(), "no bar for the excluded row");
|
||||
let first = bar(&h, "Color 1").expect("a bar per recolored row");
|
||||
let last = bar(&h, "Color 4").expect("a bar per recolored row");
|
||||
assert!(bar(&h, "Color 3").is_some_and(|r| r.top() > first.bottom() && r.bottom() < last.top()), "in row order");
|
||||
h.input_mut().events.extend(press(last.center(), 1));
|
||||
h.run_steps(3);
|
||||
assert_eq!(field(h.state(), "__sel"), json!([3]), "a click selects the bar's row");
|
||||
let unedited = field(h.state(), "rows");
|
||||
click_slider(&mut h, "H", 0.5);
|
||||
let edited = field(h.state(), "rows");
|
||||
assert_ne!(edited[3]["to"], unedited[3]["to"], "the H slider edits the selected bar's color");
|
||||
assert_eq!(&edited.as_array().unwrap()[..3], &unedited.as_array().unwrap()[..3], "and only that one");
|
||||
let previewed = h.state().session.doc().unwrap().doc.clone();
|
||||
assert_ne!(previewed, before, "the art previews the edit");
|
||||
let pairs = row_pairs(h.state());
|
||||
let last = bar(&h, "Color 4").unwrap();
|
||||
h.hover_at(last.center());
|
||||
h.run_steps(1);
|
||||
h.drag_at(last.center());
|
||||
h.run_steps(1);
|
||||
let top = egui::pos2(last.center().x, first.top() + 2.0);
|
||||
for k in 1..=8 {
|
||||
h.hover_at(last.center().lerp(top, k as f32 / 8.0));
|
||||
h.run_steps(1);
|
||||
}
|
||||
h.drop_at(top);
|
||||
h.run_steps(3);
|
||||
let rows = field(h.state(), "rows");
|
||||
assert_eq!(rows[0], edited[3], "the dragged row moved to the top: {rows}");
|
||||
assert_eq!(rows.as_array().unwrap()[1..], [edited[0].clone(), edited[1].clone(), edited[2].clone()], "the others shift down");
|
||||
assert_eq!(row_pairs(h.state()), pairs, "moving a bar changes no color");
|
||||
assert_eq!(field(h.state(), "__sel"), json!([0]), "the moved row stays selected");
|
||||
assert_eq!(h.state().session.doc().unwrap().doc, previewed, "and the art keeps the same colors");
|
||||
h.get_by_label(if confirm { "OK" } else { "Cancel" }).click();
|
||||
h.run_steps(2);
|
||||
assert!(h.state().ui.dialog.is_none());
|
||||
let app = h.state();
|
||||
if confirm {
|
||||
assert_eq!(app.session.doc().unwrap().doc, previewed, "OK keeps the edit");
|
||||
assert_eq!(app.session.doc().unwrap().history.undo.len(), undo + 1, "as one undo step");
|
||||
} else {
|
||||
assert_eq!(app.session.doc().unwrap().doc, before, "Cancel restores the art");
|
||||
assert_eq!(app.session.doc().unwrap().history.undo.len(), undo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Many color bars scroll within the wheel's height, the dialog keeps its height, and the last bar
|
||||
/// can be reached and selected; with every row excluded the bars area says there is nothing to
|
||||
/// recolor.
|
||||
#[test]
|
||||
fn color_bars_scroll_and_show_when_nothing_is_recolored() {
|
||||
let colors: Vec<String> = (1..=40).map(|i| format!("#{:06x}", i * 9973)).collect();
|
||||
let refs: Vec<&str> = colors.iter().map(String::as_str).collect();
|
||||
let mut h = edit_harness(&refs);
|
||||
let height = dialog_height(&h);
|
||||
show_display(&mut h, "Show color bars");
|
||||
assert_eq!(dialog_height(&h), height, "40 bars keep the dialog's height");
|
||||
let first = h.get_by_role_and_label(egui::accesskit::Role::Button, "Color 1").rect();
|
||||
let wheel_bottom = first.top() + 251.0;
|
||||
let last = |h: &egui_kittest::Harness<'_, VectorcraftApp>| h.get_by_role_and_label(egui::accesskit::Role::Button, "Color 40").rect();
|
||||
assert!(last(&h).top() > wheel_bottom, "the last bar starts out of view: {:?}", last(&h));
|
||||
h.input_mut().events.extend([
|
||||
Event::PointerMoved(first.center()),
|
||||
Event::MouseWheel {
|
||||
unit: egui::MouseWheelUnit::Point,
|
||||
delta: vec2(0.0, -10000.0),
|
||||
phase: egui::TouchPhase::Move,
|
||||
modifiers: Modifiers::NONE,
|
||||
source: egui::MouseWheelSource::Unknown,
|
||||
},
|
||||
]);
|
||||
h.run_steps(12);
|
||||
let shown = last(&h);
|
||||
assert!(shown.bottom() <= wheel_bottom + 1.0, "the last bar scrolls into view: {shown:?}");
|
||||
assert_eq!(dialog_height(&h), height);
|
||||
h.input_mut().events.extend(press(shown.center(), 1));
|
||||
h.run_steps(3);
|
||||
assert_eq!(field(h.state(), "__sel"), json!([39]));
|
||||
let mut rows = field(h.state(), "rows");
|
||||
for r in rows.as_array_mut().unwrap() {
|
||||
r["exclude"] = json!(true);
|
||||
}
|
||||
set(h.state_mut(), "rows", rows);
|
||||
h.run_steps(4);
|
||||
assert!(h.query_by_role_and_label(egui::accesskit::Role::Button, "Color 1").is_none());
|
||||
let _ = h.get_by_label("No colors to recolor (preserved colors are left out).");
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ pub use chips::{ProxyClicks, chip_button, chip_button_named, fill_stroke_proxy,
|
||||
pub(crate) use color::pointer_phase;
|
||||
pub use color::{
|
||||
Live, SliderValue, WheelResponse, channel_slider, channel_slider_named, channel_slider_valued, color_field, color_mesh, color_slider,
|
||||
color_slider_named, color_slider_stepped, color_slider_valued, harmony_wheel, hex_field, hex_field_named, region_texture, stepped_area,
|
||||
color_slider_named, color_slider_stepped, color_slider_valued, harmony_wheel, hex_field, hex_field_named, region_texture, segment_color,
|
||||
segmented_wheel, stepped_area,
|
||||
};
|
||||
pub use drag::{
|
||||
PanelDrag, RowClick, SwatchRows, apply_row_click, art_drop, doc_preview, drag_ghost, drag_source, drop_target_outline, ghost_row, row_click,
|
||||
|
||||
@@ -486,6 +486,48 @@ fn wheel_shape(c: Pos2, r: f32, v: f32) -> egui::Shape {
|
||||
egui::Shape::mesh(mesh)
|
||||
}
|
||||
|
||||
/// Hue sectors and saturation rings of the [`segmented_wheel`]: patches 15° of hue by 20%
|
||||
/// saturation wide, the sectors centered on red at 0°.
|
||||
const WHEEL_SECTORS: u32 = 24;
|
||||
const WHEEL_RINGS: u32 = 5;
|
||||
|
||||
/// The color of the [`segmented_wheel`] patch that holds hue `h` (degrees) and saturation `s` (0 to
|
||||
/// 1), at brightness `v`: the hue and saturation at the patch's center.
|
||||
pub fn segment_color(h: f32, s: f32, v: f32) -> Color {
|
||||
let step = 360.0 / WHEEL_SECTORS as f32;
|
||||
let sector = (h.rem_euclid(360.0) / step).round() % WHEEL_SECTORS as f32;
|
||||
let ring = (s.clamp(0.0, 1.0) * WHEEL_RINGS as f32).floor().min((WHEEL_RINGS - 1) as f32);
|
||||
Color::from_hsb(sector * step, (ring + 0.5) / WHEEL_RINGS as f32, v)
|
||||
}
|
||||
|
||||
/// The segmented hue and saturation wheel at brightness `v`: a solid patch per hue sector and
|
||||
/// saturation ring, in its [`segment_color`].
|
||||
fn segmented_wheel_shape(c: Pos2, r: f32, v: f32) -> egui::Shape {
|
||||
// Points along each patch's arcs.
|
||||
const ARC: u32 = 4;
|
||||
let step = 360.0 / WHEEL_SECTORS as f32;
|
||||
let mut mesh = egui::Mesh::default();
|
||||
for ring in 0..WHEEL_RINGS {
|
||||
let (s0, s1) = (ring as f32 / WHEEL_RINGS as f32, (ring + 1) as f32 / WHEEL_RINGS as f32);
|
||||
for k in 0..WHEEL_SECTORS {
|
||||
let h = k as f32 * step;
|
||||
let col = crate::panels::c32(&segment_color(h, (s0 + s1) / 2.0, v));
|
||||
let first = mesh.vertices.len() as u32;
|
||||
for j in 0..=ARC {
|
||||
let a = h - step / 2.0 + step * j as f32 / ARC as f32;
|
||||
mesh.colored_vertex(wheel_pos(c, r, a, s0), col);
|
||||
mesh.colored_vertex(wheel_pos(c, r, a, s1), col);
|
||||
}
|
||||
for j in 0..ARC {
|
||||
let at = first + 2 * j;
|
||||
mesh.add_triangle(at, at + 1, at + 3);
|
||||
mesh.add_triangle(at, at + 3, at + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
egui::Shape::mesh(mesh)
|
||||
}
|
||||
|
||||
/// What a [`harmony_wheel`] did this frame.
|
||||
#[derive(Default)]
|
||||
pub struct WheelResponse {
|
||||
@@ -501,6 +543,18 @@ pub struct WheelResponse {
|
||||
/// marker per colour joined to the centre (the selected colour, `base`, larger) and a Brightness
|
||||
/// slider for the selected colour below it.
|
||||
pub fn harmony_wheel(ui: &mut Ui, id: &str, size: f32, colors: &[Color], base: Option<usize>) -> WheelResponse {
|
||||
wheel(ui, id, size, colors, base, false)
|
||||
}
|
||||
|
||||
/// [`harmony_wheel`] drawn as solid patches (24 hues by 5 saturations, each in its
|
||||
/// [`segment_color`]). The markers sit at their colors' own hue and
|
||||
/// saturation, and a drag moves them as freely as on the smooth wheel.
|
||||
pub fn segmented_wheel(ui: &mut Ui, id: &str, size: f32, colors: &[Color], base: Option<usize>) -> WheelResponse {
|
||||
wheel(ui, id, size, colors, base, true)
|
||||
}
|
||||
|
||||
/// [`harmony_wheel`], or with `segmented` [`segmented_wheel`].
|
||||
fn wheel(ui: &mut Ui, id: &str, size: f32, colors: &[Color], base: Option<usize>, segmented: bool) -> WheelResponse {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let mut out = WheelResponse::default();
|
||||
let base_hsb = base.and_then(|b| colors.get(b)).map(Color::to_hsb);
|
||||
@@ -510,7 +564,7 @@ pub fn harmony_wheel(ui: &mut Ui, id: &str, size: f32, colors: &[Color], base: O
|
||||
let (rect, resp) = ui.allocate_exact_size(vec2(size, size), Sense::click_and_drag());
|
||||
let (c, r) = (rect.center(), size / 2.0 - 10.0);
|
||||
let painter = ui.painter_at(rect);
|
||||
painter.add(wheel_shape(c, r, v.max(0.15)));
|
||||
painter.add(if segmented { segmented_wheel_shape(c, r, v.max(0.15)) } else { wheel_shape(c, r, v.max(0.15)) });
|
||||
painter.circle_stroke(c, r, Stroke::new(1.0, t.border));
|
||||
let at = |col: &Color| {
|
||||
let [h, s, _] = col.to_hsb();
|
||||
@@ -539,7 +593,8 @@ pub fn harmony_wheel(ui: &mut Ui, id: &str, size: f32, colors: &[Color], base: O
|
||||
let h = (-dv.y).atan2(dv.x).to_degrees().rem_euclid(360.0);
|
||||
out.moved = Some((i, [Some(h), Some((dv.length() / r).clamp(0.0, 1.0)), None]));
|
||||
}
|
||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Slider, ui.is_enabled(), tl!("Color wheel")));
|
||||
let name = if segmented { tl!("Segmented color wheel") } else { tl!("Color wheel") };
|
||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Slider, ui.is_enabled(), name));
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.horizontal(|ui| {
|
||||
|
||||
@@ -264,7 +264,10 @@ same sliders under the wheel, and its mode menu also has Global Adjust (`sliderM
|
||||
Brightness, Temperature and Luminosity sliders that set `globalAdjust` (`{saturation, brightness, temperature,
|
||||
luminosity}`, percent from -100 to 100, 0 by default). The dialog sends a non-zero `globalAdjust` as `recolor.apply`'s
|
||||
`globalAdjust` in every slider mode, so one slider changes every recolored color at once and the rows keep their new
|
||||
colors; colors opened alone are saved as a group with the change. Setting
|
||||
colors; colors opened alone are saved as a group with the change. `display` sets how the Edit tab shows the new
|
||||
colors: `smooth` (the smooth wheel, also when unset; each opening starts there), `segmented` (a wheel of solid patches,
|
||||
24 hues by 5 saturations, whose markers move as freely) or `bars` (a solid bar per recolored color in row order; a click
|
||||
selects its row for the sliders and a drag moves the row without changing a color). Setting
|
||||
`colors`, a preserve flag or `limitTo` reduces the rows again (also on `ui.dialog.confirm`). The dialog previews
|
||||
`recolor.apply` on the canvas; confirm keeps it as one undo step (with a group, the group is rewritten in the same
|
||||
step; with no art selected only the group changes). In the Swatches panel, double-clicking a colour group's folder,
|
||||
|
||||
@@ -988,6 +988,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
The Separations Preview panel's Gamut Check button reports one color outside the CMYK gamut in the singular and more in the plural, in English and in each language's plural forms (`gamut_check_status_takes_the_plural_form` in `crates/ui-egui/src/i18n/tests.rs`).
|
||||
- **Document Color Mode outside the layers:** File › Document Color Mode (in one undo step) and `document.open {colorMode}` also convert the colors of graph designs, graphic styles, opacity-mask art, the tile that pattern editing's Cancel puts back and the art of brushes (with a Hue Shift brush's key color); a tint of a global swatch there becomes the converted swatch's color at its tint, as in the art.
|
||||
- **Recolor Artwork layout (#3416):** the Edit chip list uses the modal scroll helper and its actual remaining height; the tab's minimum-height space scrolls with the chips. `tests_recolor` covers 5/20/200/400 colors, window resizing, 100/125/150/200% UI scaling, scrolling to and editing the final chip, Preview, Cancel, Confirm and Undo, alongside the existing #1733 drag regressions. Scores and estimates unchanged.
|
||||
- **Recolor Artwork Edit displays:** buttons beside the Edit tab's wheel show the colors on a smooth color wheel, a segmented color wheel or as color bars ([Edit colors using Recolor Artwork](https://helpx.adobe.com/illustrator/desktop/manage-colors/modify-colors/edit-colors-using-edit-colors-dialog.html): "Select a smooth color wheel, a segmented color wheel, or color bars to display and edit colors"). The segmented wheel draws 24 hues by 5 saturations as solid patches, and its markers move freely as on the smooth wheel. The bars show one solid bar per recolored color in row order: a click selects a bar for the color sliders, and a drag moves its row without changing a color ([Advanced recolor options overview](https://helpx.adobe.com/illustrator/desktop/manage-colors/learn-color-basics/edit-colors-dialog-overview.html)). The dialog opens on the smooth wheel; `display` (`smooth`, `segmented`, `bars`) is the dialog field. `tests_recolor` covers switching, the dialog height, segmented dragging and Brightness, and bar selection, editing, reordering, scrolling, Preview, Cancel and OK. Scores and estimates unchanged.
|
||||
- **Edit Colors parameters:** `edit.colors.saturate` needs a number `intensity`, the value from -100% to 100% that Saturate takes ([Adjusting colors](https://web.archive.org/web/20230128232631/https://helpx.adobe.com/illustrator/using/adjusting-colors.html)), and answers "missing number `intensity`" without one; a number outside the range counts as its nearest end. `edit.colors.adjustBalance` refuses a channel and `color.harmony` refuses `steps` or `amount` that is not a number; both read `null` as a value left out. `tests::editcolors` and `tests::colorguide` cover them.
|
||||
- **Color Picker layout (#2008):** a centered draggable header, equal-width actions above aligned HSB/RGB/Hex and Lab/CMYK fields, compact field and slider, and warnings below the preview. No horizontal scrolling; narrow windows stack the sections with vertical scrolling. Tests in `dialogs/color_picker.rs` cover actual pointer/key input, typed values before OK/Enter, Cancel/Escape rollback, one Undo step, mode-size stability, warnings and layout at two UI scales. The screen sampler sits above the field and the upstream two-grid Tab order is retained. AccessKit regressions check the action names and each channel slider's displayed value, units and range. German and enlarged English UI renders were reviewed locally; no reference images are shipped. This is a layout improvement, with no change to scores or remaining-hour estimates.
|
||||
- **Eyedropper sources (#1251):** ordinary and Shift clicks, Shift+Alt appearance append, raster pixels and gradient-stop sampling can read visible locked/template art and sources outside isolation. Sampling leaves the source and selection intact; Alt reverse still uses editable hits, and hidden art stays excluded. Engine/tool/hit regressions cover the editing boundary, color models and one-step Undo/Redo. The general object/image workflow is described in [public HelpX](https://helpx.adobe.com/illustrator/using/tool-techniques/eyedropper-tool.html); the lock/isolation expectations come from the user report and these regressions, without a new full-parity measurement.
|
||||
|
||||
Reference in New Issue
Block a user