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

Set cursor on Glade-based GUI #587

Closed
StefanMathis opened this issue Sep 30, 2021 · 9 comments
Closed

Set cursor on Glade-based GUI #587

StefanMathis opened this issue Sep 30, 2021 · 9 comments

Comments

@StefanMathis
Copy link

StefanMathis commented Sep 30, 2021

Hello,

this is probably a very simple problem, but I can't figure out how to set the mouse cursor type (e.g. waiting, crosshair and so on) in a GUI window created by Glade. For a non-Glade UI, I found the following issue: #516.

I tried to apply this to a Glade-based window:

# Utility function to change the mouse cursor (see https://github.com/JuliaGraphics/Gtk.jl/issues/516)
function gdk_cursor_new(id::String)
	d = ccall((:gdk_display_get_default,Gtk.libgdk),Ptr{Nothing},(Cstring,),id)
	return ccall((:gdk_cursor_new_from_name,Gtk.libgdk),Ptr{Nothing},(Ptr{Nothing}, Cstring), d, id)
end

function gdk_window_set_cursor(wnd, cursor::Ptr{Nothing})
	wptr = Gtk.GAccessor.window(wnd)
	ccall((:gdk_window_set_cursor, Gtk.libgdk), Nothing, (Ptr{Nothing}, Ptr{Nothing}), wptr, cursor)
	return Cint(false) # Return value for Gtk
end

const CURSOR_WAIT = gdk_cursor_new("wait")
const CURSOR_DEFAULT = gdk_cursor_new("default")
const CURSOR_CROSSHAIR = gdk_cursor_new("crosshair")

builder = GtkBuilder(filename="arbitraryName.glade")
win = builder["main_window"]
gdk_window_set_cursor(builder["main_window"], CURSOR_WAIT)

This leads to the following warning:

Gdk-CRITICAL **: 20:58:35.164: gdk_window_set_cursor: assertion 'GDK_IS_WINDOW (window)' failed

I found that the type of win in the example above is Gtk.GtkWindowLeaf, while the type of win in the linked thread is GtkWindowLeaf. So i suspect there is some difference here ...

If necessary, I can gladly provide a sample Glade-file to get the MWE above to run.

Thanks for your help in advance!

@tknopp
Copy link
Collaborator

tknopp commented Oct 1, 2021

It seems that

wptr = Gtk.GAccessor.window(wnd)

is wrong. In the linked code this was used to extract the window from the canvas. In your example you are directly passing the window. What the window function does in case you pass a window to it is unclear to me.

@StefanMathis
Copy link
Author

StefanMathis commented Oct 1, 2021

When I remove this line and pass the wnd directly to the ccall, I get an error:

LoadError: MethodError: no method matching unsafe_convert(::Type{Ptr{Nothing}}, ::Gtk.GtkWindowLeaf)

If I understand correctly, this means that wnd needs to be converted to a pointer before passing it to the ccall, correct? I assume there is an GAccessor command for that (wink, wink)?

@StefanMathis
Copy link
Author

StefanMathis commented Oct 1, 2021

Okay, I found an explanation here:
https://stackoverflow.com/questions/54144406/stack7229-gdk-critical-110922-221-gdk-window-get-origin-assertion-g
So the problem here is that I need to call showall(win) before ccall'ing gdk_window_set_cursor.

However, accounting for that, a new problem arised:

GLib-GObject-CRITICAL **: 14:22:12.660: g_object_ref: assertion 'G_IS_OBJECT (object)' failed

So the search continues ;-)

EDIT: This error is due to the line mentioned by @tknopp. Simply passing wnd directly in the ccall leads to silent failure, which is progress I guess ...

@tknopp
Copy link
Collaborator

tknopp commented Oct 1, 2021

Best would be to put a small minimal working example so that I can test this. You can also past the glade file into a string and load that string with the builder object. In that way the file is self-contained.

@StefanMathis
Copy link
Author

Ok, I will cook up a MWE including glade later on and post it here.

@StefanMathis
Copy link
Author

Okay, so this is a strange one ... I created the following MWE:

Glade-file (save this in a text file MWE.glade)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkWindow" id="main_window">
    <property name="can-focus">False</property>
    <property name="title" translatable="yes">Simple cursor change test</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can-focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkButton" id="button_waiting">
            <property name="label" translatable="yes">Click me for waiting cursor</property>
            <property name="name">button</property>
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="receives-default">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button_default">
            <property name="label" translatable="yes">Click me for default cursor</property>
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="receives-default">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
  </object>
</interface>

Julia-File:

module MWE

using Gtk
using Gtk.ShortNames

export buildGUI

# Utility function to change the mouse cursor (see https://github.com/JuliaGraphics/Gtk.jl/issues/516)
function gdk_cursor_new(id::String)
	d = ccall((:gdk_display_get_default,Gtk.libgdk),Ptr{Nothing},(Cstring,),id)
	return ccall((:gdk_cursor_new_from_name,Gtk.libgdk),Ptr{Nothing},(Ptr{Nothing}, Cstring), d, id)
end

function gdk_window_set_cursor(wnd, cursor::Ptr{Nothing})
	wptr = Gtk.GAccessor.window(wnd)
	ccall((:gdk_window_set_cursor, Gtk.libgdk), Nothing, (Ptr{Nothing}, Ptr{Nothing}), wptr, cursor)
	return Cint(false) # Return value for Gtk
end

const CURSOR_WAIT = gdk_cursor_new("wait")
const CURSOR_DEFAULT = gdk_cursor_new("default")
const CURSOR_CROSSHAIR = gdk_cursor_new("crosshair")

# ==============================================================================

# Struct for the winding GUI
mutable struct GUIContainer
    GtkBuilder::GtkBuilder
end

function buildGUI()
	filename_UI = Base.Filesystem.joinpath(@__DIR__, "MWE.glade")
	builder = GtkBuilder(filename=filename_UI)
	win = builder["main_window"]
	obj = GUIContainer(builder)

	showall(win)

	# Callback
	id = signal_connect(cb_waiting, builder["button_waiting"], "clicked", Nothing, (), false, obj)
	id = signal_connect(cb_default, builder["button_default"], "clicked", Nothing, (), false, obj)

	# 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_waiting(widget, obj)

    gdk_window_set_cursor(obj.GtkBuilder["main_window"], CURSOR_WAIT)

    return nothing # This is important to fit with the return type
end

@guarded (nothing) function cb_default(widget, obj)

    gdk_window_set_cursor(obj.GtkBuilder["main_window"], CURSOR_DEFAULT)

    return nothing # This is important to fit with the return type
end

end # Module

using .MWE

buildGUI()

The good (or bad thing) is that this MWE works flawlessly. I tried stripping my real-world example down to the MWE, but the error persists. When I use my real-world glade file in the MWE, it works as well. So I doubt this is a problem with Gtk.jl and I think I hit a very strange corner case. So this is on me to find out. Anyway, I thought I'd leave the code for the working MWE here as documentation.

@StefanMathis
Copy link
Author

So I found the error :-) The problem is not the builder, the problem is here:

# Utility function to change the mouse cursor (see https://github.com/JuliaGraphics/Gtk.jl/issues/516)
function gdk_cursor_new(id::String)
	d = ccall((:gdk_display_get_default,Gtk.libgdk),Ptr{Nothing},(Cstring,),id)
	return ccall((:gdk_cursor_new_from_name,Gtk.libgdk),Ptr{Nothing},(Ptr{Nothing}, Cstring), d, id)
end

function gdk_window_set_cursor(wnd, cursor::Ptr{Nothing})
	wptr = Gtk.GAccessor.window(wnd)
	ccall((:gdk_window_set_cursor, Gtk.libgdk), Nothing, (Ptr{Nothing}, Ptr{Nothing}), wptr, cursor)
	return Cint(false) # Return value for Gtk
end

const CURSOR_WAIT = gdk_cursor_new("wait")
const CURSOR_DEFAULT = gdk_cursor_new("default")
const CURSOR_CROSSHAIR = gdk_cursor_new("crosshair")

The last part where the cursor pointers are created "in advance" doesn't work in my use case because the pointer is not identified as a GObject. Anyway, the fix is simple: Create the pointer anew each time a cursor change is needed:

# Utility function to change the mouse cursor (see https://github.com/JuliaGraphics/Gtk.jl/issues/516)
function gdk_cursor_new(id::String)
	d = ccall((:gdk_display_get_default,Gtk.libgdk),Ptr{Nothing},(Cstring,),id)
	return ccall((:gdk_cursor_new_from_name,Gtk.libgdk),Ptr{Nothing},(Ptr{Nothing}, Cstring), d, id)
end

function gdk_window_set_cursor(wnd, id::String)
        cursor = gdk_cursor_new(id)
	wptr = Gtk.GAccessor.window(wnd)
	ccall((:gdk_window_set_cursor, Gtk.libgdk), Nothing, (Ptr{Nothing}, Ptr{Nothing}), wptr, cursor)
	return Cint(false) # Return value for Gtk
end

I hope this helps in case someone else stumbles upon this. As I said, this is certainly not an Gtk.jl issue, so no further action is necessary.

@tknopp
Copy link
Collaborator

tknopp commented Oct 4, 2021

great that you found the error. Although it is not obvious to me why curser needs to be created again and again.

@StefanMathis
Copy link
Author

Well, to me it isn't either ;-) Especially because it works for my MWE. Perhaps I'll have a look at it again when I'm feeling bored ;-) Anyway, thank you very much for your support!

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