From a76d3295ba2b2d22d94f87fe2a2d3a5205cdc041 Mon Sep 17 00:00:00 2001 From: itsF4LCON <107556180+itsF4LCON@users.noreply.github.com> Date: Fri, 16 Oct 2026 00:38:48 +0200 Subject: [PATCH] Type: the text caret shows on a white background (part of #6085) In the Photoshop themes the caret was a 1.5 pt white line with a 0.75 pt black hairline inside, so on a white background only a faint grey line showed. It is now a solid 1 pt black line inside a 3 pt white halo, readable on any background, in every theme. --- crates/ui-egui/src/type_tool.rs | 11 +++++-- crates/ui-egui/src/type_tool_tests.rs | 43 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/type_tool.rs b/crates/ui-egui/src/type_tool.rs index d73bb2e2c4..d85964ed62 100644 --- a/crates/ui-egui/src/type_tool.rs +++ b/crates/ui-egui/src/type_tool.rs @@ -1088,13 +1088,18 @@ pub fn draw_overlay(app: &mut PhotocraftApp, painter: &egui::Painter, xf: &ViewX if ed.dragging || (time * 1000.0 / 530.0) as i64 % 2 == 0 { let (x, top, bot) = l.caret(a); let (x, top, bot) = if l.lines.is_empty() { (0.0, -(12.0 * l.px_per_pt.max(1.0)), 3.0) } else { (x, top, bot) }; - let c = if crate::theme::Tokens::get(painter.ctx()).pro { Color32::WHITE } else { Color32::BLACK }; - painter.line_segment([scr(x, top), scr(x, bot)], Stroke::new(1.5, c)); - painter.line_segment([scr(x, top), scr(x, bot)], Stroke::new(0.75, Color32::BLACK)); + for stroke in CARET_STROKES { + painter.line_segment([scr(x, top), scr(x, bot)], stroke); + } } } } +/// The text caret, drawn in order: a white halo, then a solid black line inside it, so it reads on +/// any background (#6085). The black line used to be a 0.75 pt hairline inside a white one, which +/// on a white background left only a faint grey line. +const CARET_STROKES: [Stroke; 2] = [Stroke { width: 3.0, color: Color32::WHITE }, Stroke { width: 1.0, color: Color32::BLACK }]; + /// The Type Mask tools while typing: Quick Mask's colour over the canvas with the glyphs clear, /// as Photoshop shows the selection-to-be. The glyphs come from the hidden working layer's pixels. fn draw_mask_overlay(app: &PhotocraftApp, painter: &egui::Painter, xf: &ViewXform, id: LayerId) { diff --git a/crates/ui-egui/src/type_tool_tests.rs b/crates/ui-egui/src/type_tool_tests.rs index c7b54c8871..d27fdbcdac 100644 --- a/crates/ui-egui/src/type_tool_tests.rs +++ b/crates/ui-egui/src/type_tool_tests.rs @@ -2065,3 +2065,46 @@ fn type_mask_shift_adds_and_alt_subtracts_without_a_layer_row() { assert!(at(&app, stem.0, stem.1) < 0.01, "⌥ subtracts the glyphs"); assert!(at(&app, 1050, 150) > 0.99, "and keeps the rest"); } + +/// #6085: the text caret is a solid black line inside a white halo, so it shows on a white +/// background (the black used to be a 0.75 pt hairline inside a white line) and on a dark one. +#[test] +fn the_caret_reads_on_white_and_on_black() { + use crate::canvas::{ToolEvent, tool_event}; + let mut app = new_app(); + app.ui.tool = crate::state::Tool::Type; + tool_event(&mut app, ToolEvent::Down { x: 100.0, y: 100.0, pressure: 1.0 }, Modifiers::NONE); + tool_event(&mut app, ToolEvent::Up { x: 100.0, y: 100.0 }, Modifiers::NONE); + // A caret, not the placeholder's selection; solid, whatever the blink (as while dragging). + let ed = app.ui.text_edit.as_mut().expect("editing"); + ed.anchor = ed.caret; + ed.dragging = true; + let xf = ViewXform { + rect: egui::Rect::from_min_size(Pos2::ZERO, vec2(400.0, 300.0)), + zoom: 1.0, + center: [100.0, 100.0], + flip: false, + rotation: 0.0, + aspect: 1.0, + }; + let mut h = + Harness::builder().with_size(vec2(400.0, 300.0)).build_ui_state(move |ui, app: &mut PhotocraftApp| super::draw_overlay(app, ui.painter(), &xf), app); + PhotocraftApp::setup_context(&h.ctx, crate::theme::ThemeKind::ProMedium); + h.run(); + // Vertical line segments are the caret (the frame and underline run sideways). + let caret: Vec<(f32, egui::Color32)> = h + .output() + .shapes + .iter() + .filter_map(|s| match &s.shape { + egui::Shape::LineSegment { points: [a, b], stroke } if (a.x - b.x).abs() < 0.01 && (a.y - b.y).abs() > 1.0 => Some((stroke.width, stroke.color)), + _ => None, + }) + .collect(); + assert_eq!(caret.len(), 2, "a halo and a line: {caret:?}"); + let ((halo_w, halo), (line_w, line)) = (caret[0], caret[1]); + assert_eq!(line, egui::Color32::BLACK); + assert!(line_w >= 1.0, "a solid black line, not a hairline: {line_w}"); + assert_eq!(halo, egui::Color32::WHITE); + assert!(halo_w >= line_w + 2.0, "at least 1 pt of white on each side: {halo_w}"); +}