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.67k stars 611 forks source link

Window Button Styles #6675

Open TheColorRed opened 4 weeks ago

TheColorRed commented 4 weeks ago

fixes #6669

Adds three types of window button styles naming was taken from here.

There are a few winit restrictions.

Usage

let app = AppWindow::new()?;
app.window().set_window_button_style(slint::WindowButtonStyle::All); // Default
app.window().set_window_button_style(slint::WindowButtonStyle::None);
app.window().set_window_button_style(slint::WindowButtonStyle::Close);
app.window().set_window_button_style(slint::WindowButtonStyle::Minimize);
app.window().set_window_button_style(slint::WindowButtonStyle::Maximize);
app.window().set_window_button_style(slint::WindowButtonStyle::MinimizeClose);
app.window().set_window_button_style(slint::WindowButtonStyle::MaximizeClose);
app.window().set_window_button_style(slint::WindowButtonStyle::MinimizeMaximize);

Output

Full

image

Close

image

None

Note: On windows the x is still there just not clickable (not sure about on a different OS)

image

MinimizeMaximize

image

ogoffart commented 3 weeks ago

Thanks for the PR.

I'm just wondering if an enum is really appropriate vs 3 boolean to hide specific buttons. Would it not be simpler?

TheColorRed commented 3 weeks ago

@ogoffart You mean something like this? I didn't think of booleans, I think this look better than my implementation.

let min = false;
let max = false;
let close = true;
app.window().set_window_buttons(min, max, close);
ogoffart commented 3 weeks ago

No, I meant something like app.window().set_button_maximize_visible(true); and so on. But that's not really great.

A function with 3 boolean is quite confusing because people will write set_window_buttons(true, false, true); and that's unreadable and also easy to mix arguments.(boolean trap)

Another possibility: app.window().set_button_visible(WindowButton::Maximize, true) I think I like that.

There is also the choice to take she same API as winit with Flags.

I think your choice with an enum with 8 state is not that bad if there is only 3 buttons, but we have to think about extensibility. What if we want to add another button in the future without breaking the API? There is some WM with extra button. (Help, Menu, ...)

So I don't know exactly which API is the best that combine simplicity and extensibility. Perhaps the winit API with flags is the nicest. But flags in rust are a bit awkward to do.

Enyium commented 3 weeks ago

Shouldn't these be component properties?

export component AppWindow inherits Window {
    has-minimize-button: false;
    has-maximize-button: false;
    has-close-button: false;
}

Dialog should also make them available, but use different defaults (only close-button).

Why should you have to do this in the backend language?

TheColorRed commented 3 weeks ago

@ogoffart I have updated the implementation:

app.window().set_window_button_enabled(WindowButton::Minimize, true);
app.window().set_window_button_enabled(WindowButton::Maximize, true);
app.window().set_window_button_enabled(WindowButton::Close, true);

@Enyium

I am not sure that would fall in line with the current structure of the library, as you cannot make the window maximized from slint either, so this follows the current implementation of how setting a window to maximized works:

app.window().set_maximized(true);
Enyium commented 3 weeks ago

I am not sure that would fall in line with the current structure of the library, as you cannot make the window maximized from slint either

It's about what the user should be allowed to do! This is similar to the initial-size and resizing topic: You use Slint properties to define whether the user should be allowed to resize the window. (Docs: "Setting the width will result in a fixed width... The initial width can be controlled with the preferred-width property.")

You can't compare something like Window::set_window_button_enabled(WindowButton::Maximize, ...) to Window::set_size() and Window::set_maximize()! The latter are for immediate acions, not to manifest a framework of allowed user actions.

Do you want to disable the minimize- and maximize-button for the Window of every new Dialog component instance? It belongs to the design. It shares in defining the kind of dialog presented, and is unlikely to change during the lifetime of a window.

ogoffart commented 3 weeks ago

It is not always easy to figure out if this should be part of the native API vs. what should be part of the slint code API. The rule of thumbs is that if it is part of the design, then it should be in .slint, but if it is part of the logic, then it should be in the native code. Maximizing or Minimizing a window, when not done by the user is done by the programmer as a result of something. But maybe the appearance of the title bar is indeed something that should be described in the .slint file, it seems. So i tend to agree with @Enyium

TheColorRed commented 3 weeks ago

@ogoffart, @Enyium I have moved the code out of the API and into .slint. I wasn't sure on the name, so I went no-**-button as the default of a property seems to be false which when the value isn't set in the .slint, it automatically turns the button off which is why I didn't go with has-**-button .

Enyium commented 3 weeks ago

no-**-button

Please "Avoid Negative...Names". I also recommended this regarding the existing no-frame in #5882 - although built-in has-... properties always seem to be out-properties. Perhaps this would be a good time to think of whether has-... should also be used for properties that aren't exclusively out-properties, or whether Slint could use another positive phrasing for properties that are at least partly in-properties.

the default of a property seems to be false which when the value isn't set in the .slint, it automatically turns the button off

I don't think there's a problem defining true as the default. If it's not overwritten and not part of a two-way binding, the default of true should be kept.


According to GPT-4o, dialogs with hidden minimize- and maximize-button are more common and Dialog should hide them by default.

Enyium commented 2 weeks ago

We should use consistently either something like "visible" or "enabled" for these buttons. Winit uses "enabled", but I wonder if these shouldn't be "visible" instead. I'm a bit unsure about that. [@ogoffart]

Note that this is already inconsistent under Windows. StackOverflow:

CS_NOCLOSE disables the close button (and [system] menu entry), but it will not remove it.

...

The only way to remove the button is to give up on the system menu completely. You have to omit the WS_SYSMENU style flag in your CreateWindowEx() call.

In contrast, other styles like WS_MAXIMIZEBOX and WS_MINIMIZEBOX can really set the button visibility. But this is also dependent on the configuration: The minimize- or the maximize-button may also be just disabled.

But I don't know whether winit even uses a window class per window at all, or reuses OS-level window classes. (CS_NOCLOSE is a class style, not a window style.)