jhass / crystal-gobject

gobject-introspection for Crystal
BSD 3-Clause "New" or "Revised" License
127 stars 13 forks source link

Missing support for after signals. #68

Closed hugopl closed 4 years ago

hugopl commented 4 years ago

In C we can connect signals using the macro g_signal_connect_after, that just connect the signal using the AFTER flag.

This is useful to e.g. connect focus-out-event signal, so your code is ran after the default signal handlers and some GTK widgets get happy with this.

My suggestion if to use a after_ prefix to connect these signals, e.g. for GtkWidget focus-out-event we would have on_focus_out_event and after_focus_out_event.

I tried to mimic this with current signals implementation but got no luck on my first tries:

require "gobject/gtk/autorun"

module Gtk
  class Widget
    def after_focus_out_event(&block : FocusOutEventSignal)
      __var0 = ->(arg0 : LibGtk::Widget*, arg1 : LibGdk::EventFocus*, box : Void*) {
        ::Box(FocusOutEventSignal).unbox(box).call(Widget.new(arg0), arg1.null? ? GObject.raise_unexpected_null("event in focus_out_event") : Gdk::EventFocus.new(arg1))
      }

      __var1 = ::Box.box(FocusOutEventSignal.new { |arg0, arg1|
        block.call(arg0, arg1)
      })
      LibGObject.signal_connect_data(@pointer.as(LibGObject::Object*), "focus-out-event", LibGObject::Callback.new(__var0.pointer, Pointer(Void).null), GObject::ClosureDataManager.register(__var1), ->GObject::ClosureDataManager.deregister, GObject::ConnectFlags::AFTER)
    end
  end
end

def hey(widget, event)
  puts "hey"
  false
end

def ho(widget, event)
  puts "ho"
  true
end

window = Gtk::Window.new(title: "Enter something!", border_width: 10)
window.connect "destroy", &->Gtk.main_quit
box = Gtk::Box.new(orientation: :vertical, spacing: 6)
window.add(box)

entry1 = Gtk::Entry.new
entry1.on_focus_out_event(&->hey(Gtk::Widget, Gdk::EventFocus))
entry1.after_focus_out_event(&->ho(Gtk::Widget, Gdk::EventFocus))

entry2 = Gtk::Entry.new

box.add(entry1)
box.add(entry2)

window.show_all

This was trying to mimic this C code:

#include <gtk/gtk.h>
#include <gtksourceview/gtksource.h>

gboolean focus_out1(GtkWidget *widget, GdkEvent *event,gpointer user_data) {
  printf("hey\n");
  return 0;
}

gboolean focus_out2(GtkWidget *widget, GdkEvent *event,gpointer user_data) {
  printf("ho\n");
  return 1;
}

int main(int argc, char *argv[]) {
  gtk_init(&argc, &argv);
  gtk_source_init();

  GtkWidget* wnd = gtk_window_new(0);
  g_signal_connect(wnd, "destroy", G_CALLBACK(gtk_main_quit), wnd);

  GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
  gtk_container_add(GTK_CONTAINER(wnd), GTK_WIDGET(box));

  GtkWidget* entry1 = gtk_entry_new();
  GtkWidget* entry2 = gtk_entry_new();

  g_signal_connect_data(entry1, "focus-out-event", G_CALLBACK(focus_out1), 0, 0, 0);
  g_signal_connect_data(entry1, "focus-out-event", G_CALLBACK(focus_out2), 0, 0, G_CONNECT_AFTER);
  // g_signal_connect(entry1, "focus-out-event", G_CALLBACK(focus_out1), entry1);
  // g_signal_connect_after(entry1, "focus-out-event", G_CALLBACK(focus_out2), entry1);

  gtk_container_add(GTK_CONTAINER(box), entry1);
  gtk_container_add(GTK_CONTAINER(box), entry2);

  // g_signal_connect(wnd, )

  gtk_widget_show_all(GTK_WIDGET(wnd));
  gtk_main();  
  gtk_source_finalize();
}
jhass commented 4 years ago

So adding overloads for after is the easy part.

Your example doesn't work because something seems to clobber the return value :( If you add false into either proc inside the on_ wrapper method, it starts working. If you then do so much and assign the original value of the call to a local it breaks again. Can the calling conventions be so different for C functions and Crystal procs? :/

jhass commented 4 years ago

Ooh, found it. Crystal generates Bool as i1 but gboolean is i32...

hugopl commented 4 years ago

yeah, I got those crashes too.