diff --git a/samples/gtk_keep_wrapper.cr b/samples/gtk_keep_wrapper.cr new file mode 100644 index 00000000..14653c7f --- /dev/null +++ b/samples/gtk_keep_wrapper.cr @@ -0,0 +1,42 @@ +require "../src/gtk/autorun" + +class MyWidget < Gtk::Button + @foo : Int32? + + def instantiate + self.label = "Default Label" + @foo = MyWidget.next_foo + + keep_wrapper + end + + @@foo = -1 + def self.next_foo + @@foo += 1 + end + + def foo + @foo + end +end + +w = Gtk::Window.new +w.add v=Gtk::VBox.new(false,0) + +3.times do |i| + v.pack_start(mw=MyWidget.new, false, false, 1) + + mw.label = "button#{i}" + + mw.on_clicked do |b| + p b.as(MyWidget).foo + v.foreach do |c| p c.as(MyWidget).foo end + end +end + +v.foreach do |c| p c end + +w.connect "delete-event", ->Gtk.main_quit + +w.show_all + diff --git a/samples/gtk_subclasses.cr b/samples/gtk_subclasses.cr index 736f8360..b167200d 100644 --- a/samples/gtk_subclasses.cr +++ b/samples/gtk_subclasses.cr @@ -1,13 +1,7 @@ require "../src/gtk" class MyWindow < Gtk::ApplicationWindow - def self.new(app : Gtk::Application) - super - end - - def initialize(ptr) - super(ptr) - + def instantiate self.border_width = 10 self.title = "Hello" add Gtk::Label.new("Hello") @@ -15,13 +9,7 @@ class MyWindow < Gtk::ApplicationWindow end class MyApp < Gtk::Application - def self.new(id, flags : Gio::ApplicationFlags) - super - end - - def initialize(ptr) - super(ptr) - + def instantiate on_activate do |application| window = MyWindow.new(self) window.connect "destroy", &->quit @@ -31,4 +19,4 @@ class MyApp < Gtk::Application end app = MyApp.new("org.crystal.mysample", :flags_none) -app.run \ No newline at end of file +app.run diff --git a/src/g_i_repository/info/callable_info.cr b/src/g_i_repository/info/callable_info.cr index c6095e87..2b51519f 100644 --- a/src/g_i_repository/info/callable_info.cr +++ b/src/g_i_repository/info/callable_info.cr @@ -19,7 +19,7 @@ module GIRepository end def convert_to_crystal(variable) - "#{@definition}.new(#{variable})" + "#{@definition}.cast(#{variable})" end def convert_from_crystal(variable) diff --git a/src/g_i_repository/info/function_info.cr b/src/g_i_repository/info/function_info.cr index 285bf412..39eb7f82 100644 --- a/src/g_i_repository/info/function_info.cr +++ b/src/g_i_repository/info/function_info.cr @@ -60,8 +60,11 @@ module GIRepository io << "\n#{indent} GLib::Error.assert __error" if throws? unless skip_return? - io << "\n#{indent} #{"cast " if constructor?}#{return_type.convert_to_crystal("__return_value")}" + io << "\n#{indent} ins=#{"cast " if constructor?}#{return_type.convert_to_crystal("__return_value")}" io << " if __return_value" if may_return_null? + io << "\n#{indent} ins" + io << ".as(self)" if constructor? + io << " if __return_value" if may_return_null? io << '\n' else io << "\n#{indent} nil\n" diff --git a/src/g_i_repository/info/type_info.cr b/src/g_i_repository/info/type_info.cr index 4be19c3c..57d28cfc 100644 --- a/src/g_i_repository/info/type_info.cr +++ b/src/g_i_repository/info/type_info.cr @@ -101,7 +101,9 @@ module GIRepository when LibGIRepository::TypeTag::INTERFACE interface = self.interface case interface - when ObjectInfo, StructInfo, UnionInfo + when ObjectInfo + "#{interface.full_constant}.cast(#{variable})" + when StructInfo, UnionInfo "#{interface.full_constant}.new(#{variable})" else variable @@ -128,7 +130,7 @@ module GIRepository def convert_from_crystal(variable) case tag when LibGIRepository::TypeTag::INTERFACE - pointer? ? "#{variable}.to_unsafe.as(Lib#{interface.full_constant}*)" : variable + pointer? ? "#{variable}.void_pointer.as(Lib#{interface.full_constant}*)" : variable when LibGIRepository::TypeTag::ARRAY, LibGIRepository::TypeTag::GLIST, LibGIRepository::TypeTag::GSLIST, diff --git a/src/g_i_repository/wrapper_generator.cr b/src/g_i_repository/wrapper_generator.cr index ec8c1b32..ca27db33 100644 --- a/src/g_i_repository/wrapper_generator.cr +++ b/src/g_i_repository/wrapper_generator.cr @@ -7,13 +7,13 @@ module GIRepository def write_constructor(libname, io, indent="") io.puts "#{indent} @pointer : Void*" io.puts "#{indent} def initialize(pointer : #{ptr_type(libname)})" - io.puts "#{indent} @pointer = pointer.as(Void*)" + io.puts "#{indent} @pointer = pointer.as(Void*)" io.puts "#{indent} end" io.puts end def write_to_unsafe(libname, io, indent="") - io.puts "#{indent} def to_unsafe" + io.puts "#{indent} def to_unsafe : #{ptr_type(libname)}" io.puts "#{indent} @pointer.not_nil!.as(#{ptr_type(libname)})" io.puts "#{indent} end" io.puts diff --git a/src/g_object/wrapped.cr b/src/g_object/wrapped.cr new file mode 100644 index 00000000..b655f9b4 --- /dev/null +++ b/src/g_object/wrapped.cr @@ -0,0 +1,102 @@ +module GObject + module WrappedType + GOBJECT_IS_WRAPPED_TAG = "__crystal_gobject_wrapper_id__" + + @@n_wrapped = UInt64.new(-1) + @@wrapped = Hash(UInt64?, GObject::WrappedType?).new + + def self.set_wrapped(ins) + @@n_wrapped += 1 + + ins.set_data_full(GOBJECT_IS_WRAPPED_TAG, Pointer(UInt64).new(@@n_wrapped+1).as(Void*), ->(d : Void*){ + GObject::WrappedType.unwrap(d.address-1) + nil + }) + + @@n_wrapped + end + + def self.keep_wrapper(ins) + if ins.has_wrapper? + id = ins.wrapper_id + else + id = set_wrapped ins + end + + @@wrapped[id] = ins + end + + def self.unwrap(id : UInt64) + @@wrapped.delete id + end + + def self.wrapper(id : UInt64?) + @@wrapped[id] + rescue KeyError + nil + end + + def self.wrapper(ptr) + if id=wrapper_id(ptr.as(LibGObject::Object*)) + wrapper(id) + end + end + + def self.wrapper_id(ptr) : UInt64? + addr = LibGObject.object_get_data(ptr.as(LibGObject::Object*), GObject::WrappedType::GOBJECT_IS_WRAPPED_TAG).address + if addr >= 0 + addr-1 + else + nil + end + end + + def self.has_wrapper?(id : Int) + @@wrapped.has_key?(id) + end + + def self.has_wrapper?(ptr) + @@wrapped.has_key?(wrapper_id(ptr.as(LibGObject::Object*))) + end + + def set_data_full(key, data, cb) + LibGObject.object_set_data_full(@pointer.as(LibGObject::Object*), key.to_unsafe, data ? data : nil,cb) + data + end + + def set_wrapped + return if !is_a?(GObject::Object) + unless has_wrapper? + GObject::WrappedType.set_wrapped self + end + end + + def has_wrapper? + return unless is_a?(GObject::Object) + GObject::WrappedType.has_wrapper?(@pointer.as(LibGObject::Object*)) + end + + def wrapper_id + if has_wrapper? + GObject::WrappedType.wrapper_id(@pointer.as(LibGObject::Object*)) + end + end + + def keep_wrapper + GObject::WrappedType.keep_wrapper self + end + + def wrapper + GObject::WrappedType.wrapper(self) + end + + def instantiate + raise "foo" + end + end +end + +lib LibGObject + alias DestroyNotify = Void* -> Void + fun object_set_data_full = g_object_set_data_full(this : LibGObject::Object*, key : UInt8*, data : Void*, closure : LibGObject::DestroyNotify) : Void +end diff --git a/src/g_object/wrapped_type.cr b/src/g_object/wrapped_type.cr index c5652dc7..029152f7 100644 --- a/src/g_object/wrapped_type.cr +++ b/src/g_object/wrapped_type.cr @@ -1,22 +1,54 @@ +require "./wrapped.cr" + module GObject - module WrappedType - macro def_cast(libtype) + module WrappedType + def instantiate() + end + + macro init(&b) + def instantiate + {{b.body}} + end + end + + macro def_cast(libtype, type) {% if libtype.resolve? %} - def self.cast(object) : self - new(object.to_unsafe.as({{libtype}}*)) + def self.cast(object : GObject::WrappedType) : self + if object.has_wrapper? + if ins = object.wrapper + return ins.as(self) + end + end + + ins = new(object.void_pointer.as({{libtype}}*)).as(self) + ins.set_wrapped + ins.instantiate + ins end + + def self.cast(ptr : Pointer({{libtype}})) + if ins=GObject::WrappedType.wrapper(ptr.as(LibGObject::Object*)) + return ins.as(self) + end + + new(ptr).as(self) + end {% end %} end + def void_pointer + @pointer + end + macro included macro inherited \{% begin %} - def_cast(Lib\{{@type}}) + def_cast(Lib\{{@type}}, \{{@type}}) \{% end %} end \{% begin %} - def_cast(Lib\{{@type}}) + def_cast(Lib\{{@type}}, \{{@type}}) \{% end %} end end diff --git a/src/generated/g_object/type_info.cr b/src/generated/g_object/type_info.cr index fbd8ef77..762f0818 100644 --- a/src/generated/g_object/type_info.cr +++ b/src/generated/g_object/type_info.cr @@ -23,7 +23,7 @@ module GObject @pointer = pointer.as(Void*) end - def to_unsafe + def to_unsafe : LibGObject::TypeInfo* @pointer.not_nil!.as(LibGObject::TypeInfo*) end @@ -104,7 +104,7 @@ module GObject end def value_table=(value : GObject::TypeValueTable) - to_unsafe.as(LibGObject::TypeInfo*).value.value_table = value.to_unsafe.as(LibGObject::TypeValueTable*) + to_unsafe.as(LibGObject::TypeInfo*).value.value_table = value.void_pointer.as(LibGObject::TypeValueTable*) end end diff --git a/src/gtk/gtk.cr b/src/gtk/gtk.cr index b7fbbd41..bc57e461 100644 --- a/src/gtk/gtk.cr +++ b/src/gtk/gtk.cr @@ -73,4 +73,24 @@ module Gtk run ARGC_UNSAFE, ARGV_UNSAFE end end -end \ No newline at end of file +end + +class Gtk::Container + def children + a = [] of Gtk::Widget + + foreach(-> (c : LibGtk::Widget*, d : Void*) { + d.as(Array(Gtk::Widget)*).value << Gtk::Widget.cast(c) + nil + }, pointerof(a).as(Void*)) + + a + end + + def foreach(&cb : Gtk::Widget -> _) + foreach(-> (c : LibGtk::Widget*, d : Void*) { + Proc(Gtk::Widget, Nil).new(d, Pointer(Void).null).call Gtk::Widget.cast(c) + nil + }, cb.pointer) + end +end