jhass / crystal-gobject

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

How to inherit from a Gtk::Application ? #16

Closed megatux closed 5 years ago

megatux commented 6 years ago

Hi, I'm translating some examples from Ruby-gnome2 bindings. The example Apps dir contains classes that inherits from Gtk::Application . I tried this:

require "gtk"

class ExampleAppWindow < Gtk::ApplicationWindow
  def open(file)
  end
end

class ExampleApp < Gtk::Application

  def initialize
    super   #   <----- error

    on_activate do |application|
      window = ExampleAppWindow.new(application)
      window.present
    end
...
end

I got:

wrong number of arguments for 'Gtk::Application#initialize' (given 0, expected 1)
Overloads are:
 - Gtk::Application#initialize(pointer : ::Pointer(LibGtk::Application))

    super
    ^~~~~
jhass commented 6 years ago

I guess you could use super Gtk::Application.new("org.crystal.sample", Gio::ApplicationFlags::FLAGS_NONE).to_unsafe.as LibGtk::Application* for now

megatux commented 6 years ago

That works but can not move forward a lot because I can not instantiate a custom inherited Windows class:

require "gtk"

class ExampleAppWindow < Gtk::ApplicationWindow
end

class ExampleApp < Gtk::Application
  def initialize
    super Gtk::Application.new("org.crystal.sample", Gio::ApplicationFlags::FLAGS_NONE).to_unsafe.as LibGtk::Application* 

    on_activate do |application|
      window = ExampleAppWindow.new(application)
      window.present
    end
  end
end

app = ExampleApp.new
puts app.run(ARGC_UNSAFE, ARGV_UNSAFE)

There is an error in ExampleAppWindow.new(application) I don't understand.

megatux commented 6 years ago

The error is this one:

$ crystal samples/tutorial/example_app1/example_app1.cr 
Error in samples/tutorial/example_app1/example_app1.cr:54: instantiating 'ExampleApp:Class#new()'

app = ExampleApp.new
                ^~~

in samples/tutorial/example_app1/example_app1.cr:34: instantiating 'ExampleAppWindow:Class#new(Gio::Application+)'

      window = ExampleAppWindow.new(application)
                                ^~~

in src/generated/gtk/application_window.cr:26: instantiating 'cast(Gtk::Widget)'

      cast Gtk::Widget.new(__return_value)
      ^~~~

in samples/tutorial/example_app1/example_app1.cr:24: expanding macro

class ExampleAppWindow < Gtk::ApplicationWindow
^

in macro 'inherited' expanded macro: included:1, line 3:

  1.         # Add run-time check
  2.         def self.cast(object) : self
>  3.           new(object.to_unsafe.as(LibExampleAppWindow*))
  4.         end
  5.       

undefined constant LibExampleAppWindow (did you mean 'ExampleAppWindow')
mvz commented 6 years ago

@megatux it looks like crystal-gobject doesn't support user-defined gobject types yet. Is it possible to rewrite the example without inheritance?

megatux commented 6 years ago

It's absolutely possible, I just wanted to stay as close as possible to the Ruby-Gtk+ binding examples.

ppibburr commented 5 years ago

add inherit_gobject macro to wrapped_type.cr
its use is explicit.

module GObject
  module WrappedType
    macro included
      macro inherited
        # Add run-time check
        def self.cast(object) : self
          new(object.to_unsafe.as(Lib\{{@type}}*))
        end
      end

      macro inherit_gobject
        # Add run-time check
        def self.cast(object) : self
          new(object.to_unsafe.as(Lib\{{@type.superclass}}*))
        end 
      end

      # Add run-time check
      def self.cast(object) : self
        new(object.to_unsafe.as(Lib{{@type}}*))
      end
    end
  end
end

and the example runs.

class ExampleAppWindow < Gtk::ApplicationWindow
  inherit_gobject
end

class ExampleApp < Gtk::Application
  inherit_gobject

  def initialize(ptr)
    super

    on_activate do |application|
      window = ExampleAppWindow.new(self)
      window.present
    end
  end

  def self.new(id, flags)
    super
  end
end

app = ExampleApp.new("org.crystal.sample", Gio::ApplicationFlags::FLAGS_NONE)
puts app.run(ARGC_UNSAFE, ARGV_UNSAFE)
jhass commented 5 years ago

Mh, I'm not convinced since this only works one level down. Maybe we can try to check for the presence of Lib\{{@type}} and chase up the inheritance chain until we find an existing constant? That is in the inherited macro, so it would work seamlessly. Not sure it's possible right now with the macro language (checking for a constants presence).

ppibburr commented 5 years ago

This seems to work.

wrapped_type.cr

module GObject
  module WrappedType
    macro _g_if_defined?(path, &blok)
      {% if path.resolve? %}
        {{blok.body}}
      {% else %}
         super
      {% end %}
    end # === macro _g_if_defined

    macro included
      macro inherited
        # Add run-time check
        def self.cast(object) : self
          _g_if_defined?(::Lib\{{@type}}) do
            return self.new(object.to_unsafe.as(Lib\{{@type}}*))
          end
        end
      end

      # Add run-time check
      def self.cast(object) : self
        new(object.to_unsafe.as(Lib{{@type}}*))
      end
    end
  end
end
jhass commented 5 years ago

@ppibburr Thanks that gave me the right thinking, I went for a slight variation of that :)

I hate how you have to redefine the self.new overloads when you subclass and override initialize, maybe we can provide some kind of after_initialize hook or so to avoid that, but that's for another time.

megatux commented 5 years ago

Glad to see this issue fixed. I'll resume my tests examples.