* Preferences: wire Tools › Show Transformation Values (#204) The size and angle readouts beside the pointer (marquee W/H, Crop frame W/H and angle, Crop Straighten angle) now go through canvas::draw_value_readout, which draws them only while tools.showTransformationValues is on, as in Photoshop. The brush HUD isn't a transformation value and keeps drawing. The preference leaves HIDDEN_UNTIL_IMPLEMENTED, so Preferences › Tools shows it (its label is already translated). Settings that do nothing: 43 → 42 of 149. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Contributors: add my display name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
parent
55a617cd66
commit
026432815c
|
|
@ -101,3 +101,5 @@ sync_github_name = true
|
|||
[people.TheTeslak]
|
||||
sync_github_name = true
|
||||
|
||||
[people.timothydaly1]
|
||||
display_name = "SyntaxError"
|
||||
|
|
|
|||
|
|
@ -317,6 +317,8 @@ pub struct Tools {
|
|||
pub vary_round_brush_hardness_on_hud: bool,
|
||||
/// Round snapped vector and transform coordinates to whole pixels.
|
||||
pub snap_vector_tools_and_transforms_to_pixel_grid: bool,
|
||||
/// The size and angle readouts beside the pointer while a marquee or crop frame is drawn,
|
||||
/// resized or turned (off: none).
|
||||
pub show_transformation_values: bool,
|
||||
/// Move tool with Auto-Select: outline the layer a click would pick under the pointer.
|
||||
pub show_highlight_on_rollover: bool,
|
||||
|
|
@ -992,7 +994,6 @@ pub const HIDDEN_UNTIL_IMPLEMENTED: &[&str] = &[
|
|||
"workspace.enableFloatingDocumentWindowDocking",
|
||||
"workspace.enableNarrowOptionsBar",
|
||||
"tools.enableFlickPanning",
|
||||
"tools.showTransformationValues",
|
||||
"fileHandling.imagePreviews",
|
||||
"fileHandling.saveInBackground",
|
||||
"fileHandling.ignoreExifProfileTag",
|
||||
|
|
|
|||
|
|
@ -317,8 +317,17 @@ pub fn readout_in(ur: &photocraft_engine::prefs::UnitsAndRulers, dpi: f64, exten
|
|||
|
||||
/// Draw the marquee size readout below-right of the cursor (kept on screen), like Photoshop's:
|
||||
/// two rows, `W:` / `H:` labels on the left and the values right-aligned.
|
||||
fn draw_marquee_readout(ctx: &egui::Context, cursor: Pos2, values: [String; 2]) {
|
||||
draw_readout(ctx, "marquee-readout", cursor, ["W:", "H:"], values);
|
||||
fn draw_marquee_readout(app: &PhotocraftApp, ctx: &egui::Context, cursor: Pos2, values: [String; 2]) {
|
||||
draw_value_readout(app, ctx, "marquee-readout", cursor, ["W:", "H:"], values);
|
||||
}
|
||||
|
||||
/// A transformation-values readout beside the pointer (a marquee's or the crop frame's size, an
|
||||
/// angle), unless Preferences › Tools › Show Transformation Values is off, as in Photoshop (#204).
|
||||
/// The brush HUD isn't one: Show Transformation Values doesn't hide it, so it calls [`draw_readout`].
|
||||
pub(crate) fn draw_value_readout<const N: usize>(app: &PhotocraftApp, ctx: &egui::Context, id: &str, cursor: Pos2, labels: [&str; N], values: [String; N]) {
|
||||
if app.session.prefs().tools.show_transformation_values {
|
||||
draw_readout(ctx, id, cursor, labels, values);
|
||||
}
|
||||
}
|
||||
|
||||
/// A readout of `N` rows beside the pointer (labels left, values right-aligned), above everything.
|
||||
|
|
@ -3822,10 +3831,10 @@ fn draw_tool_state(app: &PhotocraftApp, painter: &egui::Painter, xf: &ViewXform,
|
|||
}
|
||||
// The frame's angle beside the pointer while it turns (#1792).
|
||||
if let (Some(a), Some(h)) = (crate::crop_ui::turning(app), hover) {
|
||||
draw_readout(painter.ctx(), "crop-angle-readout", h, ["Angle:"], [format!("{a:.1}°")]);
|
||||
draw_value_readout(app, painter.ctx(), "crop-angle-readout", h, ["Angle:"], [format!("{a:.1}°")]);
|
||||
} else if let (Some([w, h]), Some(at)) = (crate::crop_ui::sizing(app), hover) {
|
||||
// Its W × H while it is drawn or resized (#1919), like the marquee's.
|
||||
draw_marquee_readout(painter.ctx(), at, size_readout(app, [0.0, 0.0, w, h]));
|
||||
draw_marquee_readout(app, painter.ctx(), at, size_readout(app, [0.0, 0.0, w, h]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3929,7 +3938,7 @@ fn draw_drag_preview(app: &mut PhotocraftApp, painter: &egui::Painter, xf: &View
|
|||
let last = d.points.last().map(|p| [p[0], p[1]]).unwrap_or(d.start);
|
||||
let marquee = marquee_preview_px(&app.ui.tool_options, d, app.point_zoom());
|
||||
if let Some(r) = marquee {
|
||||
draw_marquee_readout(painter.ctx(), xf.to_screen(last[0] as f32, last[1] as f32), size_readout(app, r));
|
||||
draw_marquee_readout(app, painter.ctx(), xf.to_screen(last[0] as f32, last[1] as f32), size_readout(app, r));
|
||||
}
|
||||
match d.tool {
|
||||
// The canvas shows the live stroke itself (`LiveStroke`); a stroke that couldn't start one
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ pub fn draw(app: &PhotocraftApp, painter: &egui::Painter, to_screen: impl Fn([f6
|
|||
let (dx, dy) = (b[0] - a[0], b[1] - a[1]);
|
||||
let deg = (-dy).atan2(dx).to_degrees();
|
||||
if let Some(h) = hover.filter(|_| deg.is_finite() && (dx != 0.0 || dy != 0.0)) {
|
||||
crate::canvas::draw_readout(painter.ctx(), "crop-straighten-readout", h, ["Angle:"], [format!("{deg:.1}°")]);
|
||||
crate::canvas::draw_value_readout(app, painter.ctx(), "crop-straighten-readout", h, ["Angle:"], [format!("{deg:.1}°")]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,6 +147,48 @@ fn shift_during_the_drag_draws_a_square_and_the_readout_follows() {
|
|||
assert!(h.query_by_label("W:").is_none() && h.query_by_label_contains(" px").is_none(), "the readout goes away with the drag");
|
||||
}
|
||||
|
||||
/// #204: Preferences › Tools › Show Transformation Values off hides the readout, not the marquee.
|
||||
#[test]
|
||||
fn show_transformation_values_off_hides_the_readout() {
|
||||
for shown in [false, true] {
|
||||
let mut h = harness(Tool::RectMarquee);
|
||||
h.state_mut().run("prefs.set", json!({"path": "tools.showTransformationValues", "value": shown})).unwrap();
|
||||
press_at(&mut h, 50.0, 50.0, Modifiers::NONE);
|
||||
move_to(&mut h, 150.0, 90.0);
|
||||
assert_eq!(readout(&h, 100, 40), shown, "shown: {shown}");
|
||||
if !shown {
|
||||
assert!(h.query_by_label("W:").is_none() && h.query_by_label_contains(" px").is_none());
|
||||
}
|
||||
release_at(&mut h, 150.0, 90.0, Modifiers::NONE);
|
||||
assert_eq!(selection(&h), Rect::new(50, 50, 150, 90), "the marquee still selects");
|
||||
}
|
||||
}
|
||||
|
||||
/// #204: the Free Transform readout (#1767) follows Show Transformation Values too.
|
||||
#[test]
|
||||
fn show_transformation_values_off_hides_the_free_transform_readout() {
|
||||
use crate::canvas::{ToolEvent, tool_event};
|
||||
for shown in [false, true] {
|
||||
let mut h = harness(Tool::Move);
|
||||
let app = h.state_mut();
|
||||
app.run("prefs.set", json!({"path": "tools.showTransformationValues", "value": shown})).unwrap();
|
||||
app.run("layer.new.layer", json!({})).unwrap();
|
||||
app.run("select.all", json!({})).unwrap();
|
||||
app.run("edit.fill", json!({"color": "#3366cc"})).unwrap();
|
||||
app.run("select.deselect", json!({})).unwrap();
|
||||
app.ui.extras.snap = false;
|
||||
crate::menus::invoke(app, &egui::Context::default(), "edit.freeTransform", json!({})).unwrap();
|
||||
// Move the box by dragging inside it (away from the centre pivot), then hover there.
|
||||
tool_event(app, ToolEvent::Down { x: 100.0, y: 100.0, pressure: 1.0 }, Modifiers::NONE);
|
||||
tool_event(app, ToolEvent::Move { x: 130.0, y: 120.0, pressure: 1.0 }, Modifiers::NONE);
|
||||
assert!(crate::transform_tool::live_readout(h.state()).is_some(), "a live readout to show");
|
||||
let at = screen(&h, 130.0, 120.0);
|
||||
h.event(egui::Event::PointerMoved(at));
|
||||
h.run_steps(2);
|
||||
assert_eq!(h.query_by_label("ΔX:").is_some(), shown, "shown: {shown}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_draws_from_the_centre_and_shift_alt_a_centred_circle() {
|
||||
let mut h = harness(Tool::RectMarquee);
|
||||
|
|
|
|||
|
|
@ -1699,7 +1699,8 @@ pub fn draw_overlay(app: &PhotocraftApp, painter: &egui::Painter, xf: &ViewXform
|
|||
let toward = xf.to_screen((p[0] + k[0] * 16.0) as f32, (p[1] + k[1] * 16.0) as f32) - at;
|
||||
crate::crop_ui::draw_turn_cursor(painter.ctx(), at, toward);
|
||||
}
|
||||
if let Some(r) = live_readout(app) {
|
||||
// Preferences › Tools › Show Transformation Values off hides it, as in Photoshop (#204).
|
||||
if let Some(r) = live_readout(app).filter(|_| app.session.prefs().tools.show_transformation_values) {
|
||||
draw_live_readout(painter.ctx(), at, r);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Generated by `cargo xtask scorecard` from `perf/budgets.toml`, `perf/baseline.js
|
|||
| [Performance](#performance) | 3 | 22 | 22 | 56 scenarios: 3 within budget, 22 over budget or failing, 9 tracked only, 17 not run, 5 not measurable yet (baseline 2026-10-05) |
|
||||
| [File compatibility](#file-compatibility) | 6 | 11 | 10 | io corpus: oracle ≥ 146/170, round trip ≥ 169/170; psd-tools corpus: oracle ≥ 236/309, round trip ≥ 308/309; photoshop oracles: oracle ≥ 133/256, round trip ≥ 258/256 |
|
||||
| [Tools](#tools) | 25 | 16 | 2 | |
|
||||
| [Workspace and UI](#workspace-and-ui) | 8 | 8 | 7 | settings that do nothing: 36 of 153 (target 0) |
|
||||
| [Workspace and UI](#workspace-and-ui) | 8 | 8 | 7 | settings that do nothing: 35 of 153 (target 0) |
|
||||
| [Type](#type) | 1 | 4 | 4 | |
|
||||
| [Automation](#automation) | 3 | 2 | 6 | 630 of 630 menu items live (100.0%) |
|
||||
| [Reliability](#reliability) | 5 | 2 | 0 | never-crash lints on 28 of 28 crates; panic_hunt present |
|
||||
|
|
@ -176,9 +176,9 @@ Corpus floors (`crates/io/tests/corpus.rs`): the enforced minimums; the corpus t
|
|||
|
||||
## Workspace and UI
|
||||
|
||||
**Settings that do nothing: 36 of 153** (target 0, #204). Counted by the prefs audit (`xtask/src/scorecard.rs`): a `Preferences` field counts as read when code outside the prefs model (`prefs.rs` except its `impl Session` blocks; tests excluded; the schema-driven dialog never names a field) accesses `section.field`, or `.section` and `.field` in the same file, or calls a section method that reads `self.field`. It is a heuristic lower bound: a generic name can look read when it isn't.
|
||||
**Settings that do nothing: 35 of 153** (target 0, #204). Counted by the prefs audit (`xtask/src/scorecard.rs`): a `Preferences` field counts as read when code outside the prefs model (`prefs.rs` except its `impl Session` blocks; tests excluded; the schema-driven dialog never names a field) accesses `section.field`, or `.section` and `.field` in the same file, or calls a section method that reads `self.field`. It is a heuristic lower bound: a generic name can look read when it isn't.
|
||||
|
||||
Unread: `general.color_picker`, `general.animated_zoom`, `general.zoom_resizes_windows`, `workspace.auto_collapse_icon_panels`, `workspace.auto_show_hidden_panels`, `workspace.enable_floating_document_window_docking`, `workspace.enable_narrow_options_bar`, `tools.enable_flick_panning`, `tools.show_transformation_values`, `file_handling.image_previews`, `file_handling.save_in_background`, `file_handling.ignore_exif_profile_tag`, `file_handling.maximize_psd_compatibility`, `performance.cache_levels`, `performance.legacy_compositing`, `scratch_disks.disks`, `units_and_rulers.column_width`, `units_and_rulers.gutter`, `units_and_rulers.print_resolution`, `plug_ins.show_extension_panels`, `plug_ins.allow_scripts_to_connect`, `plug_ins.generator_enabled`, `type_.missing_glyph_protection`, `type_.show_font_names_in_english`, `type_.text_engine`, `type_.recent_fonts`, `enhanced_controls.scrubby_slider_acceleration`, `enhanced_controls.touch_gestures`, `raw_defaults.color_space`, `raw_defaults.bit_depth`, `raw_defaults.resolution`, `raw_defaults.sharpen_for`, `raw_defaults.open_as_smart_object`, `raw_defaults.apply_auto_tone`, `integrations.allow_agent_control`, `integrations.control_port`.
|
||||
Unread: `general.color_picker`, `general.animated_zoom`, `general.zoom_resizes_windows`, `workspace.auto_collapse_icon_panels`, `workspace.auto_show_hidden_panels`, `workspace.enable_floating_document_window_docking`, `workspace.enable_narrow_options_bar`, `tools.enable_flick_panning`, `file_handling.image_previews`, `file_handling.save_in_background`, `file_handling.ignore_exif_profile_tag`, `file_handling.maximize_psd_compatibility`, `performance.cache_levels`, `performance.legacy_compositing`, `scratch_disks.disks`, `units_and_rulers.column_width`, `units_and_rulers.gutter`, `units_and_rulers.print_resolution`, `plug_ins.show_extension_panels`, `plug_ins.allow_scripts_to_connect`, `plug_ins.generator_enabled`, `type_.missing_glyph_protection`, `type_.show_font_names_in_english`, `type_.text_engine`, `type_.recent_fonts`, `enhanced_controls.scrubby_slider_acceleration`, `enhanced_controls.touch_gestures`, `raw_defaults.color_space`, `raw_defaults.bit_depth`, `raw_defaults.resolution`, `raw_defaults.sharpen_for`, `raw_defaults.open_as_smart_object`, `raw_defaults.apply_auto_tone`, `integrations.allow_agent_control`, `integrations.control_port`.
|
||||
|
||||
| Id | Target | Status | Issue | Note |
|
||||
|---|---|---|---|---|
|
||||
|
|
|
|||
Loading…
Reference in New Issue