slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
17.58k stars 604 forks source link

Wrong "scale_factor" of window after calling set_position #5701

Open Horbin-Magician opened 3 months ago

Horbin-Magician commented 3 months ago

Slint: 1.7.0; Windows; Rust

When I use two monitors with different scale factors. A new window always return the scale factor of the primary monitor when using win.window().scale_factor();. At the same time, if it first appears on the non-primary monitor, its window size is not normal.

I'm not quite sure at what stage the scale_factor is set, but I think it needs to be reset after using win.window().set_position() or before win.show().

ogoffart commented 3 months ago

could you maybe provide a sample code that one can try that reproduce the problem?

Horbin-Magician commented 3 months ago

I implemented a simple demo. It is a borderless window, and a button that takes up the entire window, like:

image

The appwindow.slint file:

import { Button, VerticalBox } from "std-widgets.slint";

export component AppWindow inherits Window {
    no-frame: true;

    in-out property <int> win_width;
    in-out property <int> win_height;
    callback clicked <=> btn.clicked;

    width: win_width * 1px;
    height: win_height * 1px;

    VerticalBox {
        padding: 0px;
        btn := Button {
            text: "Click";
        }
    }
}

The main.rs file:

slint::include_modules!();

use xcap::Monitor;

fn main() -> Result<(), slint::PlatformError> {
    let ui = AppWindow::new()?;

    // get screens and info
    let monitors = Monitor::all().unwrap();
    let mut sec_monitor = None;
    for monitor in monitors {
        if monitor.is_primary() == false {
            sec_monitor = Some(monitor);
        }
    }

    let mut default_scale_factor: f32 = 0.0;
    if let Some(sec_monitor) = sec_monitor {
        ui.window().set_position(slint::PhysicalPosition::new(sec_monitor.x(), sec_monitor.y()));
        default_scale_factor = ui.window().scale_factor();
        println!("the default scale factor: {default_scale_factor}");
        ui.set_win_height( (sec_monitor.height() as f32 / (default_scale_factor * 2.0) ) as i32);
        ui.set_win_width( (sec_monitor.width() as f32 / (default_scale_factor * 2.0) ) as i32);

        let ui_clone = ui.as_weak();
        ui.on_clicked(move || {
            let scale_factor = ui_clone.unwrap().window().scale_factor();
            println!("the scale factor: {scale_factor}");
        });
    } else {
        eprintln!("No secondary monitor found");
    }

    ui.run()
}

Abnormal one:the default_scale_factor always be the scale_factor of the primary monitor. But when I click the button, it returns the correct scale_factor. I understand this default behavior, but perhaps it would be better to refresh the scale_factor after setting the window position.

Abnormal two:according to the code below:

ui.set_win_height( (sec_monitor.height() as f32 / (default_scale_factor * 2.0) ) as i32);
ui.set_win_width( (sec_monitor.width() as f32 / (default_scale_factor * 2.0) ) as i32);

My expectation is that the window will take up a quarter of the monitor. But considering the “Abnormal one”, the reality should not be what I expected. But surprisingly, the window takes up exactly a quarter of the monitor, but the buttons don't take up the entire screen, like:

image

I think it might caused by a mix using of default scale factor and true scale factor in slint. This is my demo project: demo.zip

ogoffart commented 3 months ago

So the bug is that scale_factor is not updated right after set_position? I think that might be because set_position is asynchronous and so it will take some iteration of the event loop before it actually applies and that the new scale factor is provided to slint.

Horbin-Magician commented 3 months ago

So the bug is that scale_factor is not updated right after set_position? I think that might be because set_position is asynchronous and so it will take some iteration of the event loop before it actually applies and that the new scale factor is provided to slint.

Yes, besides, I think the "Abnormal two" is due to the correct scale_factor of winit. Because my window size is right, but the content displayed inside the window is wrong. The scale_factor of slint and winit should be synchronized (if my guess is right).