From 361e1e41081fb20f9515a824b974b8236190a731 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Sat, 7 Nov 2026 20:22:56 +0100 Subject: [PATCH] Interface size in finer percent steps; fixed sizes no longer double the interface Co-Authored-By: Claude Opus 5.5 --- src/main.rs | 10 +++++----- src/menu.rs | 12 +++++------- src/settings.rs | 42 ++++++++++++++++++++++++++++++++++++++++-- src/ui.rs | 12 ++---------- 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index f42ae6b..67bb25c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -475,7 +475,6 @@ fn next_seed(seed: u32) -> u32 { fn apply_settings(s: &Settings, r3d: &mut R3d) { i18n::set_language(&s.language); - ui::set_ui_factor(s.ui_factor); r3d.outlines = s.outlines; r3d.set_wall_detail(s.wall_subdivisions()); r3d.max_lights = s.max_lights as usize; @@ -993,10 +992,11 @@ async fn main() { let need: u32 = 900; // measured on the shorter side: upright (portrait) the width gets those pixels and the // height grows with the screen, so the world and the menus keep their size. The setting - // "interface size" picks the interface pixels per world pixel: large 2, medium 3, small 4 - let short = match app.settings.ui_factor { - k @ 2..=4 => rows * k as u32, - _ => rows * need.div_ceil(rows), + // "interface size" picks the interface rows in percent of the standard (100% = 1080 rows); + // on a screen with fewer rows the interface stays at the screen's rows + let short = match app.settings.ui_size { + 0 => rows * need.div_ceil(rows), + p => settings::ui_size_rows(p).min((screen_width().min(screen_height()) * screen_dpi_scale()).max(1.0) as u32), }; let (w, h) = (screen_width().max(1.0), screen_height().max(1.0)); let vrows = if h > w { (short as f32 * h / w).round() as u32 } else { short }; diff --git a/src/menu.rs b/src/menu.rs index c30b5cb..560141d 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -141,12 +141,10 @@ fn item_value(it: Item, s: &Settings) -> String { _ => "val.medium", }), Item::Lights => s.max_lights.to_string(), - Item::UiScale => tr(match s.ui_factor { - 2 => "val.large", - 3 => "val.medium", - 4 => "val.small", - _ => "val.auto_plain", - }), + Item::UiScale => match s.ui_size { + 0 => tr("val.auto_plain"), + p => format!("{p}%"), + }, Item::DebugFps => onoff(s.debug_fps), Item::DebugFrameTime => onoff(s.debug_frame_time), Item::DebugPing => onoff(s.debug_ping), @@ -201,7 +199,7 @@ fn item_change(it: Item, s: &mut Settings, d: i32) { Item::Outlines => s.outlines = !s.outlines, Item::WallDetail => s.wall_detail = cycle(&[1u8, 2, 3], s.wall_detail, d), Item::Lights => s.max_lights = cycle(&[8u8, 16, 24], s.max_lights, d), - Item::UiScale => s.ui_factor = cycle(&[0u8, 2, 3, 4], if [0, 2, 3, 4].contains(&s.ui_factor) { s.ui_factor } else { 0 }, d), + Item::UiScale => s.ui_size = cycle(&crate::settings::UI_SIZES, s.ui_size, d), Item::RenderScale => s.pixel_rows = cycle(&[270u16, 360, 480], if [270, 360, 480].contains(&s.pixel_rows) { s.pixel_rows } else { 360 }, d), Item::DebugFps => s.debug_fps = !s.debug_fps, Item::DebugFrameTime => s.debug_frame_time = !s.debug_frame_time, diff --git a/src/settings.rs b/src/settings.rs index 8fec9ed..b3341ec 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -148,7 +148,8 @@ pub struct Settings { pub outlines: bool, pub wall_detail: u8, // 1 low, 2 medium, 3 high pub max_lights: u8, // 8, 16 or 24 - pub ui_factor: u8, // interface size: 0 = automatic, 2 large, 3 medium, 4 small (interface pixels per world pixel) + pub ui_factor: u8, // old interface size (2 large, 3 medium, 4 small; only read to carry old settings over) + pub ui_size: u16, // interface size in percent of the standard (100 = 1080 interface rows), 0 = automatic pub render_scale: u8, // old pixel-look divisor (no longer used, kept for old settings files) pub pixel_rows: u16, // pixel look: rows of the world on every screen (270, 360, 480) pub touch_ui: u8, // touch interface: 0 = automatic (after the first touch), 1 = on, 2 = off @@ -189,6 +190,7 @@ impl Default for Settings { wall_detail: 2, max_lights: 24, ui_factor: 0, + ui_size: 0, render_scale: 1, pixel_rows: 360, touch_ui: 0, @@ -244,10 +246,32 @@ impl Settings { } pub fn from_json(s: &str) -> Option { - serde_json::from_str(s).ok() + let mut st: Settings = serde_json::from_str(s).ok()?; + // old interface sizes (pixels per world pixel) become percentages + if st.ui_size == 0 { + st.ui_size = match st.ui_factor { + 2 => 150, + 3 => 100, + 4 => 75, + _ => 0, + }; + } + st.ui_factor = 0; + if st.ui_size != 0 && !UI_SIZES.contains(&st.ui_size) { + st.ui_size = 0; + } + Some(st) } } +// the interface sizes of the settings menu in percent (0 = automatic), from small to large +pub const UI_SIZES: [u16; 9] = [0, 75, 85, 100, 115, 130, 150, 175, 200]; + +// interface rows of the virtual screen for a size in percent (100% = 1080 rows) +pub fn ui_size_rows(percent: u16) -> u32 { + (1080.0 * 100.0 / percent.max(1) as f32).round() as u32 +} + #[cfg(test)] mod tests { use super::*; @@ -273,4 +297,18 @@ mod tests { assert!(!old.debug_trigger_zones); assert!(!old.debug_command_markers); } + + #[test] + fn interface_size_percent() { + let old = Settings::from_json(r#"{"ui_factor":2}"#).unwrap(); + assert_eq!(old.ui_size, 150); + assert_eq!(old.ui_factor, 0); + let mut s = Settings::default(); + s.ui_size = 115; + assert_eq!(Settings::from_json(&s.to_json()).unwrap().ui_size, 115); + assert_eq!(Settings::from_json(r#"{"ui_size":123}"#).unwrap().ui_size, 0); + assert_eq!(ui_size_rows(100), 1080); + assert_eq!(ui_size_rows(150), 720); + assert!(ui_size_rows(200) < ui_size_rows(175)); + } } diff --git a/src/ui.rs b/src/ui.rs index 07de82e..cedbfeb 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -15,9 +15,9 @@ use macroquad::prelude::*; // the frame (see begin_ui, end_frame) - on every device the same pixel grid. Small screens // draw in device pixels. Every size is an integer multiple // of the font's 6x8 pixel glyphs: small text = 1*F, normal text = 2*F, titles = 4*F, where F -// is the interface factor (automatic from the resolution or fixed in the settings). +// is the interface factor (1 on the virtual screen, whose rows follow the "interface size" +// setting; automatic from the resolution in device pixels). thread_local! { - static UI_FACTOR_SETTING: std::cell::Cell = std::cell::Cell::new(0); static TOUCH: std::cell::Cell = std::cell::Cell::new(false); static POINTER: std::cell::Cell<(f32, f32)> = std::cell::Cell::new((-1e5, -1e5)); static VIRTUAL_ROWS: std::cell::Cell = std::cell::Cell::new(0); @@ -43,10 +43,6 @@ pub fn device_dpr() -> f32 { screen_dpi_scale() } -pub fn set_ui_factor(f: u8) { - UI_FACTOR_SETTING.with(|c| c.set(f)); -} - // touch interface active (bigger interface, category toolbar, gestures) pub fn set_touch(on: bool) { TOUCH.with(|c| c.set(on)); @@ -84,10 +80,6 @@ pub fn mouse() -> (f32, f32) { // interface factor (integer) #[allow(non_snake_case)] pub fn F() -> u32 { - let set = UI_FACTOR_SETTING.with(|c| c.get()); - if set > 0 { - return set as u32; - } // the virtual screen already has the right size for the interface if virtual_rows() > 0 { return 1;