Type: keep text selection highlight visible while Command/Ctrl is held (#5542)

* Type: keep text selection highlight visible while Command/Ctrl is held

* Type: match Photoshop 1px dark frame and handle styling for type transform

* Merge main; keep the transform box and underline colours from the theme tokens

The selection-highlight fix stands on its own; the hard-coded box and
underline colours and the removed reference point were unrelated to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: safvanedge <241307593+safvanedge@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
safvanedge
2026-10-14 22:46:46 -04:00
committed by GitHub
co-authored by safvanedge Brandon Thomas Claude Opus 5.5
parent 7efebfa050
commit fd28da56da
2 changed files with 27 additions and 10 deletions
+10 -10
View File
@@ -1014,20 +1014,20 @@ pub fn draw_overlay(app: &mut PhotocraftApp, painter: &egui::Painter, xf: &ViewX
}
// Frame: paragraph text shows its box with handles; point text an underline per line.
let mods = crate::workspace_ui::sticky_mods(app, painter.ctx().input(|i| i.modifiers));
if crate::type_transform::draw(app, painter, xf, mods) {
return;
}
let has_transform = crate::type_transform::draw(app, painter, xf, mods);
let shape = app.session.active().and_then(|s| text_layer(&s.doc, id).map(|t| t.shape.clone()));
let frame = Stroke::new(1.0, t.accent);
match shape {
Some(photocraft_doc::text::TextShape::Box { x, y, width, height }) => {
let c = [scr_t(x, y), scr_t(x + width, y), scr_t(x + width, y + height), scr_t(x, y + height)];
painter.add(egui::Shape::closed_line(c.to_vec(), frame));
let mids = [c[0].lerp(c[1], 0.5), c[1].lerp(c[2], 0.5), c[2].lerp(c[3], 0.5), c[3].lerp(c[0], 0.5)];
for p in c.iter().chain(mids.iter()) {
let r = egui::Rect::from_center_size(*p, egui::vec2(7.0, 7.0));
painter.rect_filled(r, 0.0, Color32::WHITE);
painter.rect_stroke(r, 0.0, frame, egui::StrokeKind::Inside);
if !has_transform {
let c = [scr_t(x, y), scr_t(x + width, y), scr_t(x + width, y + height), scr_t(x, y + height)];
painter.add(egui::Shape::closed_line(c.to_vec(), frame));
let mids = [c[0].lerp(c[1], 0.5), c[1].lerp(c[2], 0.5), c[2].lerp(c[3], 0.5), c[3].lerp(c[0], 0.5)];
for p in c.iter().chain(mids.iter()) {
let r = egui::Rect::from_center_size(*p, egui::vec2(7.0, 7.0));
painter.rect_filled(r, 0.0, Color32::WHITE);
painter.rect_stroke(r, 0.0, frame, egui::StrokeKind::Inside);
}
}
}
_ => {
+17
View File
@@ -779,6 +779,23 @@ fn temporary_type_transform_visibility_and_cursors() {
assert!(crate::type_transform::visible(h.state(), sticky));
}
#[test]
fn type_selection_highlight_visible_with_command_held() {
let (app, _id) = transform_app(false, false, Affine::IDENTITY);
let mut h = harness(1.0, app);
if let Some(ed) = h.state_mut().ui.text_edit.as_mut() {
ed.anchor = 0;
ed.caret = 5;
}
hold(&mut h, type_command());
h.run();
let accent = crate::theme::Tokens::get(&h.ctx).accent;
let expected_fill = egui::Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 110);
let has_selection_highlight =
h.output().shapes.iter().any(|clipped| if let egui::Shape::Path(path) = &clipped.shape { path.fill == expected_fill && path.closed } else { false });
assert!(has_selection_highlight, "selection highlight must remain visible while Ctrl/Cmd is held");
}
/// Real canvas input: layer-local scaling stays correct across orientation, zoom, HiDPI and flip.
#[test]
fn temporary_type_transform_scales_without_reflow_or_leaving_type() {