Interfacing Raku to Gnome GTK+

The Gui Handlers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use v6;

unit class GuiHandlers;

use Gnome::N::N-GObject;

use Gnome::Gtk3::Main;
use Gnome::Gtk3::ListStore;
use Gnome::Gtk3::TreeStore;
use Gnome::Gtk3::TreeIter;
use Gnome::Gtk3::TreePath;
use Gnome::Gtk3::TreeView;
use Gnome::Gtk3::TreeViewColumn;

#-------------------------------------------------------------------------------
class Application {

  has Gnome::Gtk3::Main $!main;

  #-----------------------------------------------------------------------------
  submethod BUILD (:$!main) { }

  method exit-todo-viewer ( ) {
    $!main.quit;
  }
}

#-------------------------------------------------------------------------------
class ListView {

  has Str $!filename;

  #-----------------------------------------------------------------------------
  method select-list-entry (
    N-GtkTreePath $n-tree-path, N-GObject $n-treeview-column,
    Hash :$data, Int :$data-col, Gnome::Gtk3::TreeStore :$files,
    Gnome::Gtk3::ListStore :$markers
  ) {

    my Gnome::Gtk3::TreePath $tree-path;
    my Gnome::Gtk3::TreeIter $iter;

    # Clear the list store of its entries
    $tree-path .= new(:string('0'));
    $iter = $markers.tree-model-get-iter($tree-path);

    while $iter.is-valid {
      $iter = $markers.list-store-remove($iter);
    }

    # Get the key from the data column and check
    $tree-path .= new(:native-object($n-tree-path));
    $iter = $files.tree-model-get-iter($tree-path);
    my Array[Gnome::GObject::Value] $v = $files.tree-model-get-value(
      $iter, $data-col
    );
    my Str $data-key = $v[0].get-string // '';
    $v[0].clear-object;

    # Return if there is no filename key
    return 1 unless ?$data-key;

    # Add new entries from the data using the filename data key
    for @($data{$data-key}) -> @entry {
      $iter = $markers.list-store-append;
      $markers.list-store-set( $iter, |(@entry.kv));
    }

    $!filename = $data-key;
  }

  #-----------------------------------------------------------------------------
  method select-marker-entry (
    N-GtkTreePath $n-tree-path, N-GObject $n-treeview-column,
    Gnome::Gtk3::ListStore :$markers,
  ) {

    my Gnome::Gtk3::TreePath $tree-path .= new(:native-object($n-tree-path));
    my Gnome::Gtk3::TreeIter $iter = $markers.tree-model-get-iter($tree-path);

    my Array[Gnome::GObject::Value] $v =
       $markers.tree-model-get-value( $iter, 1);
    my Int $line = $v[0].get-int // -1;
    $v[0].clear-object;

    note "Start atom editor with folowing file $!filename at line $line";
    shell "atom $!filename:$line &";
  }
}

The classes

The first class is called GuiHandlers::Application and is used to handle program specific signals [16-28]. There is only one handler defined exit-todo-viewer() to exit the main loop and return to the main program. It returns 1 to show that all is handled by the sub.

The second class, GuiHandlers::ListView, is used to handle events from the two tree views and to modify the markers table. There is a method select-list-entry() [36-75] to handle the double click on the files table and another method select-marker-entry() [78-100] to handle the double click on the markers table.

select-list-entry

When the row-activated signal is emitted, it will call a handler which must have the following api;

  method handler (
    N-GtkTreePath $path, N-GObject $column,
    Gnome::GObject::Object :widget($tree_view),
    *%user-options
  );

The first two arguments, $n-tree-path and $n-treeview-column [37], are obligatory, even when you do not need them. For instance, we do not need the $n-treeview-column variable in this case. Also, the types are needed by the library to make a proper connection and cannot be left out. The named attributes are always optional and here we left out :$widget here. The user options are those extra named attributes given to the register-signal() method.

Before we insert the data, we clear the table first [46-52]. We start at the top row indicated by a path 0 [47]. This path is input to the iterator initialization [48]. In a while-loop we remove the row after which a new iterator is returned now pointing to the next row [51]. The path however is still 0 because the rows move up in the table. $iter.tree-iter-is-valid() turns False when there are no rows left.

The argument $n-tree-path given to the handler, holds the row where the user has clicked on and we need to make an iterator from it [55,56]. From this row we get the key into the data hash ($data) from the hidden column ($data-col) in the files table ($files). All these values are provided to the handler [57-61].

Finally, the data pointed by the data key in the data hash is inserted in the markers table [67-70].

The data key is the absolute path to the filename and is stored in an attribute for later use [72].

select-marker-entry

This is the same type of handler as described above. This time we needed less named arguments, only a markers table [80].

Convert the provided path into an iterator [84,85] and get the line number from the second column [87-90]. The filename was already saved in the above signal handler select-list-entry() in $!filename.

Here we use it to start the github atom editor and provide the file and line number [93].