Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Open new window inside callback and yield control to the new window #592

Closed
StefanMathis opened this issue Nov 6, 2021 · 4 comments
Closed

Comments

@StefanMathis
Copy link

Hello,

I have the following question: I have a window with a button. When the button is clicked, another window opens. Now the new window should be active and the first window should wait until the second one has been closed. Also, it should be possible to create new tasks etc. in the second window while it is active. How do I achieve that?

Below you find a MWE which is almost working except for the detail that the second window doesn't "block" the first one:

using Gtk
using Gtk.ShortNames

function create_first_window()

    win = GtkWindow("First window", 400, 200)

    b = GtkButton("Click to open second window")
    push!(win,b)

    signal_connect(cb_first_window, b, "clicked", Nothing, (), false)

    showall(win)

    # Wait with programm progression until the window is closed
    c = Condition()
    signal_connect(win, :destroy) do widget
        notify(c)
    end
    wait(c)
end

@guarded (nothing) function cb_first_window(widget::Ptr, user_data)
    create_second_window()
    println("This should be printed last")
    println("===============")
    return nothing
end

function create_second_window()

    win = GtkWindow("Second window", 400, 200)

    b = GtkButton("Click to close this window")
    push!(win,b)
    showall(win)

    id = signal_connect(b, "clicked") do widget, others...
        t = Threads.@spawn sleep(0.5)
        println("This should be printed first")
        wait(t)
        destroy(win)
        return nothing
    end

    # # Wait with programm progression until the window is closed
    # c = Condition()
    # signal_connect(win, :destroy) do widget
    #     notify(c)
    # end
    # wait(c)
end

create_first_window()
@StefanMathis
Copy link
Author

StefanMathis commented Nov 6, 2021

So I found a way to get what I want by setting the first window to insensitive when the second one is created. When the "destroy"-signal for the second window is triggered, the first window is made sensitive again. I also included a print callback to detect the sensitive -> insensitive switch (and the other way around) from a callback of the first window. The full code can be found below.

If there is a more elegant way to achieve this, I'm always eager to learn, but for now this works for me.

@tknopp @timholy : Do you think this is interesting enough to create a corresponding example file? If yes, I'm happy to fill in a PR.

using Gtk
using Gtk.ShortNames

function create_first_window()

    win = GtkWindow("First window", 400, 200)

    b = GtkButton("Click to open second window")
    push!(win,b)

    signal_connect(cb_first_window, b, "clicked", Nothing, (), false, win)

    showall(win)

    signal_connect(cb_return_control, win, "state-flags-changed", Nothing, (), false, win)

    # Wait with programm progression until the window is closed
    c = Condition()
    signal_connect(win, :destroy) do widget
        notify(c)
    end
    @async Gtk.gtk_main()
    wait(c)
end

@guarded (nothing) function cb_return_control(widget::Ptr, user_data)

    win = convert(GtkWindow, widget)

    # Get the state flags
    sf = GAccessor.state_flags(win)

    # Detect a "rising edge" of the sensitivity
    if get_gtk_property(win, :sensitive, Bool) && sf == 192
        println("Returning control to the first window")

    # Detect a "falling edge" of the sensitivity
    elseif sf == 200
        println("Yielding control to the second window")
    end
    return nothing
end

@guarded (nothing) function cb_first_window(widget::Ptr, user_data)
    create_second_window(user_data)
    return nothing
end

function create_second_window(first_win)

    win = GtkWindow("Second window", 400, 200)

    b = GtkButton("Click to close this window")
    push!(win,b)
    showall(win)

    id = signal_connect(b, "clicked") do widget, others...
        destroy(win)
        return nothing
    end

    # Deactive the caller window for the time being
    set_gtk_property!(first_win, :sensitive, false)

    # Reactivate the caller window when this window is destroyed
    signal_connect(win, :destroy) do widget
        set_gtk_property!(first_win, :sensitive, true)
    end
end

create_first_window()

@tknopp
Copy link
Collaborator

tknopp commented Nov 8, 2021

I do not fully understand what you are aiming for here but isn't that what a Dialog is used for? A dialog can be a regular window that you fill with whatever you want. If you make the dialog modal that it will block the first window. There is thus no need to change the sensitivity manually.

@StefanMathis
Copy link
Author

Thank you very much for the hint regarding the modal property, this avoids the manual sensitivity fiddling. However, my main goal is to trigger a callback when the second window is closed which is why I have the state-flags-changed callback. Is there a better way to do this?

@tknopp
Copy link
Collaborator

tknopp commented Nov 8, 2021

In my point of view this scenario is much easier done with a dialog. A dialog is something that is run and you get feedback once it is finished. This will usually not be a close of a window but a dialog has buttons where the user presses "ok" or "cancel".

If you have your custom dialog you can call it like this:

  signal_connect(myButton, :clicked) do w
    dlg = MyDialog(parentWindow) # I usually define a new composite widgets, which "inherits" from a base dialog. Lets call that "Mydialog"
    ret = run(dlg) # Now the second window is opened and takes over control. It just gets back if the second window is closed
    # Now the second window is closed and the first has back the control 
    if ret == GtkResponseType.ACCEPT
      # do whatever you want to do ...
    end
    destroy(dlg)
  end

and here is how you implement MyDialog


mutable struct MyDialog <: Gtk.GtkDialog
  handle::Ptr{Gtk.GObject}
  ...
end


function MyDialog(..., parentWindow)

  dialog = Dialog("My cool new Dialog", parentWindow, GtkDialogFlags.MODAL,
                        Dict("gtk-cancel" => GtkResponseType.CANCEL,
                             "gtk-ok"=> GtkResponseType.ACCEPT) )

  resize!(dialog, 1024, 600)
  box = G_.content_area(dialog)

  # Now you can push your actual window widgets to box

  dlg = MyDialog(dialog.handle, ...)
  Gtk.gobject_move_ref(dlg, dialog) # this moves ref to dlg

  return dlg
end

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants