romgrk / node-gtk

GTK+ bindings for NodeJS (via GObject introspection)
MIT License
494 stars 41 forks source link

Gtk.Builder.connectSignals does not bind "this" to object #305

Closed rexkogitans closed 3 years ago

rexkogitans commented 3 years ago

First of all: I think you are into something great. IMHO, the entire Gnome Infrastructure could rely on node-gtk rather than on GJS, since GJS is an interpreter on its own and hence introduces useless hassle.

From the very early days when I learnt JS, I trained the habit of using classes rather than plain functions or objects. When programming a web application and connecting an event listener to the click event of a button, I use something like this:

button_go.addEventListener("click", this.button_go_click.bind(this))

Note the bind at the end. In this way, the variable "this" points to the object within the event handler.

I expected something like this when using Gtk.Builder.connectSignals() too, but unfortunately, "this" seems to refer to the static function context.

Just to outline what I mean, here is a rough sketch:

class MyClass {
  #builder = null;

  constructor () {
        this.#builder = new Gtk.Builder()
        this.#builder.addObjectsFromFile("test.glade", ["window1"])
        this.#builder.connectSignals(this)
        this.#window = this.#builder.getObject("window1")
        this.#window.show()
  }

 on_btn_go_click() {
  console.log(this)  // expected: mycl MyClass, but yields: Function on_btn_go_click
 }

}

mycl = new MyClass()

On the other hand: Is it viable in the year 2021 to start writing a GTK 3 app and porting it in a few years, or should I start out with GTK 4 immediately?

rexkogitans commented 3 years ago

I reply to my own issue. I started out in GTK 4 and got it working.

class MyClass extends Gtk.BuilderCScope {

  #builder = null;
  #window = null;

  constructor() {
        super()
        this.#builder = new Gtk.Builder()
        super.addCallbackSymbol("btn_go_clicked", this.btn_go_clicked.bind(this))
        this.#builder.setScope(this)
        this.#builder.addFromFile("test.cmb.ui")
        this.#window = this.#builder.getObject("win_main")
        this.#window.show()
    }

    btn_go_clicked() {
      console.log(this)
    }
}

var mycl = null;

function onActivate() {
   mycl = new MyClass()
}

const loop = GLib.MainLoop.new(null, false)
const app = new Gtk.Application("Test App", 0)
app.on('activate', onActivate)
const status = app.run([])

gi.startLoop()

loop.run()
romgrk commented 3 years ago

For GTK3 vs GTK4: I'd suggest GTK4.

For connectSignals: see https://github.com/romgrk/node-gtk/blob/master/examples/builder-auto-connect-signals.js I don't feel like auto-binding this would be a good choice, if you want bound functions you'll need to use the same syntax that before: .connectSignals({ onClick: this.onClick.bind(this) }).

Your issue seems resolved, I'll close but you can re-open if something is unclear.

rexkogitans commented 3 years ago

Your example uses GTK 3, however the GTK documentation clearly states that this function no longer exists:

https://docs.gtk.org/gtk4/migrating-3to4.html

Somehow, I have the feeling that there should be a language-specific implementation of Gtk.BuilderScope. Since Gtk.BuilderCScope is the C/C++ implementation, naming it Gtk.BuilderJSScope would make sense.