Ferada / cl-cffi-gtk

#cl-cffi-gtk on Freenode. A Lisp binding to GTK+3. SBCL/CCL/ABCL (ECL/CLISP unstable)
http://www.crategus.com/books/cl-cffi-gtk
41 stars 8 forks source link

Connect the signals to the protocol of a class #47

Open asarch opened 4 years ago

asarch commented 4 years ago

If you would have:

dialog.glade

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkDialog" id="dialog">
    <property name="can_focus">False</property>
    <property name="type_hint">dialog</property>
    <child>
      <placeholder/>
    </child>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
            <child>
              <object class="GtkButton" id="cancel">
                <property name="label">gtk-cancel</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="apply">
                <property name="label">gtk-apply</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <property name="spacing">5</property>
            <child>
              <object class="GtkLabel" id="label">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">label</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button">
                <property name="label">gtk-yes</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
                <signal name="clicked" handler="saludo" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="padding">5</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

and this piece of code:

(ql:quickload :cl-cffi-gtk)

(defpackage :aplicacion
  (:use :gtk :gdk :gdk-pixbuf :gobject
    :glib :gio :pango :cairo :common-lisp))

(in-package :aplicacion)

(defclass beer ()
  ((widget :initarg :widget :initform nil :accessor widget)
   (boton :initarg :boton :initform nil :accessor boton)))

(defgeneric cheers (beer))

(defmethod cheers ((instance beer))
  (format t "Click!~%"))

(defmethod initialize-instance :after ((instance beer) &key)
  (let ((builder (make-instance 'gtk-builder)))
    (gtk-builder-add-from-file builder "dialog.glade")
    (setf (widget instance) (gtk-builder-get-object builder "dialog"))
    (setf (boton instance) (gtk-builder-get-object builder "button")))
  (g-signal-connect (widget instance) "destroy" (lambda (msg) (declare (ignore msg)) (leave-gtk-main)))
  (g-signal-connect (boton instance) "clicked" (lambda (msg) (declare (ignore msg) (cheers instance)))))

(within-main-loop
 (let ((app (make-instance 'beer)))
   (gtk-widget-show-all (widget app))))

How would you connect the "clicked" signal to the cheers function of the class' protocol without actually using anonymous functions (just like the gtk-builder-connect-signals function to the package does)?

Ferada commented 4 years ago

Well, the problem is that the handler receives the msg, no? I suppose that's also why there's no library function for it yet, no clear idea of how this should look like.

Something like (g-signal-connect-instance (boton instance) #'cheers)?

Though of course now you'd likely have to have a way of specifying where event arguments go in the call. Or they're always discarded? I don't see how that's a good case to make for a dedicated function. But maybe I'm wrong. Could you write up how you'd deal with those cases, or why it's not a problem?

There are also some libraries to generate anonymous functions more succinctly, maybe that's another way of approaching it.