godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Give some way to define window size/position at runtime before the window is shown #3145

Open qrrk opened 3 years ago

qrrk commented 3 years ago

Describe the project you are working on

A non-game desktop application using Control nodes.

Describe the problem or limitation you are having in your project

Once the application starts, first thing I want to do is resize and/or reposition the window, either to account for screen DPI, or to restore previous size/position from a config file. Doing this after the window is already shown is kind of unprofessional and distracting (and can mess with window managers on Linux). This is made worse by the splash screen (see #3144): first it shows for some time, and only then you get to manipulate your window.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

If at all possible, add the ability to execute code before the game window is shown. Alternatively, allow the window to be hidden by default and made visible at the right moment from code.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

I honestly don't know how it can be done in the current engine paradigm. Maybe the second option from the previous paragraph will be both easier to implement and more straightforward to engine users: just have a project setting that makes the window hidden by default, and some method like OS.show_window().

If this enhancement will not be used often, can it be worked around with a few lines of script?

No, since your scripts don't get control early enough.

Is there a reason why this should be core and not an add-on in the asset library?

It will improve the usefulness of the engine for non-game applications that typically don't run in fullscreen. I can't imagine how this could be done with an add-on.

MaaaxiKing commented 3 years ago

You could set the window size without code at Project → Project Settings → Display → Window → Size.

qrrk commented 3 years ago

You could set the window size without code at Project → Project Settings → Display → Window → Size.

I know, but that's beside the point. The problem is, I can't know at compile time what size my window will need to be, and resizing it after it's already been on the screen for a bit is not ideal. It may go partially offscreen, or lose centering, which you can counteract to some degree, but it will still look janky to the user.

Calinou commented 3 years ago

Related to https://github.com/godotengine/godot-proposals/issues/3144.

One way this could be done is by adding a project settings to define the initial window position[^1] and using the project settings override feature. This is kind of hacky, because writing a project settings override file with only the settings desired requires a bit of wrangling around (usually with the ConfigFile class). See also https://github.com/godotengine/godot/pull/32056.

While the splash screen is being displayed, some of the engine singletons aren't initialized yet, which means it would be unsafe to call GDScript code at that point in time.

[^1]: Defining the initial window size and fullscreen state using the project settings is already possible. We'll also need a new project setting to define the maximized state too.

qrrk commented 3 years ago

While the splash screen is being displayed, some of the engine singletons aren't initialized yet, which means it would be unsafe to call GDScript code at that point in time.

Then perhaps a crude but effective solution could be to

allow the window to be hidden by default and made visible at the right moment from code.

Lauson1ex commented 3 years ago

Then perhaps a crude but effective solution could be to

allow the window to be hidden by default and made visible at the right moment from code.

You can do this by setting on Project Settings:

Display/Window/Size/Resizable = disabled Display/Window/Size/Borderless = enabled Display/Window/Size/Test Width = 1 Display/Window/Size/Test Height = 1 Display/Window/Per Pixel Transparency/Allowed = enabled Display/Window/Per Pixel Transparency/Enabled = enabled

Then using a 1x1 black transparent image as the boot splash.

Create an autoload script that changes the window size and position on _init(), and make sure it's the first autoload in the list:

#autoload.gd
extends Node

func _init():
    OS.set_window_size(Vector2(1280.0, 720.0))
    OS.center_window()

When you're finally ready to show the window, create a function to call deferred on idle:

#autoload.gd
func _on_SceneTree_idle():
    yield(get_tree(), "idle_frame")
    ProjectSettings.call_deferred("set_setting", "display/window/per_pixel_transparency/allowed", false)
    OS.set_deferred("window_per_pixel_transparency_enabled", false)
    OS.set_deferred("window_borderless", false)

You must also set the icon back to the title bar, because setting the window to borderless removes it. You can do this with:

    OS.call_deferred("set_icon", load("res://icon.png").get_data())

And finally, call _on_SceneTree_idle() on _ready():

#autoload.gd
func _ready():
    _on_SceneTree_idle()

I faced the same issue, so I did this for my project and it works great. The window gets hidden during initialization until you're ready to show it from code. I'm also attaching a 1x1 black transparent boot splash image so you don't have to create one from scratch. ??

transparent_splash.zip

qrrk commented 3 years ago

@Lauson1ex Finally got to try your solution. It works wonderfully, except there is an issue with KWin window manager on Linux which produces a glitchy bottom border when the window is changed from borderless to regular. I work around it by additionally moving the window by 1 pixel. Thanks for your help!

lostminds commented 2 months ago

Similar use-case I think, but with a different proposed solution: https://github.com/godotengine/godot-proposals/issues/10625