mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-31 03:32:08 +01:00
Type: paragraph direction buttons in the Paragraph panel and options bar (TYPE-218-2, #218)
The engine has had TextDirection (Auto / LTR / RTL) and type.setStyle's "direction" key, but nothing in the UI set it. Now: - Window > Paragraph and the Properties Paragraph section have a row of three buttons under the alignment row: Automatic, Left-to-right and Right-to-left paragraph direction. They set the selected layers' direction (or the selected text's paragraphs while typing) in one undo step, and show the current one. - The Type options bar has the same three buttons after its alignment buttons. Like alignment, they also set the direction new type starts with (new tool option `type_direction`, "auto" by default, reset with the tool's other options). - The glyph is drawn (a pilcrow over an arrow), so no new icon asset. - The three tooltips are translated in all 20 catalogs (every_tl_literal_is_translated). Tests: paragraph_direction_buttons_set_and_show_the_direction and options_bar_direction_sets_selected_layers_and_new_type. TYPE-218-2 goes from missing to partial: the visual RTL caret (arrow keys moving on screen rather than in logical order) is still to do. scorecard/type.toml, docs/scorecard.md and docs/gaps.md (G8) are updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WpQQNxTPBzSLvgAhX27Vtv
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
2a952f164c
commit
8dcc64bd2f
@@ -3816,3 +3816,8 @@ navigator Zoom Zvětšení
|
||||
This document's layers: {needed} of {budget} for layers Vrstvy tohoto dokumentu: {needed} z {budget} pro vrstvy
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Vrstvy tohoto dokumentu potřebují {needed}, ale mohou zabrat {budget}: překreslení celého dokumentu probíhá na CPU
|
||||
GPU Memory (MB, 0 = Automatic) Paměť GPU (MB, 0 = Automaticky)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Automatický směr odstavce
|
||||
Left-to-right paragraph direction Směr odstavce zleva doprava
|
||||
Right-to-left paragraph direction Směr odstavce zprava doleva
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3210,3 +3210,8 @@ curves Smooth Glätten
|
||||
This document's layers: {needed} of {budget} for layers Ebenen dieses Dokuments: {needed} von {budget} für Ebenen
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Die Ebenen dieses Dokuments brauchen {needed}, dürfen aber {budget} belegen: Das ganze Dokument wird auf der CPU neu gezeichnet
|
||||
GPU Memory (MB, 0 = Automatic) GPU-Speicher (MB, 0 = Automatisch)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Automatische Absatzrichtung
|
||||
Left-to-right paragraph direction Absatzrichtung von links nach rechts
|
||||
Right-to-left paragraph direction Absatzrichtung von rechts nach links
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3908,3 +3908,8 @@ curves Smooth Εξομάλυνση
|
||||
This document's layers: {needed} of {budget} for layers Επίπεδα αυτού του εγγράφου: {needed} από {budget} για επίπεδα
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Τα επίπεδα αυτού του εγγράφου χρειάζονται {needed} αλλά μπορούν να καταλάβουν {budget}: η επανασχεδίαση όλου του εγγράφου γίνεται στη CPU
|
||||
GPU Memory (MB, 0 = Automatic) Μνήμη GPU (MB, 0 = Αυτόματο)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Αυτόματη κατεύθυνση παραγράφου
|
||||
Left-to-right paragraph direction Κατεύθυνση παραγράφου από αριστερά προς τα δεξιά
|
||||
Right-to-left paragraph direction Κατεύθυνση παραγράφου από δεξιά προς τα αριστερά
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1030 and column 18.
|
@@ -4170,3 +4170,8 @@ navigator Zoom Zoom
|
||||
This document's layers: {needed} of {budget} for layers Capas de este documento: {needed} de {budget} para capas
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Las capas de este documento necesitan {needed} pero pueden ocupar {budget}: los redibujados de todo el documento usan la CPU
|
||||
GPU Memory (MB, 0 = Automatic) Memoria de GPU (MB, 0 = Automático)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Dirección de párrafo automática
|
||||
Left-to-right paragraph direction Dirección de párrafo de izquierda a derecha
|
||||
Right-to-left paragraph direction Dirección de párrafo de derecha a izquierda
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -3213,3 +3213,8 @@ curves Smooth Lisser
|
||||
This document's layers: {needed} of {budget} for layers Calques de ce document : {needed} sur {budget} pour les calques
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Les calques de ce document ont besoin de {needed} mais peuvent occuper {budget} : le document entier se redessine sur le CPU
|
||||
GPU Memory (MB, 0 = Automatic) Mémoire GPU (Mo, 0 = Automatique)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Sens du paragraphe automatique
|
||||
Left-to-right paragraph direction Sens du paragraphe de gauche à droite
|
||||
Right-to-left paragraph direction Sens du paragraphe de droite à gauche
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4127,3 +4127,8 @@ cameraRaw Yellow Sárga
|
||||
This document's layers: {needed} of {budget} for layers A dokumentum rétegei: {needed} / {budget} a rétegeknek
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU A dokumentum rétegeinek {needed} kell, de {budget} foglalhatnak: a teljes dokumentum újrarajzolása a CPU-n fut
|
||||
GPU Memory (MB, 0 = Automatic) GPU-memória (MB, 0 = Automatikus)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Automatikus bekezdésirány
|
||||
Left-to-right paragraph direction Balról jobbra irányuló bekezdés
|
||||
Right-to-left paragraph direction Jobbról balra irányuló bekezdés
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3200,3 +3200,8 @@ curves Smooth Haluskan
|
||||
This document's layers: {needed} of {budget} for layers Layer dokumen ini: {needed} dari {budget} untuk layer
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Layer dokumen ini membutuhkan {needed} tetapi boleh memakai {budget}: penggambaran ulang seluruh dokumen memakai CPU
|
||||
GPU Memory (MB, 0 = Automatic) Memori GPU (MB, 0 = Otomatis)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Arah paragraf otomatis
|
||||
Left-to-right paragraph direction Arah paragraf kiri ke kanan
|
||||
Right-to-left paragraph direction Arah paragraf kanan ke kiri
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -4329,3 +4329,8 @@ shape Appearance Aspetto
|
||||
This document's layers: {needed} of {budget} for layers Livelli di questo documento: {needed} su {budget} per i livelli
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU I livelli di questo documento richiedono {needed} ma possono occupare {budget}: i ridisegni dell'intero documento usano la CPU
|
||||
GPU Memory (MB, 0 = Automatic) Memoria GPU (MB, 0 = Automatico)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Direzione del paragrafo automatica
|
||||
Left-to-right paragraph direction Direzione del paragrafo da sinistra a destra
|
||||
Right-to-left paragraph direction Direzione del paragrafo da destra a sinistra
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2653 and column 53.
|
@@ -3884,3 +3884,8 @@ curves Smooth スムーズ
|
||||
This document's layers: {needed} of {budget} for layers このドキュメントのレイヤー: レイヤー用 {budget} のうち {needed}
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU このドキュメントのレイヤーには {needed} が必要ですが、使えるのは {budget} です: ドキュメント全体の再描画は CPU で行われます
|
||||
GPU Memory (MB, 0 = Automatic) GPU メモリ(MB、0 = 自動)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction 段落の方向を自動判別
|
||||
Left-to-right paragraph direction 左から右への段落方向
|
||||
Right-to-left paragraph direction 右から左への段落方向
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3386,3 +3386,8 @@ curves Smooth 매끄럽게
|
||||
This document's layers: {needed} of {budget} for layers 이 문서의 레이어: 레이어용 {budget} 중 {needed}
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU 이 문서의 레이어에 {needed}가 필요하지만 {budget}만 사용할 수 있습니다: 문서 전체 다시 그리기는 CPU를 사용합니다
|
||||
GPU Memory (MB, 0 = Automatic) GPU 메모리(MB, 0 = 자동)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction 자동 단락 방향
|
||||
Left-to-right paragraph direction 왼쪽에서 오른쪽 단락 방향
|
||||
Right-to-left paragraph direction 오른쪽에서 왼쪽 단락 방향
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3165 and column 53.
|
@@ -3968,3 +3968,8 @@ curves Smooth Vloeiend maken
|
||||
This document's layers: {needed} of {budget} for layers Lagen van dit document: {needed} van {budget} voor lagen
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU De lagen van dit document hebben {needed} nodig maar mogen {budget} gebruiken: het hele document wordt op de CPU opnieuw getekend
|
||||
GPU Memory (MB, 0 = Automatic) GPU-geheugen (MB, 0 = Automatisch)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Automatische alinearichting
|
||||
Left-to-right paragraph direction Alinearichting van links naar rechts
|
||||
Right-to-left paragraph direction Alinearichting van rechts naar links
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -3806,3 +3806,8 @@ curves Smooth Wygładź
|
||||
This document's layers: {needed} of {budget} for layers Warstwy tego dokumentu: {needed} z {budget} na warstwy
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Warstwy tego dokumentu potrzebują {needed}, ale mogą zająć {budget}: odświeżanie całego dokumentu używa CPU
|
||||
GPU Memory (MB, 0 = Automatic) Pamięć GPU (MB, 0 = Automatycznie)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Automatyczny kierunek akapitu
|
||||
Left-to-right paragraph direction Kierunek akapitu od lewej do prawej
|
||||
Right-to-left paragraph direction Kierunek akapitu od prawej do lewej
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3998,3 +3998,8 @@ layerLabel No Color Sem cor
|
||||
This document's layers: {needed} of {budget} for layers Camadas deste documento: {needed} de {budget} para camadas
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU As camadas deste documento precisam de {needed} mas podem ocupar {budget}: o documento inteiro é redesenhado na CPU
|
||||
GPU Memory (MB, 0 = Automatic) Memória da GPU (MB, 0 = Automático)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Direção automática do parágrafo
|
||||
Left-to-right paragraph direction Direção do parágrafo da esquerda para a direita
|
||||
Right-to-left paragraph direction Direção do parágrafo da direita para a esquerda
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3178,3 +3178,8 @@ curves Smooth Сгладить
|
||||
This document's layers: {needed} of {budget} for layers Слои этого документа: {needed} из {budget} для слоёв
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Слоям этого документа нужно {needed}, но занимать они могут {budget}: весь документ перерисовывается на CPU
|
||||
GPU Memory (MB, 0 = Automatic) Память GPU (МБ, 0 = автоматически)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Автоматическое направление абзаца
|
||||
Left-to-right paragraph direction Направление абзаца слева направо
|
||||
Right-to-left paragraph direction Направление абзаца справа налево
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4281,3 +4281,8 @@ shape Appearance Izgled
|
||||
This document's layers: {needed} of {budget} for layers Slojevi ovog dokumenta: {needed} od {budget} za slojeve
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Slojevima ovog dokumenta treba {needed}, a smeju da zauzmu {budget}: ceo dokument se ponovo iscrtava na CPU-u
|
||||
GPU Memory (MB, 0 = Automatic) GPU memorija (MB, 0 = Automatski)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Automatski smer pasusa
|
||||
Left-to-right paragraph direction Smer pasusa sleva nadesno
|
||||
Right-to-left paragraph direction Smer pasusa zdesna nalevo
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3938,3 +3938,8 @@ cameraData Lens Lens
|
||||
This document's layers: {needed} of {budget} for layers Bu belgenin katmanları: katmanlar için {budget} içinden {needed}
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Bu belgenin katmanlarının {needed} belleğe ihtiyacı var ama en fazla {budget} kullanabilirler: tüm belgenin yeniden çizimi CPU'yu kullanır
|
||||
GPU Memory (MB, 0 = Automatic) GPU Belleği (MB, 0 = Otomatik)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Otomatik paragraf yönü
|
||||
Left-to-right paragraph direction Soldan sağa paragraf yönü
|
||||
Right-to-left paragraph direction Sağdan sola paragraf yönü
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -3907,3 +3907,8 @@ curves Smooth Згладити
|
||||
This document's layers: {needed} of {budget} for layers Шари цього документа: {needed} з {budget} для шарів
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Шарам цього документа потрібно {needed}, але займати вони можуть {budget}: увесь документ перемальовується на CPU
|
||||
GPU Memory (MB, 0 = Automatic) Пам'ять GPU (МБ, 0 = автоматично)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Автоматичний напрямок абзацу
|
||||
Left-to-right paragraph direction Напрямок абзацу зліва направо
|
||||
Right-to-left paragraph direction Напрямок абзацу справа наліво
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1388 and column 18.
|
@@ -3694,3 +3694,8 @@ curves Smooth Làm mượt
|
||||
This document's layers: {needed} of {budget} for layers Các lớp của tài liệu này: {needed} trên {budget} dành cho lớp
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU Các lớp của tài liệu này cần {needed} nhưng chỉ được dùng {budget}: vẽ lại toàn bộ tài liệu dùng CPU
|
||||
GPU Memory (MB, 0 = Automatic) Bộ nhớ GPU (MB, 0 = Tự động)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction Hướng đoạn văn tự động
|
||||
Left-to-right paragraph direction Hướng đoạn văn từ trái sang phải
|
||||
Right-to-left paragraph direction Hướng đoạn văn từ phải sang trái
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2824,3 +2824,8 @@ curves Smooth 平滑
|
||||
This document's layers: {needed} of {budget} for layers 此文档的图层:图层可用 {budget} 中的 {needed}
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU 此文档的图层需要 {needed},但只能占用 {budget}:整个文档的重绘使用 CPU
|
||||
GPU Memory (MB, 0 = Automatic) GPU 内存(MB,0 = 自动)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction 自动段落方向
|
||||
Left-to-right paragraph direction 从左到右的段落方向
|
||||
Right-to-left paragraph direction 从右到左的段落方向
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2599 and column 53.
|
@@ -2809,3 +2809,8 @@ curves Smooth 平滑
|
||||
This document's layers: {needed} of {budget} for layers 此文件的圖層:圖層可用 {budget} 中的 {needed}
|
||||
This document's layers need {needed} but may hold {budget}: whole-document redraws use the CPU 此文件的圖層需要 {needed},但只能佔用 {budget}:整份文件的重繪使用 CPU
|
||||
GPU Memory (MB, 0 = Automatic) GPU 記憶體(MB,0 = 自動)
|
||||
|
||||
# Paragraph direction (TYPE-218-2)
|
||||
Automatic paragraph direction 自動段落方向
|
||||
Left-to-right paragraph direction 由左至右的段落方向
|
||||
Right-to-left paragraph direction 由右至左的段落方向
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -539,6 +539,9 @@ pub struct ToolOptions {
|
||||
pub type_size: f32,
|
||||
pub type_aa: String,
|
||||
pub type_align: String,
|
||||
/// Type tool: paragraph direction of new type, "auto" (from the first strong character),
|
||||
/// "ltr" or "rtl" (TYPE-218-2).
|
||||
pub type_direction: String,
|
||||
/// Clone Stamp / Healing Brush.
|
||||
pub clone_aligned: bool,
|
||||
/// Pattern Stamp: lock the tile origin in document space across strokes.
|
||||
@@ -784,6 +787,7 @@ impl Default for ToolOptions {
|
||||
type_size: 48.0,
|
||||
type_aa: "sharp".into(),
|
||||
type_align: "left".into(),
|
||||
type_direction: "auto".into(),
|
||||
clone_aligned: true,
|
||||
pattern_stamp_aligned: true,
|
||||
pattern_stamp_impressionist: false,
|
||||
|
||||
@@ -150,7 +150,9 @@ pub fn reset_options(o: &mut ToolOptions, tool: Tool) {
|
||||
Tool::MagicEraser => reset!(tolerance, anti_alias, contiguous, sample_all_layers, magic_eraser_opacity),
|
||||
Tool::Gradient => reset!(gradient_style, gradient_reverse, gradient_dither, gradient_classic, gradient_blend_mode, fill_opacity),
|
||||
Tool::PaintBucket => reset!(bucket_fill_pattern, fill_opacity, tolerance, anti_alias, contiguous, sample_all_layers),
|
||||
Tool::Type | Tool::VerticalType | Tool::HorizontalTypeMask | Tool::VerticalTypeMask => reset!(type_font, type_style, type_size, type_aa, type_align),
|
||||
Tool::Type | Tool::VerticalType | Tool::HorizontalTypeMask | Tool::VerticalTypeMask => {
|
||||
reset!(type_font, type_style, type_size, type_aa, type_align, type_direction)
|
||||
}
|
||||
Tool::Zoom => reset!(zoom_scrubby),
|
||||
Tool::SpotHealing => reset!(spot_type, sample_all_layers),
|
||||
Tool::Healing => reset!(clone_aligned, clone_sample),
|
||||
|
||||
@@ -317,6 +317,7 @@ pub fn pointer_up(app: &mut PhotocraftApp, start: [f64; 2], end: [f64; 2]) {
|
||||
"fontStyle": o.type_style,
|
||||
"size": o.type_size,
|
||||
"align": o.type_align,
|
||||
"direction": o.type_direction,
|
||||
"color": hex(app.session.tools.foreground),
|
||||
});
|
||||
if w >= min && h >= min {
|
||||
@@ -1780,6 +1781,15 @@ pub fn options_bar(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
ui.spacing_mut().item_spacing.x = 8.0;
|
||||
crate::widgets::vline(ui, 22.0);
|
||||
ui.spacing_mut().item_spacing.x = 2.0;
|
||||
for (key, tip, d) in direction_items() {
|
||||
if direction_button(ui, d, o.type_direction == key, tip).clicked() {
|
||||
app.ui.tool_options.type_direction = key.into();
|
||||
apply(app, ui.ctx(), json!({"direction": key}));
|
||||
}
|
||||
}
|
||||
ui.spacing_mut().item_spacing.x = 8.0;
|
||||
crate::widgets::vline(ui, 22.0);
|
||||
// Text colour swatch with a picker popup.
|
||||
let c = shown
|
||||
.as_ref()
|
||||
@@ -1983,6 +1993,47 @@ fn align_glyph(p: &egui::Painter, r: egui::Rect, a: photocraft_doc::text::TextAl
|
||||
}
|
||||
}
|
||||
|
||||
/// Paragraph direction choices for the Paragraph panel and the options bar (TYPE-218-2): the
|
||||
/// `direction` value `type.setStyle` takes, the tooltip, and the direction it sets.
|
||||
fn direction_items() -> [(&'static str, &'static str, photocraft_doc::text::TextDirection); 3] {
|
||||
use photocraft_doc::text::TextDirection as D;
|
||||
[
|
||||
("auto", tl!("Automatic paragraph direction"), D::Auto),
|
||||
("ltr", tl!("Left-to-right paragraph direction"), D::Ltr),
|
||||
("rtl", tl!("Right-to-left paragraph direction"), D::Rtl),
|
||||
]
|
||||
}
|
||||
|
||||
/// A pilcrow over an arrow pointing the way the paragraph runs; Auto points both ways.
|
||||
fn direction_glyph(p: &egui::Painter, r: egui::Rect, d: photocraft_doc::text::TextDirection, c: Color32) {
|
||||
use photocraft_doc::text::TextDirection as D;
|
||||
let g = p.layout_no_wrap("¶".to_string(), egui::FontId::proportional(11.0), c);
|
||||
p.galley(egui::pos2(r.center().x - g.size().x / 2.0, r.top() - 2.0), g, c);
|
||||
let (y, s) = (r.bottom() - 1.5, Stroke::new(1.3, c));
|
||||
p.line_segment([egui::pos2(r.left(), y), egui::pos2(r.right(), y)], s);
|
||||
let head = |x: f32, towards: f32| {
|
||||
for dy in [-3.0, 3.0] {
|
||||
p.line_segment([egui::pos2(x, y), egui::pos2(x - towards * 3.0, y + dy)], s);
|
||||
}
|
||||
};
|
||||
if d != D::Rtl {
|
||||
head(r.right(), 1.0);
|
||||
}
|
||||
if d != D::Ltr {
|
||||
head(r.left(), -1.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// The options bar's 24 px square button for one paragraph direction, styled like its icon buttons.
|
||||
fn direction_button(ui: &mut egui::Ui, d: photocraft_doc::text::TextDirection, selected: bool, tip: &str) -> egui::Response {
|
||||
let (r, resp) = ui.allocate_exact_size(egui::vec2(24.0, 24.0), egui::Sense::click());
|
||||
let tint = crate::icons::button_chrome(ui, r, selected, resp.hovered());
|
||||
direction_glyph(ui.painter(), egui::Rect::from_center_size(r.center(), egui::vec2(14.0, 16.0)), d, tint);
|
||||
let tip = tip.to_string();
|
||||
resp.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::Button, true, selected, &tip));
|
||||
resp.on_hover_text(tip)
|
||||
}
|
||||
|
||||
/// A toggle button (faux styles, paragraph alignment) of width `w`, painted by `paint`.
|
||||
fn toggle_cell(ui: &mut egui::Ui, w: f32, on: bool, tip: &str, paint: impl FnOnce(&egui::Ui, egui::Rect, Color32)) -> bool {
|
||||
let t = crate::theme::Tokens::get(ui.ctx());
|
||||
@@ -2305,6 +2356,19 @@ fn type_sections(app: &mut PhotocraftApp, ui: &mut egui::Ui, character: bool, pa
|
||||
}
|
||||
});
|
||||
ui.add_space(ROW_GAP);
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 2.0;
|
||||
for (key, tip, d) in direction_items() {
|
||||
let clicked = toggle_cell(ui, cw, para.direction == d, tip, |ui, r, col| {
|
||||
let g = egui::Rect::from_center_size(r.center(), egui::vec2(14.0, 16.0));
|
||||
direction_glyph(ui.painter(), g, d, col);
|
||||
});
|
||||
if clicked {
|
||||
apply(app, ui.ctx(), json!({"direction": key}));
|
||||
}
|
||||
}
|
||||
});
|
||||
ui.add_space(ROW_GAP);
|
||||
row(ui, &mut |ui| {
|
||||
if let Some(v) = num_field(ui, "→|", "Indent left margin", para.start_indent_pt, -1296.0..=1296.0, "pt", w) {
|
||||
apply(app, ui.ctx(), json!({"startIndent": v}));
|
||||
|
||||
@@ -1438,6 +1438,62 @@ mod multi_layer_formatting {
|
||||
});
|
||||
}
|
||||
|
||||
/// The Paragraph section's direction buttons set the selected layers' paragraph direction in
|
||||
/// one undo step, show the current one, and leave unselected layers and the tool's default for
|
||||
/// new type alone (TYPE-218-2).
|
||||
#[test]
|
||||
fn paragraph_direction_buttons_set_and_show_the_direction() {
|
||||
use egui::accesskit::Toggled;
|
||||
use egui_kittest::kittest::NodeT;
|
||||
use photocraft_doc::text::TextDirection;
|
||||
let (app, ids) = fixture();
|
||||
let dir = |app: &PhotocraftApp, id: LayerId| text(app, id).paragraph_runs()[0].style.direction;
|
||||
let shown = |h: &Harness<'static, PhotocraftApp>, label: &str| h.get_by_label(label).accesskit_node().toggled();
|
||||
let mut h = controls(app, true);
|
||||
assert_eq!(shown(&h, "Automatic paragraph direction"), Some(Toggled::True), "new type follows its text");
|
||||
assert_eq!(shown(&h, "Right-to-left paragraph direction"), Some(Toggled::False));
|
||||
let steps = h.state().session.active().unwrap().history.entries().len();
|
||||
h.get_by_label("Right-to-left paragraph direction").click();
|
||||
h.run();
|
||||
for id in &ids[..2] {
|
||||
assert_eq!(dir(h.state(), *id), TextDirection::Rtl, "selected layers get the direction");
|
||||
}
|
||||
assert_eq!(dir(h.state(), ids[2]), TextDirection::Auto, "an unselected layer is untouched");
|
||||
assert_eq!(h.state().session.active().unwrap().history.entries().len(), steps + 1, "one undo step");
|
||||
assert_eq!(h.state().ui.tool_options.type_direction, "auto", "the panel edits layers, not new type");
|
||||
h.run();
|
||||
assert_eq!(shown(&h, "Right-to-left paragraph direction"), Some(Toggled::True), "the button shows it");
|
||||
assert_eq!(shown(&h, "Automatic paragraph direction"), Some(Toggled::False));
|
||||
h.get_by_label("Left-to-right paragraph direction").click();
|
||||
h.run();
|
||||
assert_eq!(dir(h.state(), ids[0]), TextDirection::Ltr);
|
||||
assert!(h.state_mut().session.undo());
|
||||
assert!(h.state_mut().session.undo());
|
||||
for id in &ids[..2] {
|
||||
assert_eq!(dir(h.state(), *id), TextDirection::Auto, "undo restores it");
|
||||
}
|
||||
}
|
||||
|
||||
/// The options bar's direction buttons set the selected layers' direction and the one new
|
||||
/// type starts with, as its alignment buttons do (TYPE-218-2).
|
||||
#[test]
|
||||
fn options_bar_direction_sets_selected_layers_and_new_type() {
|
||||
use photocraft_doc::text::TextDirection;
|
||||
let (app, ids) = fixture();
|
||||
let mut h = controls(app, false);
|
||||
h.get_by_label("Right-to-left paragraph direction").click();
|
||||
h.run();
|
||||
assert_eq!(h.state().ui.tool_options.type_direction, "rtl");
|
||||
for id in &ids[..2] {
|
||||
assert_eq!(text(h.state(), *id).paragraph_runs()[0].style.direction, TextDirection::Rtl);
|
||||
}
|
||||
assert_eq!(text(h.state(), ids[2]).paragraph_runs()[0].style.direction, TextDirection::Auto);
|
||||
super::super::pointer_up(h.state_mut(), [40.0, 60.0], [40.0, 60.0]);
|
||||
let id = h.state().session.active().unwrap().active_layer.unwrap();
|
||||
assert!(!ids.contains(&id), "a new type layer");
|
||||
assert_eq!(text(h.state(), id).paragraph_runs()[0].style.direction, TextDirection::Rtl, "new type starts right to left");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_font_family_preserves_peer_faces_and_batches_layer_flags() {
|
||||
let (app, ids) = fixture();
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
# Where PhotoCraft falls short of Photoshop
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** minor (G1: smart filter masks show on PSD open, Photoshop-oracle floor 188/258; G10: DPX read and write, #1049, Cineon read; G1: CMYK Hue/Saturation and Color Balance keep the black ink; G1: CMYK Posterize on the stored values, psd-tools oracle floor 242/309; G1: CMYK adjustment layers composite on the document's inks, Photoshop-oracle floor 165/258; G1: CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; G1: modern Brightness/Contrast's brightness curve matches Photoshop, Photoshop-oracle floor 151/258; G1: Lab documents composite on their stored L, a, b, Photoshop-oracle floor 152/258; G1: Levels on whole levels at every step, Photoshop-oracle floor 146/258, psd-tools 238/309; G1: 32-bit Brightness/Contrast legacy, Channel Mixer, Photo Filter and Black & White tint unclipped as in Photoshop, Photoshop-oracle floor 149/258; G1: mask feathers blur with their exact variance, psd-tools oracle floor 240/309; G1: Threshold splits on Photoshop's luminosity, psd-tools oracle floor 241/309; G1: pass-through clipping base with an adjustment renders isolated, psd-tools oracle floor 240/309; G1: 16-bit Posterize as Photoshop bins it, psd-tools oracle floor 241/309; G1: Gradient Map driven by the W3C luminosity, Photoshop-oracle floor 147/258; G7: Field Blur's on-canvas pins, #3373; G4: Hue Jitter amount track, #3825 partial; G4: minimum field interaction, #3825 partial; G1: CMYK Invert, Posterize and Threshold on the inks, 16-bit Lab Invert; Photoshop-oracle floor 145/258; G11: `.atn` import maps 112 events: Select and Mask, Filter Gallery Spatter, Ripple, Crystallize, Pattern Fill layers, mask targeting, Step Backward, nudges in points, Bitmap mode; G5: Free Transform H/V skew fields; G9: toolbar tool shortcut editing; refreshed against main: G2, G5, G7, G9, G10, G13, G14, G17 items that landed; G10: DPX read and write, #1049; G4: checkbox-only Brush Settings rows; G11: Load/Save Actions `.atn` landed, AUTO-219-2 partial; G5: hold-a-key spring-loaded tools landed, #2672; G1: #2465 fixed; stale missing-tool rows corrected: Color Replacement, Red Eye and Add/Delete Anchor Point are live tools, so only Art History Brush is missing; no estimate changed) · **Target:** Adobe Photoshop 2026
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-16 · **Change:** minor (G8: paragraph direction buttons in the Paragraph panel and options bar, TYPE-218-2 partial; G1: smart filter masks show on PSD open, Photoshop-oracle floor 188/258; G10: DPX read and write, #1049, Cineon read; G1: CMYK Hue/Saturation and Color Balance keep the black ink; G1: CMYK Posterize on the stored values, psd-tools oracle floor 242/309; G1: CMYK adjustment layers composite on the document's inks, Photoshop-oracle floor 165/258; G1: CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; G1: modern Brightness/Contrast's brightness curve matches Photoshop, Photoshop-oracle floor 151/258; G1: Lab documents composite on their stored L, a, b, Photoshop-oracle floor 152/258; G1: Levels on whole levels at every step, Photoshop-oracle floor 146/258, psd-tools 238/309; G1: 32-bit Brightness/Contrast legacy, Channel Mixer, Photo Filter and Black & White tint unclipped as in Photoshop, Photoshop-oracle floor 149/258; G1: mask feathers blur with their exact variance, psd-tools oracle floor 240/309; G1: Threshold splits on Photoshop's luminosity, psd-tools oracle floor 241/309; G1: pass-through clipping base with an adjustment renders isolated, psd-tools oracle floor 240/309; G1: 16-bit Posterize as Photoshop bins it, psd-tools oracle floor 241/309; G1: Gradient Map driven by the W3C luminosity, Photoshop-oracle floor 147/258; G7: Field Blur's on-canvas pins, #3373; G4: Hue Jitter amount track, #3825 partial; G4: minimum field interaction, #3825 partial; G1: CMYK Invert, Posterize and Threshold on the inks, 16-bit Lab Invert; Photoshop-oracle floor 145/258; G11: `.atn` import maps 112 events: Select and Mask, Filter Gallery Spatter, Ripple, Crystallize, Pattern Fill layers, mask targeting, Step Backward, nudges in points, Bitmap mode; G5: Free Transform H/V skew fields; G9: toolbar tool shortcut editing; refreshed against main: G2, G5, G7, G9, G10, G13, G14, G17 items that landed; G10: DPX read and write, #1049; G4: checkbox-only Brush Settings rows; G11: Load/Save Actions `.atn` landed, AUTO-219-2 partial; G5: hold-a-key spring-loaded tools landed, #2672; G1: #2465 fixed; stale missing-tool rows corrected: Color Replacement, Red Eye and Add/Delete Anchor Point are live tools, so only Art History Brush is missing; no estimate changed) · **Target:** Adobe Photoshop 2026
|
||||
|
||||
Every known shortfall against Photoshop 2026 (27.11.0), one entry each, ranked by user impact
|
||||
on the way to beta. This is the work list agents pick from: take the highest-ranked entry that
|
||||
@@ -78,13 +78,13 @@ what an agent can't finish alone.
|
||||
- **Estimate:** 80–140 h.
|
||||
|
||||
### G8. Type depth
|
||||
- **Missing:** type on a path (TYPE-218-1), paragraph direction UI (TYPE-218-2), complex-script
|
||||
fallback fonts and golden tests (TYPE-218-3), OpenType Numerals and Stylistic Sets submenus
|
||||
- **Missing:** type on a path (TYPE-218-1), the visual RTL caret (TYPE-218-2; the paragraph
|
||||
direction buttons landed 2026-10-16), complex-script fallback fonts and golden tests (TYPE-218-3), OpenType Numerals and Stylistic Sets submenus
|
||||
(TYPE-218-5; the Character panel buttons landed 2026-10-11, #3752), live Warp Text preview
|
||||
(TYPE-218-7, #2689), "rasterize and continue" for distort/perspective on type
|
||||
(TYPE-218-9), text free transform (#2630). Done since the re-measure: manual and optical kerning
|
||||
with PSD round trip (TYPE-218-6, #206), variable-font axes (TYPE-218-4, #2985).
|
||||
- **Evidence:** scorecard Type: 3 done, 2 partial, 4 missing. 2026-10-11: 2/3/4/5 clicks select
|
||||
- **Evidence:** scorecard Type: 4 done, 4 partial, 1 missing. 2026-10-11: 2/3/4/5 clicks select
|
||||
word, line, paragraph and all text, with tests (TYPE-218-8, #3493). The Type tool froze the app for
|
||||
60 s on one macOS machine (#2518).
|
||||
- **Estimate:** 120–200 h.
|
||||
@@ -270,6 +270,7 @@ what an agent can't finish alone.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-16 | minor | G8: Auto / Left-to-right / Right-to-left paragraph direction buttons in the Paragraph panel, Properties and the Type options bar; TYPE-218-2 partial (the visual RTL caret is still missing) |
|
||||
| 2026-10-14 | minor | G1: a PSD smart object's filter mask shows when the file opens (Photoshop stores the filtered pixels unmasked); export stores them that way too; Photoshop-oracle floor 185 → 188 of 258 |
|
||||
| 2026-10-13 | minor | G14: D90 image area measured (4288 × 2848 at (10, 10)); gap remains open |
|
||||
| 2026-10-14 | minor | Corpus floors raised to the measured results of the CMYK, Lab, 32-bit, Levels, Threshold, Posterize, feather and pass-through fixes landed together: Photoshop-authored 165 → 185 of 258, io 146 → 148 of 170, psd-tools 242 → 249 of 309 |
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ Generated by `cargo xtask scorecard` from `perf/budgets.toml`, `perf/baseline.js
|
||||
| [File compatibility](#file-compatibility) | 7 | 14 | 6 | io corpus: oracle ≥ 148/170, round trip ≥ 169/170; psd-tools corpus: oracle ≥ 249/309, round trip ≥ 308/309; photoshop oracles: oracle ≥ 188/256, round trip ≥ 258/256 |
|
||||
| [Tools](#tools) | 33 | 11 | 0 | |
|
||||
| [Workspace and UI](#workspace-and-ui) | 13 | 6 | 4 | settings that do nothing: 26 of 168 (target 0) |
|
||||
| [Type](#type) | 4 | 3 | 2 | |
|
||||
| [Type](#type) | 4 | 4 | 1 | |
|
||||
| [Automation](#automation) | 5 | 2 | 4 | 631 of 631 menu items live (100.0%) |
|
||||
| [Reliability](#reliability) | 5 | 2 | 0 | never-crash lints on 29 of 29 crates; panic_hunt present |
|
||||
| [Distribution](#distribution) | 5 | 4 | 2 | |
|
||||
@@ -214,7 +214,7 @@ Unread: `general.color_picker`, `general.animated_zoom`, `general.zoom_resizes_w
|
||||
| Id | Target | Status | Issue | Note |
|
||||
|---|---|---|---|---|
|
||||
| TYPE-218-1 | Type on a path: path shape, glyphs along the curve, caret follows, PSD round trip | partial | [#218](https://github.com/storytold/photocraft/issues/218) | TextShape::Path (doc text.rs) with glyphs placed rigidly along the curve by arc length (text warp.rs PathFrame), type.attachToPath and type.detachFromPath (#4365), .pcraft round trip. Missing: clicking a path with the Type tool, the caret and selection following the curve, and PSD path text (it saves as point text) |
|
||||
| TYPE-218-2 | Paragraph direction (Auto/LTR/RTL) in the panel and options bar; visual RTL caret | missing | [#218](https://github.com/storytold/photocraft/issues/218) | Engine has TextDirection (text.rs:155); no UI |
|
||||
| TYPE-218-2 | Paragraph direction (Auto/LTR/RTL) in the panel and options bar; visual RTL caret | partial | [#218](https://github.com/storytold/photocraft/issues/218) | Paragraph section (Window > Paragraph and Properties) and the options bar have Auto / Left-to-right / Right-to-left buttons setting the paragraph direction through type.setStyle; the options bar's choice is also the direction new type starts with (type_tool.rs direction_items; paragraph_direction_buttons_set_and_show_the_direction, options_bar_direction_sets_selected_layers_and_new_type). Still missing: visual RTL caret (the arrow keys move in logical order) |
|
||||
| TYPE-218-3 | Thai/Khmer/Indic fallback fonts; golden shaping tests for complex scripts and RTL | partial | [#218](https://github.com/storytold/photocraft/issues/218) | Fallback fonts are covered for Thai, Khmer, Lao, Myanmar (THAI_FAMILIES, KHMER_FAMILIES, LAO_FAMILIES, MYANMAR_FAMILIES in fonts.rs; #1909, #3259), the nine Indic scripts (INDIC_FAMILIES: Nirmala UI, Kohinoor and Sangam MN, Noto Sans) and Arabic and Hebrew. Still missing: per-script golden shaping tests against reference output (only smoke tests that an installed font shapes without .notdef, plus a bidi test) |
|
||||
| TYPE-218-4 | Variable font axis sliders from the font's axes; opsz follows the point size | done | [#218](https://github.com/storytold/photocraft/issues/218) | One slider per axis of the style's font in the Character section (Properties and Window > Character), setting type.setStyle variations; opsz follows the type size unless set (layout.rs variation_list); a style or weight resets the wght axis (#2985). PSD does not round-trip axis values yet |
|
||||
| TYPE-218-5 | OpenType UI: Character panel buttons, Numerals and Stylistic Sets submenus | partial | [#218](https://github.com/storytold/photocraft/issues/218) | Type > OpenType toggles (type_extra_cmds.rs:512). Character panel (and the Properties Character section) has Photoshop's eight OpenType buttons: Standard Ligatures, Contextual Alternates, Discretionary Ligatures, Swash, Stylistic Alternates, Titling Alternates, Ordinals, Fractions, pressed while the feature is on at the selection and dispatching the type.openType.* commands (type_tool.rs opentype_row; character_panel_opentype_buttons_toggle_and_show_the_feature, #3752). Still missing: the Numerals and Stylistic Sets submenus, and greying out features the font lacks |
|
||||
|
||||
+2
-2
@@ -14,9 +14,9 @@ note = "TextShape::Path (doc text.rs) with glyphs placed rigidly along the curve
|
||||
[[item]]
|
||||
id = "TYPE-218-2"
|
||||
target = "Paragraph direction (Auto/LTR/RTL) in the panel and options bar; visual RTL caret"
|
||||
status = "missing"
|
||||
status = "partial"
|
||||
issue = 218
|
||||
note = "Engine has TextDirection (text.rs:155); no UI"
|
||||
note = "Paragraph section (Window > Paragraph and Properties) and the options bar have Auto / Left-to-right / Right-to-left buttons setting the paragraph direction through type.setStyle; the options bar's choice is also the direction new type starts with (type_tool.rs direction_items; paragraph_direction_buttons_set_and_show_the_direction, options_bar_direction_sets_selected_layers_and_new_type). Still missing: visual RTL caret (the arrow keys move in logical order)"
|
||||
|
||||
[[item]]
|
||||
id = "TYPE-218-3"
|
||||
|
||||
Reference in New Issue
Block a user