mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-14 19:26:51 -04:00
* fix(ui): respect native desktop scale under Auto UI scaling (#1278) * UI scale: drop the now-unused monitor size, update docs and scorecard Merge main; Auto no longer reads the monitor size, so remove the parameter and its computation, simplify the test, and update ui-design.md and UI-217-9 to match. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
4 files changed
+20
-44
No files matched your search
@@ -341,30 +341,20 @@ fn presets_store(app: &mut PhotocraftApp) {
|
||||
|
||||
/// Resolve an absolute pixels-per-point preference without multiplying the OS DPI twice.
|
||||
/// Monitor dimensions are physical pixels, independent of window size and UI zoom.
|
||||
fn display_scale(pref: prefs::UiScale, native: Option<f32>, monitor_px: Option<egui::Vec2>) -> f32 {
|
||||
fn display_scale(pref: prefs::UiScale, native: Option<f32>) -> f32 {
|
||||
let native = native.filter(|v| v.is_finite() && *v > 0.0).unwrap_or(1.0);
|
||||
match pref {
|
||||
prefs::UiScale::Auto => {
|
||||
// Follow the system's scale, fractional ones included (125%, 150%, 175%: #1072).
|
||||
// Only when the system reports no scaling at all (100%) does a 4K display fall back
|
||||
// to 200%, since unscaled 4K controls are unreadably small (#225). Overriding a scale
|
||||
// the user picked in their desktop settings left only "too small" or "too big".
|
||||
let unscaled = (native - 1.0).abs() < 0.01;
|
||||
let is_4k = monitor_px.is_some_and(|s| s.x.is_finite() && s.y.is_finite() && s.x.min(s.y) >= 2160.0 && s.x.max(s.y) >= 3840.0);
|
||||
if unscaled && is_4k { 2.0 } else { native }
|
||||
}
|
||||
// The system's scale, fractional ones included (125%, 150%, 175%: #1072), and 100% on a
|
||||
// large 4K display run unscaled (#1278); 200% there is a fixed choice.
|
||||
prefs::UiScale::Auto => native,
|
||||
fixed => fixed.name().parse::<f32>().map_or(1.0, |pct| pct / 100.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_display_scale(app: &PhotocraftApp, ctx: &egui::Context) {
|
||||
let native = ctx.native_pixels_per_point();
|
||||
// ViewportInfo uses egui points, including the current UI zoom. Converting back to
|
||||
// physical pixels avoids oscillating between 100% and 200% on successive frames.
|
||||
// logic() can receive new viewport DPI before InputState::pixels_per_point updates.
|
||||
let native_scale = native.filter(|v| v.is_finite() && *v > 0.0).unwrap_or(1.0);
|
||||
let monitor_px = ctx.input(|i| i.viewport().monitor_size).map(|s| s * (native_scale * ctx.zoom_factor()));
|
||||
let scale = display_scale(app.session.prefs().interface.ui_scale, native, monitor_px);
|
||||
let scale = display_scale(app.session.prefs().interface.ui_scale, native);
|
||||
ctx.set_zoom_factor(scale / native_scale);
|
||||
}
|
||||
|
||||
@@ -2463,28 +2453,16 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_scale_detects_4k_and_preserves_system_scale() {
|
||||
fn auto_scale_preserves_system_scale() {
|
||||
use prefs::UiScale::Auto;
|
||||
for size in [vec2(3840.0, 2160.0), vec2(4096.0, 2160.0), vec2(2160.0, 3840.0)] {
|
||||
assert_eq!(display_scale(Auto, Some(1.0), Some(size)), 2.0);
|
||||
// A fractional system scale is the user's choice and is kept as is (#1072).
|
||||
for dpi in [1.25, 1.5, 1.75, 2.0, 2.5] {
|
||||
assert_eq!(display_scale(Auto, Some(dpi), Some(size)), dpi);
|
||||
}
|
||||
// Whatever the monitor, 4K included: an unscaled desktop stays at 100% (#1278), and a
|
||||
// fractional or below-100% system scale is the user's choice (#1072).
|
||||
for dpi in [0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0] {
|
||||
assert_eq!(display_scale(Auto, Some(dpi)), dpi);
|
||||
}
|
||||
// Below 100% is a system choice too, even on 4K.
|
||||
assert_eq!(display_scale(Auto, Some(0.75), Some(vec2(3840.0, 2160.0))), 0.75);
|
||||
for size in [vec2(1920.0, 1080.0), vec2(2560.0, 1440.0)] {
|
||||
for dpi in [1.25, 1.5, 1.75] {
|
||||
assert_eq!(display_scale(Auto, Some(dpi), Some(size)), dpi);
|
||||
}
|
||||
}
|
||||
assert_eq!(display_scale(Auto, Some(3.0), Some(vec2(3840.0, 2160.0))), 3.0);
|
||||
for size in [vec2(1920.0, 1080.0), vec2(2560.0, 1440.0), vec2(3840.0, 1080.0)] {
|
||||
assert_eq!(display_scale(Auto, Some(1.0), Some(size)), 1.0);
|
||||
}
|
||||
assert_eq!(display_scale(Auto, None, None), 1.0);
|
||||
assert_eq!(display_scale(Auto, Some(f32::NAN), Some(vec2(f32::INFINITY, 2160.0))), 1.0);
|
||||
assert_eq!(display_scale(Auto, None), 1.0);
|
||||
assert_eq!(display_scale(Auto, Some(f32::NAN)), 1.0);
|
||||
assert_eq!(display_scale(Auto, Some(-2.0)), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2507,13 +2485,13 @@ mod tests {
|
||||
assert!((ctx.pixels_per_point() - expected).abs() < 1e-4);
|
||||
};
|
||||
for _ in 0..4 {
|
||||
step(vec2(3840.0, 2160.0), 1.0, 2.0);
|
||||
step(vec2(3840.0, 2160.0), 1.0, 1.0);
|
||||
}
|
||||
step(vec2(1920.0, 1080.0), 1.0, 1.0);
|
||||
step(vec2(3840.0, 2160.0), 1.5, 1.5);
|
||||
step(vec2(3840.0, 2160.0), 1.25, 1.25);
|
||||
step(vec2(2560.0, 1440.0), 1.75, 1.75);
|
||||
step(vec2(3840.0, 2160.0), 1.0, 2.0);
|
||||
step(vec2(3840.0, 2160.0), 1.0, 1.0);
|
||||
}
|
||||
for (pref, expected) in [("200", 2.0), ("125", 1.25), ("150", 1.5), ("100", 1.0), ("95", 0.95), ("90", 0.9), ("85", 0.85), ("80", 0.8), ("auto", 1.5)] {
|
||||
app.run("prefs.set", json!({"values": {"interface.uiScale": pref}})).unwrap();
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ Unread: `general.color_picker`, `general.animated_zoom`, `general.zoom_resizes_w
|
||||
| UI-217-6 | Document tabs: overflow list, context menu (Close/Others/All/Reveal), drag to reorder | done | [#217](https://github.com/storytold/photocraft/issues/217) | canvas.rs: Close/Others/All menu with unsaved prompt; Reveal reuses file.revealInFinder (engine layer_menu_cmds::reveal); the » chevron (tab_strip) lists overflow; dragging a tab reorders via document.move |
|
||||
| UI-217-7 | Command palette shows recently run commands on an empty query | done | [#217](https://github.com/storytold/photocraft/issues/217) | palette.rs: note_recent from menus::invoke keeps the last 8 commands (dedup, newest first); an empty query lists them via recent_hits; the_palette_records_and_replays_recent_commands_on_an_empty_query covers record -> empty query -> replay |
|
||||
| UI-217-8 | Tool-specific canvas right-click menu; right-click spelling suggestions in text fields | partial | [#217](https://github.com/storytold/photocraft/issues/217) | paint_mouse.rs brush picker and canvas_tool_menu.rs selection actions; text_field_menu.rs gives every text field its right-click Undo/Cut/Copy/Paste/Delete/Select All menu (#2921); other tools missing; spelling suggestions (edit.checkSpelling suggest) not in the text-field menu yet |
|
||||
| UI-217-9 | UI scale presets 100/115/125/150/200 % through zoom_factor, remembered | done | [#217](https://github.com/storytold/photocraft/issues/217) | prefs::UiScale offers Auto plus 75 to 300 percent (13 presets, more than the five this row lists); prefs_ui.rs display_scale resolves it (Auto follows the system scale and only jumps to 200 percent on an unscaled 4K display, #1072/#225) and sync_display_scale applies it with ctx.set_zoom_factor. It is a preference, so it is remembered. The zoom_factor assert in input_tests.rs is about canvas zoom not scaling the interface, not about this setting |
|
||||
| UI-217-9 | UI scale presets 100/115/125/150/200 % through zoom_factor, remembered | done | [#217](https://github.com/storytold/photocraft/issues/217) | prefs::UiScale offers Auto plus 75 to 300 percent (13 presets, more than the five this row lists); prefs_ui.rs display_scale resolves it (Auto follows the system scale, fractional and unscaled 4K included, #1072/#1278) and sync_display_scale applies it with ctx.set_zoom_factor. It is a preference, so it is remembered. The zoom_factor assert in input_tests.rs is about canvas zoom not scaling the interface, not about this setting |
|
||||
| UI-217-10 | Appearance: animation toggle, canvas pattern toggle per theme, start-screen recents | missing | [#217](https://github.com/storytold/photocraft/issues/217) | No such prefs; start screen (canvas.rs:825) has no recents |
|
||||
| UI-217-11 | Soft dark and soft light themes with picker previews; outlined knobs in light themes | missing | [#217](https://github.com/storytold/photocraft/issues/217) | theme.rs ThemeKind has twelve themes (the four Pro brightness levels, Studio and Studio Light, Classic, Adwaita and Adwaita Dark, Solarized Dark, Breeze Light and Breeze Dark); no Soft dark or Soft light, and no outlined knobs in the light themes |
|
||||
| UI-217-12 | Save custom New Document presets; a real Recent category | partial | [#217](https://github.com/storytold/photocraft/issues/217) | document_preset_cmds.rs and new_doc_ui.rs: named presets save/select/delete through preferences; Recent remains a static list plus Clipboard |
|
||||
|
||||
+3
-5
@@ -164,11 +164,9 @@ change the current window's decorations; the next launch reads the saved choice.
|
||||
|
||||
Edit → Preferences → Interface → UI Scale applies immediately. Auto follows the operating
|
||||
system's display scale, including fractional scales such as 125%, 150% and 175% (on Linux,
|
||||
Wayland's fractional scale or X11's `Xft.dpi`). Only when the system reports no scaling (100%) on
|
||||
a 4K or larger monitor does Auto use 200%, so text and controls remain readable. Detection uses the current monitor,
|
||||
including portrait displays, and updates when the window moves between monitors. If monitor
|
||||
size is unavailable, Auto follows system DPI. The fixed choices (75% to 300%) set an absolute UI
|
||||
scale, allowing large 4K displays to use smaller controls when desired. Canvas zoom shortcuts
|
||||
Wayland's fractional scale or X11's `Xft.dpi`), and updates when the window moves between
|
||||
monitors. A 4K display the system runs at 100% stays at 100% (#1278). The fixed choices (75% to
|
||||
300%) set an absolute UI scale, for example 200% on an unscaled 4K display. Canvas zoom shortcuts
|
||||
continue to control the document independently of UI scaling.
|
||||
|
||||
Interface → UI Font Size changes interface text independently of UI Scale and canvas zoom.
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ id = "UI-217-9"
|
||||
target = "UI scale presets 100/115/125/150/200 % through zoom_factor, remembered"
|
||||
status = "done"
|
||||
issue = 217
|
||||
note = "prefs::UiScale offers Auto plus 75 to 300 percent (13 presets, more than the five this row lists); prefs_ui.rs display_scale resolves it (Auto follows the system scale and only jumps to 200 percent on an unscaled 4K display, #1072/#225) and sync_display_scale applies it with ctx.set_zoom_factor. It is a preference, so it is remembered. The zoom_factor assert in input_tests.rs is about canvas zoom not scaling the interface, not about this setting"
|
||||
note = "prefs::UiScale offers Auto plus 75 to 300 percent (13 presets, more than the five this row lists); prefs_ui.rs display_scale resolves it (Auto follows the system scale, fractional and unscaled 4K included, #1072/#1278) and sync_display_scale applies it with ctx.set_zoom_factor. It is a preference, so it is remembered. The zoom_factor assert in input_tests.rs is about canvas zoom not scaling the interface, not about this setting"
|
||||
|
||||
[[item]]
|
||||
id = "UI-217-10"
|
||||
|
||||
Reference in new issue
Block a user