]> git.sur5r.net Git - i3/i3/blob - gtk-tree-watch.pl
docs/userguide: use i3bar instead of dzen2 in the 'exec' example (Thanks ubitux)
[i3/i3] / gtk-tree-watch.pl
1 #!/usr/bin/env perl
2 # vim:ts=4:sw=4:expandtab
3 # renders the layout tree using asymptote
4
5 use strict;
6 use warnings;
7
8 use JSON::XS;
9 use Data::Dumper;
10 use AnyEvent::I3;
11 use v5.10;
12
13 use Gtk2 '-init';
14 use Gtk2::SimpleMenu;
15 use Glib qw/TRUE FALSE/;
16
17 my $window = Gtk2::Window->new('toplevel');
18 $window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
19
20 my $tree_store = Gtk2::TreeStore->new(qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/);
21
22 my $i3 = i3("/tmp/nestedcons");
23
24 my $tree_view = Gtk2::TreeView->new($tree_store);
25
26 my $layout_box = undef;
27
28 sub copy_node {
29     my ($n, $parent, $piter, $pbox) = @_;
30
31     my $o = ($n->{orientation} == 0 ? "u" : ($n->{orientation} == 1 ? "h" : "v"));
32     my $w = (defined($n->{window}) ? $n->{window} : "N");
33
34     # convert a rectangle struct to X11 notation (WxH+X+Y)
35     my $r = $n->{rect};
36     my $x = $r->{x};
37     my $y = $r->{y};
38     my $dim = $r->{width}."x".$r->{height}.($x<0?$x:"+$x").($y<0?$y:"+$y");
39
40     # add node to the tree with all known properties
41     my $iter = $tree_store->append($piter);
42     $tree_store->set($iter, 0 => $n->{name}, 1 => $w, 2 => $o, 3 => sprintf("0x%08x", $n->{id}), 4 => $n->{urgent}, 5 => $n->{focused}, 6 => $n->{layout}, 7 => $dim);
43
44     # also create a box for the node, each node has a vbox
45     # for combining the title (and properties) with the
46     # container itself, the container will be empty in case
47     # of no children, a vbox or hbox
48     my $box;
49     if($n->{orientation} == 1) {
50          $box = Gtk2::HBox->new(1, 5);
51     } else {
52          $box = Gtk2::VBox->new(1, 5);
53     }
54
55     # combine label and container
56     my $node = Gtk2::Frame->new($n->{name}.",".$o.",".$w);
57     $node->set_shadow_type('etched-out');
58     $node->add($box);
59
60     # the parent is added onto a scrolled window, so add it with a viewport
61     if(defined($pbox)) {
62         $pbox->pack_start($node, 1, 1, 0);
63     } else {
64         $layout_box = $node;
65     }
66
67     # recurse into children
68     copy_node($_, $n, $iter, $box) for @{$n->{nodes}};
69
70     # if it is a window draw a nice color
71     if(defined($n->{window})) {
72         # use a drawing area to fill a colored rectangle
73         my $area = Gtk2::DrawingArea->new();
74
75         # the color is stored as hex in the name
76         $area->{"user-data"} = $n->{name};
77
78         $area->signal_connect(expose_event => sub {
79             my ($widget, $event) = @_;
80
81             # fetch a cairo context and it width/height to start drawing nodes
82             my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
83
84             my $w = $widget->allocation->width;
85             my $h = $widget->allocation->height;
86
87             my $hc  = $widget->{"user-data"};
88             my $r = hex(substr($hc, 1, 2)) / 255.0;
89             my $g = hex(substr($hc, 3, 2)) / 255.0;
90             my $b = hex(substr($hc, 5, 2)) / 255.0;
91
92             $cr->set_source_rgb($r, $g, $b);
93             $cr->rectangle(0, 0, $w, $h);
94             $cr->fill();
95
96         return FALSE;
97         });
98
99     $box->pack_end($area, 1, 1, 0);
100     }
101 }
102
103 # Replaced by Gtk2 Boxes:
104 #sub draw_node {
105 #    my ($n, $cr, $x, $y, $w, $h) = @_;
106 #
107 #    $cr->set_source_rgb(1.0, 1.0, 1.0);
108 #    $cr->rectangle($x, $y, $w/2, $h/2);
109 #    $cr->fill();
110 #}
111
112 my $json_prev = "";
113
114 my $layout_sw = Gtk2::ScrolledWindow->new(undef, undef);
115 my $layout_container = Gtk2::HBox->new(0, 0);
116 $layout_sw->add_with_viewport($layout_container);
117
118 sub copy_tree {
119     my $tree = $i3->get_tree->recv;
120
121     # convert the tree back to json so we only rebuild/redraw when the tree is changed
122     my $json = encode_json($tree);
123     if ($json ne $json_prev) {
124         $json_prev = $json;
125
126         # rebuild the tree and the layout
127         $tree_store->clear();
128         if(defined($layout_box)) {
129             $layout_container->remove($layout_box);
130         }
131         copy_node($tree);
132         $layout_container->add($layout_box);
133         $layout_container->show_all();
134
135         # keep things expanded, otherwise the tree collapses every reload which is more annoying then this :-)
136         $tree_view->expand_all();
137     }
138
139     return(TRUE);
140 }
141
142 sub new_column {
143     my $tree_column = Gtk2::TreeViewColumn->new();
144     $tree_column->set_title(shift);
145
146     my $renderer = Gtk2::CellRendererText->new();
147     $tree_column->pack_start($renderer, FALSE);
148     $tree_column->add_attribute($renderer, text => shift);
149
150     return($tree_column);
151 }
152
153 my $col = 0;
154 $tree_view->append_column(new_column("Name", $col++));
155 $tree_view->append_column(new_column("Window", $col++));
156 $tree_view->append_column(new_column("Orientation", $col++));
157 $tree_view->append_column(new_column("ID", $col++));
158 $tree_view->append_column(new_column("Urgent", $col++));
159 $tree_view->append_column(new_column("Focused", $col++));
160 $tree_view->append_column(new_column("Layout", $col++));
161 $tree_view->append_column(new_column("Rect", $col++));
162
163 $tree_view->set_grid_lines("both");
164
165 my $tree_sw = Gtk2::ScrolledWindow->new(undef, undef);
166 $tree_sw->add($tree_view);
167
168 # Replaced by Gtk2 Boxes:
169 #my $area = Gtk2::DrawingArea->new();
170 #$area->signal_connect(expose_event => sub {
171 #    my ($widget, $event) = @_;
172 #
173 #    # fetch a cairo context and it width/height to start drawing nodes
174 #    my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
175 #
176 #    my $w = $widget->allocation->width;
177 #    my $h = $widget->allocation->height;
178 #
179 #    draw_node($gtree, $cr, 0, 0, $w, $h);
180 #
181 #    return FALSE;
182 #});
183
184 sub menu_export {
185     print("TODO: EXPORT\n");
186 }
187
188 my $menu_tree = [
189         _File => {
190                 item_type => '<Branch>',
191                 children => [
192                         _Export => {
193                                 callback => \&menu_export,
194                                 accelerator => '<ctrl>E',
195                         },
196                         _Quit => {
197                                 callback => sub { Gtk2->main_quit; },
198                                 accelerator => '<ctrl>Q',
199                         },
200                 ],
201         },
202 ];
203
204 my $menu = Gtk2::SimpleMenu->new(menu_tree => $menu_tree);
205
206 my $vbox = Gtk2::VBox->new(0, 0);
207 $vbox->pack_start($menu->{widget}, 0, 0, 0);
208 $vbox->pack_end($tree_sw, 1, 1, 0);
209 $vbox->pack_end($layout_sw, 1, 1, 0);
210
211 $window->add($vbox);
212 $window->show_all();
213 $window->set_size_request(500,500);
214
215 Glib::Timeout->add(1000, "copy_tree", undef, Glib::G_PRIORITY_DEFAULT);
216 copy_tree();
217
218 Gtk2->main();
219