Search code examples
linuxgtk3genie

The Button.connect syntax in Genie


I want to apply a certain behaviour to a label. When a lateral button is clicked, the corresponding label should rotate 90 degrees. It can be easily done in vala, but I can't discover the particular syntax on genie.

The vala code I am trying to reproduce comes from elementary OS getting started guide:

hello_button.clicked.connect(() =>
{ 
hello_label.label = "Hello World!";
hello_button.sensitive = false;
});


rotate_button.clicked.connect(() =>
{ 
rotate_label.angle = 90;
rotate_label.label = "Verbal";
rotate_button.sensitive = false;
});

I actually managed to reproduce almost entirely the code in Genie, for the exception of the rotation. Here is how far I got:

/* ANOTHER GTK EXPERIMENT WITH GENIE BASED ON ELEMENTARY INTRODUCTORY PAGE
**   compile with valac --pkg gtk+03.0 layoutgtkexample.gs */

[indent=4]
uses Gtk

init
    Gtk.init (ref args)
    var window = new Gtk.Window()
    window.title = "Hello World!"
    window.set_border_width(12)

    var layout = new Gtk.Grid ()
    layout.column_spacing = 6
    layout.row_spacing = 6

    var hello_button = new Gtk.Button.with_label("Say Hello")
    var hello_label = new Gtk.Label("Hello")

    var rotate_button = new Gtk.Button.with_label ("Rotate")
    var rotate_label = new Gtk.Label("Horizontal")

    // add first row of widgets

    layout.attach (hello_button, 0, 0, 1,1)
    layout.attach_next_to (hello_label, hello_button, PositionType.RIGHT, 1, 1)

    // add second row of widgets

    layout.attach(rotate_button, 0,1,1,1)
    layout.attach_next_to(rotate_label, rotate_button, PositionType.RIGHT, 1, 1)

    window.add(layout)

    hello_button.clicked.connect(hello_pushed)
    rotate_button.clicked.connect(rotate_pushed)

    window.destroy.connect(Gtk.main_quit)
    window.show_all ()
    Gtk.main ()

def hello_pushed (btn:Button)
    btn.label = "Hello World!"
    btn.sensitive = false

def rotate_pushed (btn:Button)
    btn.label = "Vertical"
    //btn.angle = 90
    btn.sensitive = false

Solution

  • The problem is to do with where identifiers are valid and is known as "scope".

    The Vala example makes use of an anonymous function, also called a lambda expression in Vala. An anonymous function can be a "closure", when the variables in the scope that defines the anonymous function are also available within the anonymous function. This is useful because the callback occurs after the original block of code has been run, but the variables are still available within the callback. So in the Vala example, where both the button and label are defined in the enclosing scope, the button and label are also available in the callback anonymous function.

    Unfortunately Genie isn't able to parse anonymous functions as function arguments, in this case within the connect() call. Although some work has been done on this in 2015. So you have rightly used a function name instead. The problem is the callback only passes the button as an argument and not the adjacent label. So to make the label available within the callback function we could use a class:

    /* ANOTHER GTK EXPERIMENT WITH GENIE BASED ON ELEMENTARY INTRODUCTORY PAGE
    **   compile with valac --pkg gtk+-3.0 layoutgtkexample.gs */
    
    [indent=4]
    uses Gtk
    
    init
        Gtk.init (ref args)
        new RotatingButtonWindow( "Hello World!" )
        Gtk.main ()
    
    class RotatingButtonWindow:Window
        _hello_label:Label
        _rotate_label:Label
    
        construct( window_title:string )
            title = window_title
            set_border_width(12)
    
            var layout = new Grid ()
            layout.column_spacing = 6
            layout.row_spacing = 6
    
            // add 'hello' row of widgets
            var hello_button = new Button.with_label("Say Hello")
            _hello_label = new Label("Hello")
            layout.attach (hello_button, 0, 0, 1,1)
            layout.attach_next_to (_hello_label, hello_button, PositionType.RIGHT, 1, 1)
    
            // add 'rotate' row of widgets
            var rotate_button = new Button.with_label ("Rotate")
            _rotate_label = new Label("Horizontal")
            layout.attach(rotate_button, 0,1,1,1)
            layout.attach_next_to(_rotate_label, rotate_button, PositionType.RIGHT, 1, 1)
    
            add(layout)
    
            hello_button.clicked.connect(hello_pushed)
            rotate_button.clicked.connect(rotate_pushed)
    
            destroy.connect(Gtk.main_quit)
            show_all ()
    
        def hello_pushed (btn:Button)
            _hello_label.label = "Hello World!"
            btn.sensitive = false
    
        def rotate_pushed (btn:Button)
            _rotate_label.label = "Vertical"
            _rotate_label.angle = 90
            btn.sensitive = false
    

    A few notes:

    • By placing the definitions of the _hello_label and _rotate_label within the scope of the class they become available to all the functions defined in the class. Definitions like this are often called "fields". The underscore means they are not available outside the class, so in the example you cannot access them from init
    • construct() is called when the object is created, in the example the line new RotatingButtonWindow( "Hello World!" ) instantiates the object. If you repeat the line you will have two separate windows, that is two instances of the RotatingButtonWindow data type
    • You will notice that the RotatingButtonWindow type is also defined as a Window type. This means it is adding more detail to the Gtk.Window class. This is why title and set_border_width() can be used within the new class. They have been "inherited" from the parent Gtk.Window class
    • By using the Gtk namespace with uses Gtk we don't need to prefix everything with Gtk

    As your Gtk application gets more complex you probably want to look at GtkBuilder. That allows windows and widgets to be laid out in an external file. Then use GResource to build the file into the binary of your application so there is no need to distribute the UI file separately.