Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to influence where new windows are created? #58

Open
MichaelSchloesser opened this issue Jan 12, 2024 · 5 comments
Open
Labels
Acknowledged We are aware of this issue and will respond soon

Comments

@MichaelSchloesser
Copy link

MichaelSchloesser commented Jan 12, 2024

When opening a new window, say via an action set to a button the new window opens up at a rather random place. Closing the window and opening it again, it appears in a entirely different place every time. Is there some way to influence this?

A small example:

main() do app::Application
    main_window = Window(app)
    other_window = Window(app)
    set_hide_on_close!(other_window, true)

    button = Button()

    set_child!(main_window, button)

    open_other_window = Action("open_other_window.action", app) do x    
        set_is_modal!(other_window, true)
        present!(other_window)

        return nothing
    end
    set_action!(button, open_other_window)

    connect_signal_close_request!(main_window) do self::Window
        destroy!(other_window)
        return WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE
    end

    present!(main_window)
end

Device specifivations:
Device name: DESKTOP-Q9AJ3HU
Processor: Intel(R) Core(TM) i5-10600K CPU @ 4.10GHz 4.10 GHz
Installed RAM: 16,0 GB
System type: 64-bit operating system, x64-based processor
Pen and touch: No pen or touch input is available for this display

Windows specifications:
Edition: Windows 10 Pro
Version: 22H2
Installed on: ‎ 16/‎12/‎2020
OS build: 19045.3930
Experience: Windows Feature Experience Pack 1000.19053.1000.0

@Clemapfel Clemapfel added the Acknowledged We are aware of this issue and will respond soon label Jan 12, 2024
@Clemapfel
Copy link
Owner

Clemapfel commented Jan 12, 2024

There's definitely no way to do this cross-platform, as the window manager has complete control over where windows appear. On Linux Wayland or x11 you can get the native pointer to the window and call a Wayland or x11 function on it, but on Windows I'm not aware of a mechanism to set the exact positions.

Mousetrap lacks this feature because of GNOMEs (the developers of GTK4, which Mousetrap is just a wrapper of) design choices, see here for one of the main developers' justification for removing this feature. I personally agree, as being able to set the exact position would violate modern operating systems window layouts, like pinning the window to one side of the screen or next to another window. The window manager should have full control, not the app.

On my Linux fedora and Windows 10 machine, your example spawns the window at the same position every time, so I can't reproduce the random position behavior.

Either way, the correct way to do this is to use the transience property of a window to make sure a window is always spawned on top of another. You have to remember that most GUI apps will have a mostly full-screen window, not a tiny window with just one button, so when spawning a window on the screen, as long as that window isn't tiny and is spawned on top of all other windows, the users will be able to adapt and not get disoriented.

@MichaelSchloesser
Copy link
Author

MichaelSchloesser commented Jan 12, 2024

Interesting. I agree that the window manager should control the exact window positions. I just hoped to get the smaller window spawn to spawn "inside" the main one or to at least get rid of the randomness (whereever that comes from).

As for the the transience property, running

main() do app::Application
    main_window = Window(app)
    other_window = Window(app)

    set_transient_for!(other_window, main_window)  
end

gives me the error

[ERROR] In Mousetrap.main: type Window has no field _itnernal
Stacktrace:
  [1] getproperty
    @ ./Base.jl:37 [inlined]
  [2] set_transient_for!(self::Window, other::Window)
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:1605
  [3] (::var"#11#12")(app::Application)
    @ Main ./REPL[6]:5
  [4] (::TypedFunction)(args::Application)
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:91
  [5] (::Mousetrap.var"#14#15"{TypedFunction})(app::Application)
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:1542
  [6] (::TypedFunction)(args::Application)
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:91
  [7] (::Mousetrap.var"#6#8"{TypedFunction})(x::Tuple{CxxWrap.CxxWrapCore.CxxRef{Mousetrap.detail._Application}})
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:657
  [8] safe_call(scope::String, f::Function, args::Tuple{CxxWrap.CxxWrapCore.CxxRef{Mousetrap.detail._Application}})
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:190
  [9] run!(arg1::Mousetrap.detail._ApplicationAllocated)
    @ Mousetrap.detail ~/.julia/packages/CxxWrap/5IZvn/src/CxxWrap.jl:624
 [10] run!(app::Application)
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:1511
 [11] main
    @ ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:1552 [inlined]
 [12] main(f::Function)
    @ Mousetrap ~/.julia/packages/Mousetrap/BoOKO/src/Mousetrap.jl:1538
 [13] top-level scope
    @ REPL[6]:1
 [14] eval
    @ ./boot.jl:370 [inlined]
 [15] eval_user_input(ast::Any, backend::REPL.REPLBackend, mod::Module)
    @ REPL ~/.julia/juliaup/julia-1.9.4+0.x64.linux.gnu/share/julia/stdlib/v1.9/REPL/src/REPL.jl:153
 [16] repl_backend_loop(backend::REPL.REPLBackend, get_module::Function)
    @ REPL ~/.julia/juliaup/julia-1.9.4+0.x64.linux.gnu/share/julia/stdlib/v1.9/REPL/src/REPL.jl:249
 [17] start_repl_backend(backend::REPL.REPLBackend, consumer::Any; get_module::Function)
    @ REPL ~/.julia/juliaup/julia-1.9.4+0.x64.linux.gnu/share/julia/stdlib/v1.9/REPL/src/REPL.jl:234
 [18] run_repl(repl::REPL.AbstractREPL, consumer::Any; backend_on_current_task::Bool, backend::Any)
    @ REPL ~/.julia/juliaup/julia-1.9.4+0.x64.linux.gnu/share/julia/stdlib/v1.9/REPL/src/REPL.jl:379
 [19] run_repl(repl::REPL.AbstractREPL, consumer::Any)
    @ REPL ~/.julia/juliaup/julia-1.9.4+0.x64.linux.gnu/share/julia/stdlib/v1.9/REPL/src/REPL.jl:365
 [20] (::Base.var"#1018#1020"{Bool, Bool, Bool})(REPL::Module)
    @ Base ./client.jl:421
 [21] #invokelatest#2
    @ ./essentials.jl:819 [inlined]
 [22] invokelatest
    @ ./essentials.jl:816 [inlined]
 [23] run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, color_set::Bool)
    @ Base ./client.jl:405
 [24] exec_options(opts::Base.JLOptions)
    @ Base ./client.jl:322
 [25] _start()
    @ Base ./client.jl:522

@Clemapfel
Copy link
Owner

Wait what, there's a typo, how have I or someone else not found that yet, let me fix that real quick

@Clemapfel
Copy link
Owner

Clemapfel commented Jan 12, 2024

Until #59 is merged you can use this function instead:

function set_transient_for!(self::Window, other::Window)
    detail.set_transient_for!(self._internal, other._internal)
end

It just makes it so self is always on top of other.

Thank you for finding the typo

@MichaelSchloesser
Copy link
Author

I just gave you a stacktrace, you found the typo ;)

I can confirm that using this function not only works but also got rid of any randomness for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Acknowledged We are aware of this issue and will respond soon
Projects
None yet
Development

No branches or pull requests

2 participants