hugopl / gtk4.cr Goto Github PK
View Code? Open in Web Editor NEWGTK4 bindings for Crystal
Home Page: https://hugopl.github.io/gtk4.cr/
License: MIT License
GTK4 bindings for Crystal
Home Page: https://hugopl.github.io/gtk4.cr/
License: MIT License
Hi there!
Thank you for creating a practical GTK4 binding for the Crystal language.
I am working on a tool that shows data in a tabular format like this.
To add the ability to open a file, I wrote the following code.
def get_file_path
file_path = ""
dialog = Gtk::FileChooserDialog.new(
application: @app,
title: "Open File",
action: Gtk::FileChooserAction::Open,
transient_for: window,
modal: true
)
dialog.add_button("Cancel", Gtk::ResponseType::Cancel.value)
dialog.add_button("Open", Gtk::ResponseType::Accept.value)
dialog.response_signal.connect do |response|
case Gtk::ResponseType.from_value(response)
when .cancel?
when .accept?
file_path = dialog.file.try(&.path)
end
dialog.destroy
end
dialog.present
file_path
end
Here I tried to get the file path. This works.
The problem is that the method always return ""
.
It doesn't wait for the dialog to exit.
What am I missing?
Using this code:
require "gtk4"
file = Gio::File.parse_name("/some/image/path.png")
file.load_bytes(Gio::Cancellable.new)
Results in this exception:
Showing last frame. Use --error-trace for full trace.
In lib/gtk4/src/gio-2.0/file.cr:747:55
747 | def load_bytes(cancellable : Gio::Cancellable?) : GLib::Bytes
^----------
Error: undefined constant GLib::Bytes
I started with the helloworld example and install the dependency written in the documentation.
I'm on ubuntu 22.04 and the error was:
shards
Resolving dependencies
Fetching https://github.com/hugopl/gtk4.cr.git
Fetching https://github.com/hugopl/gi-crystal.git
Fetching https://github.com/hugopl/version_from_shard.git
Using version_from_shard (1.2.5)
Installing gi-crystal (0.14.0)
Postinstall of gi-crystal: shards build
Failed postinstall of gi-crystal on shards build:
Resolving dependencies
Fetching https://github.com/hugopl/version_from_shard.git
Using version_from_shard (1.2.5)
Writing shard.lock
Building: gi-crystal
Error target gi-crystal failed to compile:
/usr/bin/ld: impossibile trovare -lgirepository-1.0: File o directory non esistente
collect2: error: ld returned 1 exit status
Error: execution of command failed with code: 1: `cc "${@}" -o /home/marino/programmazione/personal/hellotk4/lib/gi-crystal/bin/gi-crystal -rdynamic -L/snap/crystal/1315/bin/../lib/crystal -lxml2 -lgirepository-1.0 -lglib-2.0 -lgobject-2.0 -lglib-2.0 -lyaml -lpcre -lm -lgc -lpthread -levent -lrt -lpthread -ldl`
I ended to install also:
sudo apt install libgirepository1.0-dev
and the install works correctly.
Should this go in the docs?
The Gtk::Editable#changed signal seems to be missing from the bindings (and that doesn't seem to be specifically disabled)
Trying to create the following cursor:
Gdk::Cursor.new_from_name("pointer", nil)
Results in the aforementioned compile-time error.
The generated method:
def self.new_from_name(name : ::String, fallback : Gdk::Cursor?) : self
# gdk_cursor_new_from_name: (Constructor)
# @fallback: (nullable)
# Returns: (transfer full)
# Handle parameters
fallback = if fallback.nil?
Pointer(Void).null
else
fallback.to_unsafe
end
# C call
_retval = LibGdk.gdk_cursor_new_from_name(name, fallback)
# Return value handling
Gdk::Cursor.new(_retval, GICrystal::Transfer::Full) unless _retval.null?
end
There's no type safety in current generated code, just some void pointers.
def set_draw_func(draw_func : Pointer(Void)?, user_data : Pointer(Void)?, destroy : Pointer(Void)) : Nil
# gtk_drawing_area_set_draw_func: (Method)
# @draw_func: (nullable)
# @user_data: (nullable)
# Returns: (transfer none)
# Handle parameters
draw_func = if draw_func.nil?
LibGtk::DrawingAreaDrawFunc.null
else
draw_func.to_unsafe
end
user_data = if user_data.nil?
Pointer(Void).null
else
user_data.to_unsafe
end
# C call
LibGtk.gtk_drawing_area_set_draw_func(self, draw_func, user_data, destroy)
# Return value handling
end
In both of these given examples, the application crashes (in one example instantly, in the other one when pressing the button):
require "gtk4"
app = Gtk::Application.new("hello.example.com", Gio::ApplicationFlags::None)
app.activate_signal.connect do
GLib.idle_add(GLib::Priority.new(500)) do
# Allocate memory until the gc collects or the ram is full
Pointer(UInt8).malloc(1)
true
end
window = Gtk::ApplicationWindow.new(app)
window.present
end
app.run(ARGV)
require "gtk4"
app = Gtk::Application.new("hello.example.com", Gio::ApplicationFlags::None)
app.activate_signal.connect do
window = Gtk::ApplicationWindow.new(app)
button = Gtk::Button.new(label: "Crash!")
button.clicked_signal.connect { GC.collect }
window.child = button
window.present
end
app.run(ARGV)
Using GC.disable
in either of the two examples fixes the problem, but that's not really a good solution.
I don't know if this issue actually belongs here or in gi-crystal.
Using this code:
require "gtk4"
file = Gio::File.parse_name("/some/image/path.png")
Gtk::Picture.new(file: file)
results in this error:
Unhandled exception: Unable to wrap a Gio::File__Impl into a GValue, probably not implemented. (ArgumentError)
from lib/gtk4/src/g_object-2.0/includes/value.cr:58:9 in 'g_type_for'
from lib/gtk4/src/g_object-2.0/includes/value.cr:16:36 in 'init_g_value'
from lib/gtk4/src/gtk-4.0/picture.cr:114:9 in 'initialize:file'
from lib/gtk4/src/gtk-4.0/picture.cr:67:5 in 'new:file'
from src/test2.cr:4:1 in '__crystal_main'
from /usr/share/crystal/src/crystal/main.cr:110:5 in 'main_user_code'
from /usr/share/crystal/src/crystal/main.cr:96:7 in 'main'
from /usr/share/crystal/src/crystal/main.cr:119:3 in 'main'
from /lib64/libc.so.6 in '??'
from /lib64/libc.so.6 in '__libc_start_main'
from /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp in '_start'
from ???
The following code:
require "gtk4"
snapshot = Gtk::Snapshot.new
texture = Gdk::Texture.new_from_filename("/path/to/some/image.ext")
snapshot.append_texture(texture, Graphene::Rect.zero)
Causes the program to crash with the following message:
Invalid memory access (signal 11) at address 0xffffffffffffff98
[0x4b71b6] *Exception::CallStack::print_backtrace:Nil +118 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x49524a] ~procProc(Int32, Pointer(LibC::SiginfoT), Pointer(Void), Nil) +330 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x7f608dd6dac0] ?? +140052673256128 in /lib64/libc.so.6
[0x7f608e4bbc9e] ?? +140052680916126 in /lib64/libgtk-4.so.1
[0x7f608e4bd0cb] gtk_snapshot_append_texture +155 in /lib64/libgtk-4.so.1
[0x550de2] *Gtk::Snapshot#append_texture<Gdk::Texture+, Graphene::Rect>:Nil +18 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x479e47] __crystal_main +1335 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x554c86] *Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil +6 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x554bd9] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +41 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x487486] main +6 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x7f608dd58590] ?? +140052673168784 in /lib64/libc.so.6
[0x7f608dd58649] __libc_start_main +137 in /lib64/libc.so.6
[0x479845] _start +37 in /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp
[0x0] ???
This used to work in an older version of GI-Crystal.
GI-Crystal version: 0.12.0
Crystal version: 1.4.1
The following code:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
app.activate_signal.connect do
window = Gtk::ApplicationWindow.new(application: app)
window.child = box = Gtk::Box.new
GLib.timeout_milliseconds(10) do
child = Gtk::Label.new label: "Test!" * 10000 # * 10000 to make it more dramatic
box.append child
box.remove child
puts child
true
end
window.present
end
exit(app.run(ARGV))
Simplified:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
app.activate_signal.connect do
loop do
Gtk::Label.new label: "Test!" * 1000 # * 1000 to make it more dramatic
end
end
exit(app.run(ARGV))
Will leak memory, even though it is now finalizing the Gtk::Label instances.
Everything works nice with mesa <= 21.3.7 on ArchLinux, but if I update to mesa 22.0 everything crashes on libiris.so. Looks like a mesa bug, but sometimes it's just a existing bug in the bindings that is now visible with the new version of this library.
On Archlinux with a intel card, try to run the hello world example. The bug is not GC related, since it works with GC enable or not.
Thread 1 "hello_world" received signal SIGSEGV, Segmentation fault.
0x00007fffe36e308b in ?? () from /usr/lib/dri/iris_dri.so
(gdb) bt full
#0 0x00007fffe36e308b in () at /usr/lib/dri/iris_dri.so
#1 0x00007fffe36e37e8 in () at /usr/lib/dri/iris_dri.so
#2 0x00007fffe33e25d4 in () at /usr/lib/dri/iris_dri.so
#3 0x00007fffe332b5d4 in () at /usr/lib/dri/iris_dri.so
#4 0x00007fffe33311dc in () at /usr/lib/dri/iris_dri.so
#5 0x00007fffe3333965 in () at /usr/lib/dri/iris_dri.so
#6 0x00007fffe33fe940 in () at /usr/lib/dri/iris_dri.so
#7 0x00007fffe3402a27 in () at /usr/lib/dri/iris_dri.so
#8 0x00007fffe341673d in () at /usr/lib/dri/iris_dri.so
#9 0x00007fffe324da59 in () at /usr/lib/dri/iris_dri.so
#10 0x00007fffe2b1a9b7 in () at /usr/lib/dri/iris_dri.so
#11 0x00007fffe2b1b84a in () at /usr/lib/dri/iris_dri.so
#12 0x00007fffe365a643 in () at /usr/lib/dri/iris_dri.so
#13 0x00007fffe2b1bb05 in () at /usr/lib/dri/iris_dri.so
#14 0x00007fffe26fdbd8 in () at /usr/lib/dri/iris_dri.so
#15 0x00007fffe26b14cf in () at /usr/lib/dri/iris_dri.so
#16 0x00007fffe26bc889 in () at /usr/lib/dri/iris_dri.so
#17 0x00007fffe26bc979 in () at /usr/lib/dri/iris_dri.so
#18 0x00007ffff7c3c66d in gsk_gl_glyph_library_upload_glyph (uheight=<optimized out>, uwidth=<optimized out>, height=26, width=<optimized out>, y=1, x=4, value=0x7fffe4015700, key=<optimized out>, self=<optimized out>)
at ../gtk/gsk/gl/gskglglyphlibrary.c:251
tl = <optimized out>
start_time = 0
surface = 0x5555568ef800
free_data = 0x0
gl_format = <optimized out>
pixel_data = <optimized out>
gl_type = <optimized out>
texture_id = <optimized out>
stride = <optimized out>
tl = <optimized out>
value = 0x7fffe4015700
packed_y = 0
ink_rect = {x = 0, y = -12, width = 11, height = 13}
width = <optimized out>
height = 26
packed_x = 3
k = <optimized out>
glyph_y = <optimized out>
glyph_y2 = <optimized out>
cx = <optimized out>
texture_id = <optimized out>
tx = <optimized out>
tx2 = <optimized out>
cy = 105
glyph = <optimized out>
glyph_x = <optimized out>
glyph_x2 = <optimized out>
ty = <optimized out>
ty2 = <optimized out>
font = <optimized out>
glyphs = <optimized out>
offset = <optimized out>
text_scale = <optimized out>
num_glyphs = <optimized out>
x = <optimized out>
y = <optimized out>
library = <optimized out>
batch = 0x5555568b5dd0
x_position = 10829
lookup = {font = 0x5555558af460, glyph = 76, xshift = 0, yshift = 2, scale = 2048}
last_texture = 0
vertices = 0x5555568bc630
used = 0
nc = {48128, 48128, 48128, 48128}
cc = {15223, 15223, 15207, 15360}
c = 0x7fffffffcdd8
gi = 0x5555568ef560
i = 0
yshift = <optimized out>
ypos = <optimized out>
#19 gsk_gl_glyph_library_add (out_value=<synthetic pointer>, key=<optimized out>, self=<optimized out>) at ../gtk/gsk/gl/gskglglyphlibrary.c:309
tl = <optimized out>
value = 0x7fffe4015700
packed_y = 0
ink_rect = {x = 0, y = -12, width = 11, height = 13}
width = <optimized out>
height = 26
packed_x = 3
k = <optimized out>
glyph_y = <optimized out>
glyph_y2 = <optimized out>
cx = <optimized out>
texture_id = <optimized out>
tx = <optimized out>
tx2 = <optimized out>
cy = 105
glyph = <optimized out>
glyph_x = <optimized out>
glyph_x2 = <optimized out>
ty = <optimized out>
ty2 = <optimized out>
font = <optimized out>
glyphs = <optimized out>
offset = <optimized out>
text_scale = <optimized out>
num_glyphs = <optimized out>
x = <optimized out>
y = <optimized out>
library = <optimized out>
batch = 0x5555568b5dd0
x_position = 10829
lookup = {font = 0x5555558af460, glyph = 76, xshift = 0, yshift = 2, scale = 2048}
last_texture = 0
vertices = 0x5555568bc630
used = 0
nc = {48128, 48128, 48128, 48128}
cc = {15223, 15223, 15207, 15360}
c = 0x7fffffffcdd8
gi = 0x5555568ef560
i = 0
yshift = <optimized out>
ypos = <optimized out>
#20 gsk_gl_glyph_library_lookup_or_add (out_value=<synthetic pointer>, key=0x7fffffffcd70, self=<optimized out>) at ../gtk/gsk/gl/gskglglyphlibraryprivate.h:93
k = <optimized out>
glyph_y = <optimized out>
glyph_y2 = <optimized out>
cx = <optimized out>
texture_id = <optimized out>
tx = <optimized out>
tx2 = <optimized out>
cy = 105
glyph = <optimized out>
glyph_x = <optimized out>
glyph_x2 = <optimized out>
ty = <optimized out>
ty2 = <optimized out>
font = <optimized out>
glyphs = <optimized out>
offset = <optimized out>
text_scale = <optimized out>
num_glyphs = <optimized out>
x = <optimized out>
y = <optimized out>
library = <optimized out>
batch = 0x5555568b5dd0
x_position = 10829
lookup = {font = 0x5555558af460, glyph = 76, xshift = 0, yshift = 2, scale = 2048}
last_texture = 0
vertices = 0x5555568bc630
used = 0
nc = {48128, 48128, 48128, 48128}
cc = {15223, 15223, 15207, 15360}
c = 0x7fffffffcdd8
gi = 0x5555568ef560
i = 0
yshift = <optimized out>
ypos = <optimized out>
#21 gsk_gl_render_job_visit_text_node (job=job@entry=0x5555556c3c10, node=node@entry=0x7fffe4007370, color=<optimized out>, force_color=force_color@entry=0) at ../gtk/gsk/gl/gskglrenderjob.c:3069
glyph_y = <optimized out>
glyph_y2 = <optimized out>
cx = <optimized out>
texture_id = <optimized out>
tx = <optimized out>
tx2 = <optimized out>
cy = 105
glyph = <optimized out>
glyph_x = <optimized out>
glyph_x2 = <optimized out>
ty = <optimized out>
ty2 = <optimized out>
font = <optimized out>
glyphs = <optimized out>
offset = <optimized out>
text_scale = <optimized out>
num_glyphs = <optimized out>
x = <optimized out>
y = <optimized out>
library = <optimized out>
batch = 0x5555568b5dd0
x_position = 10829
lookup = {font = 0x5555558af460, glyph = 76, xshift = 0, yshift = 2, scale = 2048}
last_texture = 0
vertices = 0x5555568bc630
used = 0
nc = {48128, 48128, 48128, 48128}
cc = {15223, 15223, 15207, 15360}
c = 0x7fffffffcdd8
gi = 0x5555568ef560
i = 0
yshift = <optimized out>
ypos = <optimized out>
#22 0x00007ffff7c3ea60 in gsk_gl_render_job_visit_node (job=0x5555556c3c10, node=0x7fffe4007370) at ../gtk/gsk/gl/gskglrenderjob.c:3809
has_clip = 1
__func__ = "gsk_gl_render_job_visit_node"
#23 0x00007ffff7c3e969 in gsk_gl_render_job_visit_node (job=0x5555556c3c10, node=<optimized out>) at ../gtk/gsk/gl/gskglrenderjob.c:3733
child = 0x7fffe4007370
i = 2
n_children = 3
has_clip = 0
__func__ = "gsk_gl_render_job_visit_node"
#24 0x00007ffff7c3226b in gsk_gl_render_job_visit_transform_node (job=0x5555556c3c10, node=0x5555568be840) at ../gtk/gsk/gl/gskglrenderjob.c:2055
dx = 17
dy = 5
transform = 0x5555568f77b0
category = GSK_TRANSFORM_CATEGORY_2D_TRANSLATE
child = 0x5555568d0100
__func__ = "gsk_gl_render_job_visit_transform_node"
#25 0x00007ffff7c3ebdd in gsk_gl_render_job_visit_node (job=0x5555556c3c10, node=0x5555568be840) at ../gtk/gsk/gl/gskglrenderjob.c:3820
has_clip = 0
__func__ = "gsk_gl_render_job_visit_node"
#26 0x00007ffff7c30799 in gsk_gl_render_job_visit_clipped_child (job=0x5555556c3c10, child=0x5555568be840, clip=0x5555568be3a8) at ../gtk/gsk/gl/gskglrenderjob.c:1659
transformed_clip = {origin = {x = 0, y = 0}, size = {width = 400, height = 400}}
intersection = {bounds = {origin = {x = 0, y = 0}, size = {width = 400, height = 400}}, corner = {{width = 0, height = 0}, {width = 0, height = 0}, {width = 0, height = 0}, {width = 0, height = 0}}}
__func__ = "gsk_gl_render_job_visit_clipped_child"
#27 0x00007ffff7c3f18b in gsk_gl_render_job_visit_clip_node (node=0x5555568be380, job=0x5555556c3c10) at ../gtk/gsk/gl/gskglrenderjob.c:1693
clip = 0x5555568be3a8
child = <optimized out>
has_clip = 0
__func__ = "gsk_gl_render_job_visit_node"
#28 gsk_gl_render_job_visit_node (job=0x5555556c3c10, node=0x5555568be380) at ../gtk/gsk/gl/gskglrenderjob.c:3686
has_clip = 0
__func__ = "gsk_gl_render_job_visit_node"
#29 0x00007ffff7c3e969 in gsk_gl_render_job_visit_node (job=0x5555556c3c10, node=<optimized out>) at ../gtk/gsk/gl/gskglrenderjob.c:3733
child = 0x5555568be380
i = 1
n_children = 2
has_clip = 1
__func__ = "gsk_gl_render_job_visit_node"
#30 0x00007ffff7c22a13 in gsk_gl_render_job_render (root=0x5555568f1030, job=0x5555556c3c10) at ../gtk/gsk/gl/gskglrenderjob.c:4094
start_time = 0
scale_factor = 2
surface_height = 400
self = 0x5555558f9700
render_region = 0x0
viewport = {origin = {x = 0, y = 0}, size = {width = 400, height = 400}}
job = 0x5555556c3c10
surface = <optimized out>
clear_framebuffer = <optimized out>
scale_factor = <optimized out>
__func__ = "gsk_gl_renderer_render"
#31 gsk_gl_renderer_render (renderer=0x5555558f9700, root=0x5555568f1030, update_area=<optimized out>) at ../gtk/gsk/gl/gskglrenderer.c:290
self = 0x5555558f9700
render_region = 0x0
viewport = {origin = {x = 0, y = 0}, size = {width = 400, height = 400}}
job = 0x5555556c3c10
surface = <optimized out>
clear_framebuffer = <optimized out>
scale_factor = <optimized out>
__func__ = "gsk_gl_renderer_render"
#32 0x00007ffff7c0900d in gsk_renderer_render (renderer=0x5555558f9700, root=0x5555568f1030, region=<optimized out>) at ../gtk/gsk/gskrenderer.c:467
priv = 0x5555558f96c0
clip = 0x5555556c8ab0
__func__ = "gsk_renderer_render"
#33 0x00007ffff7abf146 in gtk_widget_render (widget=<optimized out>, surface=<optimized out>, region=0x5555568f0e40) at ../gtk/gtk/gtkwidget.c:11694
priv = <optimized out>
snapshot = <optimized out>
renderer = 0x5555558f9700
root = 0x5555568f1030
x = 0
y = 0
#34 0x00007ffff7abfeb9 in surface_render () at ../gtk/gtk/gtkwindow.c:5158
#35 0x00007ffff7b8a449 in _gdk_marshal_BOOLEAN__BOXEDv (closure=0x555555fb5df0, return_value=0x7fffffffdb60, instance=<optimized out>, args=<optimized out>, marshal_data=<optimized out>, n_params=<optimized out>, param_types=0x555555782040) at gdk/gdkmarshalers.c:130
data1 = 0x5555556e84f0
data2 = <optimized out>
callback = 0x7ffff7abfea0 <surface_render>
v_return = <optimized out>
arg0 = 0x5555568f0e40
args_copy = {{gp_offset = 32, fp_offset = 48, overflow_arg_area = 0x7fffffffdcf0, reg_save_area = 0x7fffffffdc30}}
__func__ = "_gdk_marshal_BOOLEAN__BOXEDv"
#36 0x00007ffff75f86b6 in _g_closure_invoke_va (param_types=0x555555782040, n_params=1, args=0x7fffffffdc10, instance=0x5555556e84f0, return_value=<optimized out>, closure=<optimized out>) at ../glib/gobject/gclosure.c:893
marshal = <optimized out>
marshal_data = <optimized out>
in_marshal = 0
real_closure = <optimized out>
return_accu = <optimized out>
accu = {g_type = 0x14, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
accumulator = 0x555555782060
emission = Python Exception <class 'TypeError'>: can only concatenate str (not "NoneType") to str
{next = 0x7fffffffddc0, instance = 0x5555556e84f0, ihint = {signal_id = 47, detail = 0, run_type = (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN)}, state = EMISSION_RUN, chain_type = }
instance_type = <optimized out>
emission_return = {g_type = 0x14, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
rtype = 0x14
static_scope = 0
fastpath_handler = <optimized out>
closure = <optimized out>
run_type = <optimized out>
hlist = <optimized out>
l = <optimized out>
fastpath = 1
instance_and_params = <optimized out>
signal_return_type = <optimized out>
param_values = <optimized out>
node = <optimized out>
i = <optimized out>
n_params = <optimized out>
__func__ = "g_signal_emit_valist"
#37 g_signal_emit_valist (instance=0x5555556e84f0, signal_id=47, detail=<optimized out>, var_args=var_args@entry=0x7fffffffdc10) at ../glib/gobject/gsignal.c:3406
return_accu = <optimized out>
accu = {g_type = 0x14, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
accumulator = 0x555555782060
emission = Python Exception <class 'TypeError'>: can only concatenate str (not "NoneType") to str
{next = 0x7fffffffddc0, instance = 0x5555556e84f0, ihint = {signal_id = 47, detail = 0, run_type = (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN)}, state = EMISSION_RUN, chain_type = }
instance_type = <optimized out>
emission_return = {g_type = 0x14, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
rtype = 0x14
static_scope = 0
fastpath_handler = <optimized out>
closure = <optimized out>
run_type = <optimized out>
hlist = <optimized out>
l = <optimized out>
fastpath = 1
instance_and_params = <optimized out>
signal_return_type = <optimized out>
param_values = <optimized out>
node = <optimized out>
i = <optimized out>
n_params = <optimized out>
__func__ = "g_signal_emit_valist"
#38 0x00007ffff75f8824 in g_signal_emit (instance=instance@entry=0x5555556e84f0, signal_id=<optimized out>, detail=detail@entry=0) at ../glib/gobject/gsignal.c:3553
var_args = {{gp_offset = 24, fp_offset = 48, overflow_arg_area = 0x7fffffffdcf0, reg_save_area = 0x7fffffffdc30}}
#39 0x00007ffff7bb735c in gdk_surface_process_updates_internal (surface=0x5555556e84f0) at ../gtk/gdk/gdksurface.c:1348
expose_region = 0x5555568f0e40
handled = 32767
surface = 0x5555556e84f0
__func__ = "gdk_surface_paint_on_clock"
#40 gdk_surface_paint_on_clock (clock=<optimized out>, data=0x5555556e84f0) at ../gtk/gdk/gdksurface.c:1436
surface = 0x5555556e84f0
__func__ = "gdk_surface_paint_on_clock"
#41 0x00007ffff75f86b6 in _g_closure_invoke_va (param_types=0x0, n_params=0, args=0x7fffffffdec0, instance=0x5555557d6310, return_value=<optimized out>, closure=<optimized out>) at ../glib/gobject/gclosure.c:893
marshal = <optimized out>
marshal_data = <optimized out>
in_marshal = 0
real_closure = <optimized out>
return_accu = <optimized out>
accu = {g_type = 0x0, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
accumulator = 0x0
emission = Python Exception <class 'TypeError'>: can only concatenate str (not "NoneType") to str
{next = 0x0, instance = 0x5555557d6310, ihint = {signal_id = 43, detail = 0, run_type = (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN)}, state = EMISSION_RUN, chain_type = }
instance_type = <optimized out>
emission_return = {g_type = 0x0, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
rtype = 0x4
static_scope = 0
fastpath_handler = <optimized out>
closure = <optimized out>
run_type = <optimized out>
hlist = <optimized out>
l = <optimized out>
fastpath = 1
instance_and_params = <optimized out>
signal_return_type = <optimized out>
param_values = <optimized out>
node = <optimized out>
i = <optimized out>
n_params = <optimized out>
__func__ = "g_signal_emit_valist"
#42 g_signal_emit_valist (instance=0x5555557d6310, signal_id=43, detail=<optimized out>, var_args=var_args@entry=0x7fffffffdec0) at ../glib/gobject/gsignal.c:3406
return_accu = <optimized out>
accu = {g_type = 0x0, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
accumulator = 0x0
emission = Python Exception <class 'TypeError'>: can only concatenate str (not "NoneType") to str
{next = 0x0, instance = 0x5555557d6310, ihint = {signal_id = 43, detail = 0, run_type = (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN)}, state = EMISSION_RUN, chain_type = }
instance_type = <optimized out>
emission_return = {g_type = 0x0, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
rtype = 0x4
static_scope = 0
fastpath_handler = <optimized out>
closure = <optimized out>
run_type = <optimized out>
hlist = <optimized out>
l = <optimized out>
fastpath = 1
instance_and_params = <optimized out>
signal_return_type = <optimized out>
param_values = <optimized out>
node = <optimized out>
i = <optimized out>
n_params = <optimized out>
__func__ = "g_signal_emit_valist"
#43 0x00007ffff75f8824 in g_signal_emit (instance=instance@entry=0x5555557d6310, signal_id=<optimized out>, detail=detail@entry=0) at ../glib/gobject/gsignal.c:3553
var_args = {{gp_offset = 24, fp_offset = 48, overflow_arg_area = 0x7fffffffdfa0, reg_save_area = 0x7fffffffdee0}}
#44 0x00007ffff7babe09 in _gdk_frame_clock_emit_paint (frame_clock=0x5555557d6310) at ../gtk/gdk/gdkframeclock.c:708
before = 0
clock = 0x5555557d6310
clock_idle = 0x5555557d6310
priv = 0x5555557d6200
skip_to_resume_events = 0
timings = <optimized out>
__func__ = "gdk_frame_clock_paint_idle"
#45 gdk_frame_clock_paint_idle (data=0x5555557d6310, data@entry=<error reading variable: value has been optimized out>) at ../gtk/gdk/gdkframeclockidle.c:605
clock = 0x5555557d6310
clock_idle = 0x5555557d6310
priv = 0x5555557d6200
skip_to_resume_events = 0
timings = <optimized out>
__func__ = "gdk_frame_clock_paint_idle"
#46 0x00007ffff74de958 in g_timeout_dispatch (source=0x5555568f2400, callback=<optimized out>, user_data=<optimized out>) at ../glib/glib/gmain.c:4971
timeout_source = 0x5555568f2400
again = <optimized out>
#47 0x00007ffff74de163 in g_main_dispatch (context=0x5555556a2230) at ../glib/glib/gmain.c:3417
dispatch = 0x7ffff74de940 <g_timeout_dispatch>
prev_source = 0x0
begin_time_nsec = 6714916237286
was_in_call = <optimized out>
user_data = 0x5555557d6310
callback = 0x7ffff7baba40 <gdk_frame_clock_paint_idle>
cb_funcs = 0x7ffff75c53e0 <g_source_callback_funcs>
cb_data = 0x7fffe4009380
need_destroy = <optimized out>
source = 0x5555568f2400
current = 0x5555556bc370
i = 0
#48 g_main_context_dispatch (context=0x5555556a2230) at ../glib/glib/gmain.c:4135
#49 0x00007ffff75349e9 in g_main_context_iterate.constprop.0 (context=context@entry=0x5555556a2230, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/glib/gmain.c:4211
max_priority = 120
timeout = 0
some_ready = 1
nfds = 2
allocated_nfds = 2
fds = 0x5555556a2210
begin_time_nsec = 6714916225988
#50 0x00007ffff74db6c5 in g_main_context_iteration (context=context@entry=0x5555556a2230, may_block=may_block@entry=1) at ../glib/glib/gmain.c:4276
retval = <optimized out>
#51 0x00007ffff76f94ee in g_application_run (application=0x55555569f0e0, argc=-7852, argv=<optimized out>) at ../glib/gio/gapplication.c:2569
arguments = 0x5555556a2210
status = 0
context = 0x5555556a2230
acquired_context = <optimized out>
__func__ = "g_application_run"
#52 0x000055555563e82e in run () at /home/hugo/src/gtk4/lib/gi-crystal/src/auto/gio-2.0/application.cr:955
#53 0x00005555555a0ae3 in __crystal_main () at /home/hugo/src/gtk4/examples/hello_world.cr:20
#54 0x0000555555643016 in main_user_code () at /usr/lib/crystal/crystal/main.cr:115
#55 0x0000555555642f74 in main () at /usr/lib/crystal/crystal/main.cr:101
#56 0x00005555555adde6 in main () at /usr/lib/crystal/crystal/main.cr:127
Do not upgrade mesa package to version 22.0.
Archlinux kernel 5.17.1-arch1-1 x86_64
Video: Intel Corporation UHD Graphics 620 (rev 07)
GTK: 4.6.2
mesa: 22.0.0
css_provider = Gtk::CssProvider.new
css_provider.parsing_error_signal.connect do |section, error|
# debug
end
This code does not compile with the following error message:
In lib/libadwaita/lib/gi-crystal/src/auto/gtk-4.0/css_provider.cr:198:74
198 | ::Box(Proc(Gtk::CssSection, GLib::Error, Nil)).unbox(_lib_box).call(section, error)
^---
Error: no overload matches 'Proc(Gtk::CssSection, GLib::Error, Nil)#call' with types Gtk::CssSection, Pointer(Void)
Overloads are:
- Proc(*T, R)#call(*args : *T)
The generated code:
def connect(handler : Proc(Gtk::CssSection, GLib::Error, Nil), *, after : Bool = false) : GObject::SignalConnection
_box = ::Box.box(handler)
handler = ->(_lib_sender : Pointer(Void), lib_section : Pointer(Void), lib_error : Pointer(Void), _lib_box : Pointer(Void)) {
# Generator::BuiltInTypeArgPlan
section = Gtk::CssSection.new(lib_section, :none)
error = lib_error
::Box(Proc(Gtk::CssSection, GLib::Error, Nil)).unbox(_lib_box).call(section, error)
}.pointer
handler = LibGObject.g_signal_connect_data(@source, name, handler,
GICrystal::ClosureDataManager.register(_box), ->GICrystal::ClosureDataManager.deregister, after.to_unsafe)
GObject::SignalConnection.new(@source, handler)
end
GI-Crystal version: 0.12.0
Crystal version: 1.4.1
The GVariant
target
cannot be passed to Gio::MenuItem#set_action_and_target_value
.
Test code:
require "gtk4"
menu_item = Gio::MenuItem.new(nil, nil)
# Does not work
variant = GLib::Variant.new("test")
menu_item.set_action_and_target_value("app.some_action", variant)
# Also does not work
menu_item.set_action_and_target_value("app.some_action", "test")
# Not even this works
menu_item.set_action_and_target_value("app.some_action", nil)
Exception:
# Exception 1
Unhandled exception: Unable to wrap a Pointer(Void) into a GVariant. (ArgumentError)
from lib/gtk4/lib/gi-crystal/src/bindings/g_lib/variant.cr:20:15 in 'initialize'
from lib/gtk4/lib/gi-crystal/src/bindings/g_lib/variant.cr:5:5 in 'new'
from lib/gtk4/lib/gi-crystal/src/auto/gio-2.0/menu_item.cr:272:7 in 'set_action_and_target_value'
from src/test2.cr:19:1 in '__crystal_main'
from /usr/share/crystal/src/crystal/main.cr:115:5 in 'main_user_code'
from /usr/share/crystal/src/crystal/main.cr:101:7 in 'main'
from /usr/share/crystal/src/crystal/main.cr:127:3 in 'main'
from /lib64/libc.so.6 in '??'
from /lib64/libc.so.6 in '__libc_start_main'
from /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp in '_start'
from ???
# Exception 2
Unhandled exception: Unable to wrap a Pointer(UInt8) into a GVariant. (ArgumentError)
from lib/gtk4/lib/gi-crystal/src/bindings/g_lib/variant.cr:5:5 in 'initialize'
from lib/gtk4/lib/gi-crystal/src/bindings/g_lib/variant.cr:5:5 in 'new'
from lib/gtk4/lib/gi-crystal/src/auto/gio-2.0/menu_item.cr:272:7 in 'set_action_and_target_value'
from src/test2.cr:17:1 in '__crystal_main'
from /usr/share/crystal/src/crystal/main.cr:115:5 in 'main_user_code'
from /usr/share/crystal/src/crystal/main.cr:101:7 in 'main'
from /usr/share/crystal/src/crystal/main.cr:127:3 in 'main'
from /lib64/libc.so.6 in '??'
from /lib64/libc.so.6 in '__libc_start_main'
from /var/home/blobcodes/.cache/crystal/crystal-run-test2.tmp in '_start'
from ???
Gio::MenuItem#set_action_and_target_value
implementation:
def set_action_and_target_value(action : ::String?, target_value : _?) : Nil
# g_menu_item_set_action_and_target_value: (Method)
# @action: (nullable)
# @target_value: (nullable)
# Returns: (transfer none)
# Generator::NullableArrayPlan
action = if action.nil?
Pointer(LibC::Char).null
else
action.to_unsafe
end
# Generator::NullableArrayPlan
target_value = if target_value.nil?
Pointer(Void).null
else
target_value.to_unsafe
end
# Generator::HandmadeArgPlan
target_value = GLib::Variant.new(target_value) unless target_value.is_a?(GLib::Variant)
# C call
LibGio.g_menu_item_set_action_and_target_value(self, action, target_value)
# Return value handling
end
I think the conversion to a GLib::Variant
has to be done before using to_unsafe.
Using a property binding from within a .ui file will result in a GICrystal::ObjectCollectedError being raised.
Here's an example:
require "gtk4"
APP_ID = "com.example.test"
@[Gtk::UiTemplate(file: "src/test.ui")]
class TestWindow < Gtk::ApplicationWindow
include Gtk::WidgetTemplate
@[GObject::Property]
property test = "hello"
def initialize()
super()
end
end
app = Gtk::Application.new(APP_ID, Gio::ApplicationFlags::None)
app.activate_signal.connect do
window = TestWindow.new
window.application = app
window.present
end
exit(app.run(ARGV))
test.ui:
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="TestWindow" parent="GtkApplicationWindow">
<property name="title" bind-source="TestWindow" bind-property="test" bind-flags="sync-create"/>
</template>
</interface>
Output:
Unhandled exception: (GICrystal::ObjectCollectedError)
from src/test.cr:6:1 in '_vfunc_unsafe_get_property'
from src/test.cr:6:1 in '->'
from /lib64/libgobject-2.0.so.0 in 'g_object_get_property'
from /lib64/libgobject-2.0.so.0 in '??'
from /lib64/libgobject-2.0.so.0 in 'g_object_bind_property_full'
from /lib64/libgobject-2.0.so.0 in 'g_object_bind_property'
from /lib64/libgtk-4.so.1 in '??'
from /lib64/libgtk-4.so.1 in 'gtk_builder_extend_with_template'
from /lib64/libgtk-4.so.1 in 'gtk_widget_init_template'
from src/test.cr:7:3 in '_instance_init'
from src/test.cr:6:1 in '->'
from /lib64/libgobject-2.0.so.0 in 'g_type_create_instance'
from /lib64/libgobject-2.0.so.0 in '??'
from /lib64/libgobject-2.0.so.0 in 'g_object_newv'
from lib/gi-crystal/src/bindings/g_object/object.cr:607:18 in 'initialize'
from lib/gi-crystal/src/auto/g_object-2.0/initially_unowned.cr:17:7 in 'initialize'
from lib/gi-crystal/src/auto/gtk-4.0/widget.cr:26:7 in 'initialize'
from lib/gi-crystal/src/auto/gtk-4.0/window.cr:35:7 in 'initialize'
from lib/gi-crystal/src/auto/gtk-4.0/application_window.cr:41:7 in 'initialize'
from src/test.cr:13:5 in 'initialize'
from src/test.cr:12:3 in 'new'
from src/test.cr:20:12 in '->'
from lib/gi-crystal/src/auto/gio-2.0/application.cr:770:44 in '->'
from /lib64/libgobject-2.0.so.0 in 'g_closure_invoke'
from /lib64/libgobject-2.0.so.0 in '??'
from /lib64/libgobject-2.0.so.0 in '??'
from /lib64/libgobject-2.0.so.0 in 'g_signal_emit_valist'
from /lib64/libgobject-2.0.so.0 in 'g_signal_emit'
from /lib64/libgio-2.0.so.0 in '??'
from /lib64/libgio-2.0.so.0 in 'g_application_run'
from lib/gi-crystal/src/auto/gio-2.0/application.cr:544:7 in 'run'
from src/test.cr:26:6 in '__crystal_main'
from /usr/share/crystal/src/crystal/main.cr:129:5 in 'main_user_code'
from /usr/share/crystal/src/crystal/main.cr:115:7 in 'main'
from /usr/share/crystal/src/crystal/main.cr:141:3 in 'main'
from /lib64/libc.so.6 in '??'
from /lib64/libc.so.6 in '__libc_start_main'
from /home/leah/.cache/crystal/crystal-run-test.tmp in '_start'
from ???
The following code:
require "gtk4"
Graphene::Rect.new(
origin: Graphene::Point.zero,
size: Graphene::Size.zero
)
Results in the following compile-time exception:
In lib/gtk4/src/graphene-1.0/rect.cr:45:61
45 | _var = (@pointer + 0).as(Pointer(LibGraphene::Point)).value = value.to_unsafe
^----
Error: no overload matches 'Pointer(LibGraphene::Point)#value=' with type Pointer(Void)
Overloads are:
- Pointer(T)#value=(value : T)
The following code does not compile:
require "gtk4"
snapshot = Gtk::Snapshot.new
size = Graphene::Size.new(148, 405)
rect = Graphene::Rect.new(
origin: Graphene::Point.new(0.0_f32, 0.0_f32),
size: size
)
rounded_rect = Gsk::RoundedRect.new.init_from_rect(
bounds: rect,
radius: 90.0
)
snapshot.append_border(rounded_rect, [10f32, 10f32, 10f32, 10f32], [Gdk::RGBA.new(1.0, 0.0, 0.0, 1.0), Gdk::RGBA.new(1.0, 0.0, 0.0, 1.0), Gdk::RGBA.new(1.0, 0.0, 0.0, 1.0), Gdk::RGBA.new(1.0, 0.0, 0.0, 1.0)])
With the following error message:
In lib/gtk4/lib/gi-crystal/src/auto/gtk-4.0/snapshot.cr:58:56
58 | LibGtk.gtk_snapshot_append_border(self, outline, border_width, border_color)
^-----------
Error: argument 'border_width' of 'LibGtk#gtk_snapshot_append_border' must be Pointer(StaticArray(Float32, 4)), not Pointer(Float32)
I am not able to install the GTK4 dependendies.
$ sudo apt-get install libgtk-4-1 libgirepository1.0-dev gobject-introspection gir1.2-gtk-4.0
[sudo] password for .....
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package libgtk-4-1 is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
E: Package 'libgtk-4-1' has no installation candidate
E: Unable to locate package gir1.2-gtk-4.0
E: Couldn't find any package by glob 'gir1.2-gtk-4.0'
E: Couldn't find any package by regex 'gir1.2-gtk-4.0'
I think it would be useful to be able to use gresource paths with Gtk::UiTemplate
instead of using the file parameter.
I'm seeing awesome progress on this shard <3. I would like to continue my Crystal & GUI app-dev journey and I was thinking if you could update the docs in your site. I see 0.5.0v and looks like it's on 0.7.0 now. Thanks!
The following code:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
app.activate_signal.connect do
2.times { Gtk::Picture.new_for_filename(nil) }
GC.collect
end
exit(app.run(ARGV))
Prints the following error message when run:
~Gtk::Picture at 0x15a2170 - ref count: 1
(crystal-run-test2.tmp:21424): Gtk-WARNING **: 15:54:42.874: A floating object was finalized. This means that someone
called g_object_unref() on an object that had only a floating
reference; the initial floating reference is not owned by anyone
and must be removed with g_object_ref_sink().
Also, this code:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
app.activate_signal.connect do
2.times do
box = Gtk::Box.new
box.append Gtk::Picture.new_for_filename(nil)
end
GC.collect
end
exit(app.run(ARGV))
Even prints a critical error:
~Gtk::Box at 0x124d160 - ref count: 1
~Gtk::Picture at 0x1196170 - ref count: 0
(crystal-run-test2.tmp:22325): GLib-GObject-CRITICAL **: 16:02:42.554: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
I don't know if those are actually two distinct errors, but they seem similar.
Generated code:
def origin=(value : Graphene::Point)
# Property setter
_var = (@pointer + 0).as(Pointer(Void)).value = value.to_unsafe
value
end
Resulting error:
Error: no overload matches 'Pointer(Void)#value=' with type Pointer(Void)
Overloads are:
- Pointer(T)#value=(value : T)
When trying to assign a ui template to a class without any children, you can't just leave out the children field in the annotation.
@[Gtk::UiTemplate(file: "(some path)", children: [] of Symbol)]
class Test < Gtk::Box
end
This works.
@[Gtk::UiTemplate(file: "(some path)")]
class Test < Gtk::Box
end
This raises the following exception:
In lib/gtk4/lib/gtk4/src/bindings/gtk/widget_template.cr:45:58
45 | {% for child in @type.annotation(Gtk::UiTemplate)[:children] %}
^
Error: `for` expression must be an array, hash, tuple, named tuple or a range literal, not NilLiteral:
nil
Using the following code:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
exit(app.run(ARGV))
In the command line (app built as test2 in this example):
[blobcodes@toolbox mangaba]$ ./test2
(test2:23128): GLib-GIO-WARNING **: 16:10:17.211: Your application does not implement g_application_activate() and has no handlers connected to the 'activate' signal. It should do one of these.
⬢[blobcodes@toolbox mangaba]$ ./test2 my_app_name
(my_app_name:23157): GLib-GIO-WARNING **: 16:10:35.782: Your application does not implement g_application_activate() and has no handlers connected to the 'activate' signal. It should do one of these.
⬢[blobcodes@toolbox mangaba]$ ./test2 --help
(--help:23191): GLib-GIO-WARNING **: 16:10:52.199: Your application does not implement g_application_activate() and has no handlers connected to the 'activate' signal. It should do one of these.
⬢[blobcodes@toolbox mangaba]$ ./test2 my_app_name --help
Usage:
my_app_name [OPTION…]
Help Options:
-h, --help Show help options
--help-all Show all help options
--help-gapplication Show GApplication options
Using test2 --help
did not open the help dialog but instead showed in the error message that our app is called --help
.
To fix this, we would need to pass ARGV_UNSAFE
to app.run
, which we cannot because Pointer
is not Enumerable
.
We would need something like this:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
argv_unsafe_as_enumerable = Array.new(ARGC_UNSAFE) do |i|
String.new(ARGV_UNSAFE[i])
end
exit(app.run(argv_unsafe_as_enumerable))
..or:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
exit(app.run([PROGRAM_NAME, *ARGV]))
So the examples need to be changed and it would be useful to be able to pass ARGV_UNSAFE
directly to app.run
. But I don't really know what's the best way to deal with this issue.
When I try running the widget_template_using_resource.cr
example I get this error below. I tried changing the path to "./resource.xml"
or just "resource.xml"
but I get the same error.
avrameisner@AvramsMBPM114 browser % crystal run lib/gtk4/examples/widget_template_using_resource.cr
ld: warning: ignoring duplicate libraries: '-lglib-2.0', '-lgobject-2.0', '-lintl'
Failed to open file “examples/resource.xml”: No such file or directory
Unhandled exception: Error opening file with mode 'r': 'crystal-gio-resource.gresource': No such file or directory (File::NotFoundError)
from /opt/homebrew/Cellar/crystal/1.11.1/share/crystal/src/crystal/system/unix/file.cr:12:7 in 'open'
from /opt/homebrew/Cellar/crystal/1.11.1/share/crystal/src/file.cr:162:5 in 'new'
from /opt/homebrew/Cellar/crystal/1.11.1/share/crystal/src/file.cr:725:5 in 'read'
from /opt/homebrew/Cellar/crystal/1.11.1/share/crystal/src/file.cr:739:3 in 'read'
from lib/gtk4/examples/widget_template_using_resource.cr:25:12 in '__crystal_main'
from /opt/homebrew/Cellar/crystal/1.11.1/share/crystal/src/crystal/main.cr:129:5 in 'main_user_code'
from /opt/homebrew/Cellar/crystal/1.11.1/share/crystal/src/crystal/main.cr:115:7 in 'main'
from /opt/homebrew/Cellar/crystal/1.11.1/share/crystal/src/crystal/main.cr:141:3 in 'main'
My specs: M1 MacBook Pro with macOS Sonoma 14.2.1 (23C71)
Edit: I realize this is most likely an issue with gi-crystal, but I'll leave the issue here as it also affects this repo as well
after cloning the repo and running shards install
, running ./bin/gi-crystal
gives the following output:
info - Starting at 2023-10-21 23:09:45 -05:00, project dir: /home/conner/git-projects/gtk4.cr
info - Gi-Crystal version 0.19.0, built with Crystal 1.9.2.
info - Generating bindings at /home/conner/git-projects/gtk4.cr/lib/gi-crystal/src/auto
info - Using binding config at /home/conner/git-projects/gtk4.cr/src/bindings/gdk/binding.yml
info - Using binding config at /home/conner/git-projects/gtk4.cr/src/bindings/gsk/binding.yml
info - Using binding config at /home/conner/git-projects/gtk4.cr/src/bindings/gtk/binding.yml
info - Using binding config at /home/conner/git-projects/gtk4.cr/lib/gi-crystal/src/bindings/g_lib
/binding.yml
info - Using binding config at /home/conner/git-projects/gtk4.cr/lib/gi-crystal/src/bindings/g_obj
ect/binding.yml
info - Using binding config at /home/conner/git-projects/gtk4.cr/lib/harfbuzz/src/bindings/harfbuz
z/binding.yml
info - Using binding config at /home/conner/git-projects/gtk4.cr/lib/gio/src/bindings/gio/binding.
yml
info - Using binding config at /home/conner/git-projects/gtk4.cr/lib/pango/src/bindings/pango/bind
ing.yml
info - Gdk - No binding config found for cairo-1.0.
info - Gdk - No binding config found for PangoCairo-1.0.
info - Gdk - No binding config found for GdkPixbuf-2.0.
warn - Gdk::TimeCoord axes field - Unknown conversion to crystal for fixed size array.
info - HarfBuzz - No binding config found for freetype2-2.0.
warn - HarfBuzz - Interface constant not supported.
warn - g_cancellable_connect - Callback without user_data!
warn - Gio::ActionEntry padding field - Unknown conversion to crystal for fixed size array.
warn - Gio::DBusInterfaceVTable padding field - Unknown conversion to crystal for fixed size array
.
warn - Gio::DBusSubtreeVTable padding field - Unknown conversion to crystal for fixed size array.
info - GdkPixbuf - No binding config found for GModule-2.0.
warn - gdk_pixbuf_get_options - Unknown conversion to crystal for GHash
info - Gsk - No binding config found for Graphene-1.0.
warn - gsk_border_node_get_widths - Unknown conversion to crystal for fixed size array.
warn - Boxed not working for enums
warn - Gtk::BuildableParser padding field - Unknown conversion to crystal for fixed size array.
syntax error in './gsk-4.0/path_point.cr:122:13': can't use variable name 'center' inside assignme
nt to variable 'center'
fatal - Error formating generated files at '/home/conner/git-projects/gtk4.cr/lib/gi-crystal/src/a
uto'.
Most notable being the actual error:
`syntax error in './gsk-4.0/path_point.cr:122:13': can't use variable name 'center' inside assignment to variable 'center'
fatal - Error formating generated files at '/home/conner/git-projects/gtk4.cr/lib/gi-crystal/src/auto'.`
I'm not sure if I have improper versions of programs or what, I can provide extra information as needed!
crystal: 1.9.2
gtk+4: 4.13.0
gobject-introspection: 1.72.0
On Collision I use the --sourcedir
flag while compiling resources with glib-compile-resources
. Should register_resource
accept an extra parameter for it or maybe just an extra parameter for extra flags?
eg.
macro register_resource(resource_file, flags)
{%
`glib-compile-resources #{flags} --target crystal-gio-resource.gresource #{resource_file}`
...
register_resource("./data.gresource.xml", "--sourcedir data")
Would be nice to have github actions to run the tests like https://github.com/hugopl/gi-crystal, this would also allow the project to be listed into https://github.com/veelenga/awesome-crystal.
The only reason I didn't do this before is because the default ubuntu image used on github actions doesn't have a GTK4 package OR I didn't tried enough.
If you want to help the project this is a good start, to run the tests you need to:
./bin/gi-crystal
to generate the bindings.crystal spec
Currently, the documentation at https://hugopl.github.io/gtk4.cr/ is made "manually", I mean... every time I need to update it I do:
git checkout gh-pages
git rm doc
git merge WHATEVER_NEW_VERSION
make doc
git add doc
git push origin gh-pages
These are simple and static steps that can be automatized by a github actions that runs every time a new tag is created in the repository.
Working on interactive constraints and trying to call methods
to get the point where the drag started
def start_point(x : Float64 | Nil, y : Float64| Nil) : Bool
end
and to get the offset from the start point.
def offset(x : Float64 | Nil, y : Float64 | Nil ) : Bool
If the gesture is active, this function returns true and fills in x and y with the drag start coordinates, in surface-relative coordinates
These x,y parameters are passed by value and not by reference, so not sure how these x,y values are returned.
The compiler complains with error
In lib/gi-crystal/src/auto/gtk-4.0/gesture_drag.cr:115:21
115 | Float64.null
Using the following code:
require "gtk4"
app = Gtk::Application.new("com.example.issue", Gio::ApplicationFlags::None)
app.activate_signal.connect do
window = Gtk::ApplicationWindow.new(app)
box = Gtk::Box.new
window.child = box
label = Gtk::Label.new(label: "Test!")
box.append(label)
box.remove(label)
window.present
GLib.timeout_seconds(5) do
GC.collect
false
end
end
app.run(ARGV)
Results in the following output (using -Ddebugmemory
):
~Gtk::ApplicationWindow at 0x18f0350 - ref count: 2
~Gtk::Box at 0x183f180 - ref count: 1
(crystal-run-test2.tmp:54281): Gtk-CRITICAL **: 22:26:25.976: GtkBox 0x183f180 has a parent GtkApplicationWindow 0x18f0350 during dispose. Parents hold a reference, so this should not happen.
Did you call g_object_unref() instead of gtk_widget_unparent()?
~Gtk::Label at 0x18c6180 - ref count: 0
(crystal-run-test2.tmp:54281): GLib-GObject-CRITICAL **: 22:26:25.976: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
(crystal-run-test2.tmp:54281): Gtk-CRITICAL **: 22:26:28.854: gtk_accessible_get_accessible_role: assertion 'GTK_IS_ACCESSIBLE (self)' failed
[continues like this]
..while making the program unstable (ex. header bar disappears or buttons in gtk inspector move to random places):
I'm not sure if this is outside the scope of this repo but would be nice to have!
I don't know if gi-crystal is able to generate bindings for it (I couldn't manage to create them from a quick try).
Additional context:
I have to migrate Hashbrown to GTK 4 and need to follow GNOME HIG. Currently, with crystal-gobject I'm able to use libhandy widgets (through Glade ui files at least), but using gtk4.cr I get the following (expected) error:
Gtk-ERROR **: 11:52:58.647: failed to add UI from file /home/geopjr/projects/gtk4.cr/examples/adw_test.ui: /home/geopjr/projects/gtk4.cr/examples/adw_test.ui:12:1 Invalid object type 'AdwAvatar'
Hi!
I'm completely new to Crystal, so this might be (or probably, rather) an issue on my end. However, when I try installing gtk4.cr in my project, I get the error in the screenshot:
Steps:
shards init
shard.yml
looks like this:name:
version: 0.1.0
dependencies:
gtk4:
github: hugopl/gtk4.cr
shards install
Thanks in advance!
Cheers!
Also completely unrelated, but I thought I'd ask since you seem like a Crystal expert :D What's your IDE of choice for Crystal development? I couldn't get nvim to syntax highlight, so VScodium with the Crystal-lang extension seems to be the only option. I saw you were working on your own IDE as well, but that's still relatively early in development.
Thanks again!!
Currently, when generating the docs, the Gtk::Widget
page ends like this:
This is the related code:
# </object>
# </child>
# </object>
# ```
#
# `GtkWidget` allows style information such as style classes to
# be associated with widgets, using the custom `<style>` element:
#
# ```xml
So it seems like the <style>
element is actually interpreted as an html style element.
When trying to clone this git repo, I see the following on the terminal:
⬢[blobcodes@toolbox ~]$ time git clone https://github.com/hugopl/gtk4.cr.git
Cloning into 'gtk4.cr'...
remote: Enumerating objects: 46671, done.
remote: Counting objects: 100% (4742/4742), done.
remote: Compressing objects: 100% (369/369), done.
remote: Total 46671 (delta 4297), reused 4741 (delta 4297), pack-reused 41929
Receiving objects: 100% (46671/46671), 89.76 MiB | 9.54 MiB/s, done.
Resolving deltas: 100% (42290/42290), done.
real 0m18.722s
user 0m51.873s
sys 0m1.766s
The repo is just under 90 MB big, which will be downloaded everytime I use shards update
. As you can see, this takes over 18 seconds, which is a lot for such a simple shard. Also I don't have the worst internet connection, others will probably need even more time.
I think this is because when cloning the repo, you download every version of the docs in the gh-pages
branch. That is not really necessary and I would recommend either using GitLab CI so the docs do not need to be uploaded to the repo or to automatically purge the gh-pages
branch.
Same as #5, but for Gdk::Paintable__Impl
I don't know if this is because of gtk4.cr
or because of gtk
itself, but the following code does not actually set can_target
to false
:
require "gtk4"
app = Gtk::Application.new("test.test", Gio::ApplicationFlags::None)
app.activate_signal.connect do
window = Gtk::Window.new(application: app)
# Does not work
window.child = Gtk::Button.new(can_target: false)
# Does work
# window.child = Gtk::Button.new.tap { |btn| btn.can_target = false }
window.present
end
exit(app.run(ARGV))
can_target
is not the only variable where this happens, the variables can_focus
and focusable
also have the same issue.
In this example code:
require "gtk4"
app = Gtk::Application.new("test.application", Gio::ApplicationFlags::None)
app.activate_signal.connect do
window = Gtk::ApplicationWindow.new(app)
texture = Gdk::Texture.new_from_filename("/path/to/image.png")
window.child = Gtk::Picture.new_for_paintable(texture)
window.present
end
exit(app.run(ARGV))
Using a Gdk::Texture created using the new_from_filename method doesn't work, printing the following messages to the console:
(crystal-run-test2.tmp:59108): Gdk-CRITICAL **: 01:54:55.937: gdk_texture_new_from_filename: assertion 'error == NULL || *error == NULL' failed
#<Gdk::Texture:0x7f906d33bde0>
(crystal-run-test2.tmp:59108): Gdk-CRITICAL **: 01:54:55.937: gdk_texture_get_height: assertion 'GDK_IS_TEXTURE (texture)' failed
I already found a fix, but there are lots of methods which have this issue, it probably needs a fix in gi-crystal.
The Gdk4 docs mention that this method requires an additional GError argument, which this library does not pass to the method.
Generated bindings:
lib LibGdk
fun gdk_texture_new_from_filename(path : Pointer(LibC::Char)) : Pointer(Void)
end
class Gdk::Texture
def self.new_from_filename(path : ::String) : Gdk::Texture
# gdk_texture_new_from_filename: (Constructor | Throws)
# Returns: (transfer full)
_retval = LibGdk.gdk_texture_new_from_filename(path)
Gdk::Texture.new(_retval, GICrystal::Transfer::Full)
end
end
My modifications, which work:
lib LibGdk
fun gdk_texture_new_from_filename(path : Pointer(LibC::Char), error : Void**) : Pointer(Void)
end
class Gdk::Texture
def self.new_from_filename(path : ::String) : Gdk::Texture
ptr = Pointer(Void).null
_retval = LibGdk.gdk_texture_new_from_filename(path, pointerof(ptr))
raise GLib::Error.new(ptr, GICrystal::Transfer::Full).message unless ptr.null?
Gdk::Texture.new(_retval, GICrystal::Transfer::Full)
end
end
I am working on a Gtk4 Clipboard example and need to call the method clipboard.read_value_finish
@[GObject::Virtual]
def activate
ui = get_ui()
builder = Gtk::Builder.new_from_string(ui, ui.bytesize.to_i64)
window = Gtk::Window.cast(builder["window"])
text_entry = Gtk::Entry.cast(builder["source_text"])
text_entry.changed_signal.connect do
text_entry_changed(text_entry)
end
source_file = Gtk::Button.cast(builder["source_file"])
copy_button = Gtk::Button.cast(builder["copy_button"])
paste_button = Gtk::Button.cast(builder["paste_button"])
combobox = Gtk::DropDown.cast(builder["source_chooser"])
source_stack = Gtk::Stack.cast(builder["source_stack"])
dest_stack = Gtk::Stack.cast(builder["dest_stack"])
source_clipboard = source_stack.clipboard
visible_child_name = ""
copy_button.clicked_signal.connect do
visible_child = source_stack.visible_child
visible_child_name = source_stack.visible_child_name
case visible_child_name
when "Text"
text = visible_child.as(Gtk::Editable).text
source_clipboard.set(text)
when "Color"
when "Image"
when "File"
end
end
paste_button.clicked_signal.connect do
dest_clipboard = dest_stack.clipboard
case visible_child_name
when "Text"
result = Gio::AsyncResult.new # ?????
value = source_clipboard.read_value_finish(result)
if value
dest_stack.set_visible_child_name = "Text"
child = dest_stack.visible_child
child.as(Gtk::Label).text = value
end
end
end
window.application = self
window.present
end
Problem I have is that the argument result has to be of type Gio::AsyncResult which is a module
Platform
Linux 21
Crystal 1.9.2 [1908c816f] (2023-07-19)
libgtk-4-1
Using Gtk::TextBuffer#selection_content
returns a Gdk::ContentProvider
object, this object can return its contents wrapped in a GValue, however the called must initialize the GValue to the right type before calling Gdk::ContentProvider#value
, otherwise an error is thrown when e.g. a call for #as_s
is done in the returned GValue:
Unhandled exception: Cannot obtain raw value for g_type 0 (ArgumentError)
from lib/gi-crystal/src/bindings/g_object/value.cr:113:9 in 'raw'
from lib/gi-crystal/src/bindings/g_object/value.cr:120:7 in 'raw'
from lib/gi-crystal/src/bindings/g_object/value.cr:123:5 in 'as_s'
Current Gdk::ContentProvider#value generated binding implementation:
def value : GObject::Value
# gdk_content_provider_get_value: (Method | Throws)
# @value: (out) (caller-allocates)
# Returns: (transfer none)
_error = Pointer(LibGLib::Error).null
# Generator::CallerAllocatesPlan
value = GObject::Value.new
# C call
_retval = LibGdk.gdk_content_provider_get_value(to_unsafe, value, pointerof(_error))
# Error check
Gdk.raise_gerror(_error) unless _error.null?
# Return value handling
value
end
Note that the return value is also lost... i.e. I need to decide what to do with the tons of out values the GTK API has.
Gtk4 and 3 share a lot of dependencies in common, to avoid repeat the work already done in this shard on gtk3 shard, common dependencies must be moved to their own shards.
Binding that don't need (yet) a bindings.yml
file don't need a shard.
Hi, I am trying to make a GUI game engine with Crystal, and I wanted to use this library for GTK bindings. I am not trying to make an efficient 2-D/3-D engine, just one that works with simple design. I was browsing the docs for seeing where what is defined where and a lot come up with 404. I was wondering if it was normal: https://hugopl.github.io/gtk4.cr/
Thank you, otherwise keep up the great work! I love the idea of this project, regardless if I use GTK for my Game Engine.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.