egui icon indicating copy to clipboard operation
egui copied to clipboard

`set_zoom_factor` breaks `Area::default_pos` persistence

Open melon-masou opened this issue 2 years ago • 1 comments

Describe the bug When calling ctx.set_zoom_factor(2.0) at the very begining, there are two problems:

  • Area::default_pos() generates an incorrect initial position into memory
  • Enabling "persist_window" while disabling "persist_egui_memory", causes the window to shrink each time App is launched.

To Reproduce Demo codes, with two workarounds to make the codes working.

use eframe::egui;

fn main() -> Result<(), eframe::Error> {
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default()
            .with_inner_size([400.0, 400.0])
            .with_window_level(egui::WindowLevel::Normal)
            .with_app_id("egui_demo"),
        centered: true,
        persist_window: true,
        ..Default::default()
    };
    eframe::run_native(
        "My egui App",
        options,
        Box::new(|cc| {
            // Setting zoom_factor here has similar issue
            // cc.egui_ctx.set_zoom_factor(2.0);
            Box::<MyApp>::default()
        }),
    )
}

#[derive(Default)]
struct MyApp {
    not_first_frame: bool,
}

impl eframe::App for MyApp {
    fn persist_egui_memory(&self) -> bool {
        false // Actually I only want to persist main window size and position
    }
    fn save(&mut self, storage: &mut dyn eframe::Storage) {
        // Without this trick, the main window become smaller every time launching
        // storage.set_string("egui", format!("(options:(zoom_factor:{}))", 2.0))
    }
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        ctx.set_zoom_factor(2.0);

        // Without this trick, the following "center" is based on zoom_factor=1.0 at first frame
        // Emiting wrong default position in storage.
        // if !self.not_first_frame {
        //     self.not_first_frame = true;
        //     return;
        // }

        let center = ctx.screen_rect().center();
        let area = egui::Area::new("area_id")
            .pivot(egui::Align2::CENTER_CENTER)
            .default_pos(center);
        area.show(ctx, |ui| {
            egui::Frame::popup(ui.style()).show(ui, |ui| ui.label("TEST"))
        });
    }
}

Expected behavior

  • The area should appear at the center of the window, without the need to manually skip the first frame
  • The window size/postition remain consistent upon restarting. This could possibly solved by saving the zoom_factor along with window field in storage, or by saving the unscaled pixels

Desktop

  • OS: Windows 10
  • Version: 0.24.1, master

Screenshot ss

melon-masou avatar Dec 25 '23 08:12 melon-masou

Without having looked into this, I suspect this code, which applies the new zoom factor: https://github.com/emilk/egui/blob/1efa66014981b39f0357b8a49599e7266a96ad79/crates/egui/src/context.rs#L273-L287

emilk avatar Jan 04 '24 20:01 emilk

A very dirty hack we can do is explicitly save the scale factor memory bit on exit, which addresses half of the bug. I don't understand the problem with the area at the moment, so I can't comment on it

murl-digital avatar May 28 '24 03:05 murl-digital

Have a briefly looked into this, the problem occurs when:

  • persist_egui_memory() is false
  • NativeOptions.persist_window is true
  • zoom_factor is manually set by ctx.set_zoom_factor(), or ctx.memory_mut(|m| m.options.zoom_factor = XXX), inside AppCreator or eframe::App::update

Far before zoom_factor is actually set by user codes, the initial window size is decided by: https://github.com/emilk/egui/blob/cd3e9ea5b68107c34449f6355c7076ca5621bae4/crates/eframe/src/native/glow_integration.rs#L146-L161 Unfortunately, the initial window size is not that right since:

  • Window size is calculated from zoom_factor and inner_size_points persisted in storage
  • inner_size_points is correctly loaded from STORAGE_WINDOW_KEY
  • zoom_factor cannot be loaded from STORAGE_EGUI_MEMORY_KEY, since persist_egui_memory() is false. The default 1.0 is used
  • the initial window size and default pos of area is based on these incorrect value, so finally the window shrinks and the area shifted

Ideas on resolving the problem:

  1. persist zoom_factor under STORAGE_WINDOW_KEY instead of STORAGE_EGUI_MEMORY_KEY. A broken change though
  2. provide an API like ViewportBuilder.with_zoom_factor() to allow user setting the value before App init_run_state

melon-masou avatar Jun 21 '24 03:06 melon-masou