]> git.sur5r.net Git - i3/i3/commitdiff
Initial commit of gtk-tree-watch.pl which uses GTK/Perl to visualize the i3 tree...
authorLourens Rozema <i3hacker@LourensRozema.nl>
Fri, 20 Aug 2010 19:33:18 +0000 (21:33 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 21 Aug 2010 16:30:27 +0000 (18:30 +0200)
gtk-tree-watch.pl [new file with mode: 0755]

diff --git a/gtk-tree-watch.pl b/gtk-tree-watch.pl
new file mode 100755 (executable)
index 0000000..6d83d1f
--- /dev/null
@@ -0,0 +1,219 @@
+#!/usr/bin/env perl
+# vim:ts=4:sw=4:expandtab
+# renders the layout tree using asymptote
+
+use strict;
+use warnings;
+
+use JSON::XS;
+use Data::Dumper;
+use AnyEvent::I3;
+use v5.10;
+
+use Gtk2 '-init';
+use Gtk2::SimpleMenu;
+use Glib qw/TRUE FALSE/; 
+
+my $window = Gtk2::Window->new('toplevel');
+$window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
+
+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/);
+
+my $i3 = i3("/tmp/nestedcons");
+
+my $tree_view = Gtk2::TreeView->new($tree_store);
+
+my $layout_box = undef;
+
+sub copy_node {
+    my ($n, $parent, $piter, $pbox) = @_;
+
+    my $o = ($n->{orientation} == 0 ? "u" : ($n->{orientation} == 1 ? "h" : "v"));
+    my $w = (defined($n->{window}) ? $n->{window} : "N");
+
+    # convert a rectangle struct to X11 notation (WxH+X+Y)
+    my $r = $n->{rect};
+    my $x = $r->{x};
+    my $y = $r->{y};
+    my $dim = $r->{width}."x".$r->{height}.($x<0?$x:"+$x").($y<0?$y:"+$y");
+
+    # add node to the tree with all known properties
+    my $iter = $tree_store->append($piter);
+    $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);
+
+    # also create a box for the node, each node has a vbox
+    # for combining the title (and properties) with the
+    # container itself, the container will be empty in case
+    # of no children, a vbox or hbox
+    my $box;
+    if($n->{orientation} == 1) {
+        $box = Gtk2::HBox->new(1, 5);
+    } else {
+        $box = Gtk2::VBox->new(1, 5);
+    }
+
+    # combine label and container
+    my $node = Gtk2::Frame->new($n->{name}.",".$o.",".$w);
+    $node->set_shadow_type('etched-out');
+    $node->add($box);
+
+    # the parent is added onto a scrolled window, so add it with a viewport
+    if(defined($pbox)) {
+       $pbox->pack_start($node, 1, 1, 0);
+    } else {
+       $layout_box = $node;
+    }
+
+    # recurse into children
+    copy_node($_, $n, $iter, $box) for @{$n->{nodes}};
+
+    # if it is a window draw a nice color
+    if(defined($n->{window})) {
+       # use a drawing area to fill a colored rectangle
+       my $area = Gtk2::DrawingArea->new();
+
+       # the color is stored as hex in the name
+       $area->{"user-data"} = $n->{name};
+
+       $area->signal_connect(expose_event => sub {
+           my ($widget, $event) = @_;
+
+           # fetch a cairo context and it width/height to start drawing nodes
+           my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
+
+           my $w = $widget->allocation->width;
+           my $h = $widget->allocation->height;
+
+           my $hc  = $widget->{"user-data"};
+           my $r = hex(substr($hc, 1, 2)) / 255.0;
+           my $g = hex(substr($hc, 3, 2)) / 255.0;
+           my $b = hex(substr($hc, 5, 2)) / 255.0;
+
+           $cr->set_source_rgb($r, $g, $b);
+           $cr->rectangle(0, 0, $w, $h);
+           $cr->fill();
+
+           return FALSE;
+       });
+
+       $box->pack_end($area, 1, 1, 0);
+    }
+}
+
+# Replaced by Gtk2 Boxes:
+#sub draw_node {
+#    my ($n, $cr, $x, $y, $w, $h) = @_;
+#
+#    $cr->set_source_rgb(1.0, 1.0, 1.0);
+#    $cr->rectangle($x, $y, $w/2, $h/2);
+#    $cr->fill();
+#}
+
+my $json_prev = "";
+
+my $layout_sw = Gtk2::ScrolledWindow->new(undef, undef);
+my $layout_container = Gtk2::HBox->new(0, 0);
+$layout_sw->add_with_viewport($layout_container);
+
+sub copy_tree {
+    my $tree = $i3->get_workspaces->recv;
+
+    # convert the tree back to json so we only rebuild/redraw when the tree is changed
+    my $json = encode_json($tree);
+    if($json ne $json_prev) {
+        $json_prev = $json;
+
+       # rebuild the tree and the layout
+        $tree_store->clear();
+       if(defined($layout_box)) {
+           $layout_container->remove($layout_box);
+       }
+        copy_node($tree);
+       $layout_container->add($layout_box);
+       $layout_container->show_all();
+
+        # keep things expanded, otherwise the tree collapses every reload which is more annoying then this :-)
+        $tree_view->expand_all();
+    }
+
+    return(TRUE);
+}
+
+sub new_column {
+    my $tree_column = Gtk2::TreeViewColumn->new();
+    $tree_column->set_title(shift);
+
+    my $renderer = Gtk2::CellRendererText->new();
+    $tree_column->pack_start($renderer, FALSE);
+    $tree_column->add_attribute($renderer, text => shift);
+
+    return($tree_column);
+}
+
+my $col = 0;
+$tree_view->append_column(new_column("Name", $col++));
+$tree_view->append_column(new_column("Window", $col++));
+$tree_view->append_column(new_column("Orientation", $col++));
+$tree_view->append_column(new_column("ID", $col++));
+$tree_view->append_column(new_column("Urgent", $col++));
+$tree_view->append_column(new_column("Focused", $col++));
+$tree_view->append_column(new_column("Layout", $col++));
+$tree_view->append_column(new_column("Rect", $col++));
+
+$tree_view->set_grid_lines("both");
+
+my $tree_sw = Gtk2::ScrolledWindow->new(undef, undef);
+$tree_sw->add($tree_view);
+
+# Replaced by Gtk2 Boxes:
+#my $area = Gtk2::DrawingArea->new();
+#$area->signal_connect(expose_event => sub {
+#    my ($widget, $event) = @_;
+#
+#    # fetch a cairo context and it width/height to start drawing nodes
+#    my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
+#
+#    my $w = $widget->allocation->width;
+#    my $h = $widget->allocation->height;
+#
+#    draw_node($gtree, $cr, 0, 0, $w, $h);
+#
+#    return FALSE;
+#});
+
+sub menu_export {
+    print("TODO: EXPORT\n");
+}
+
+my $menu_tree = [
+       _File => {
+               item_type => '<Branch>',
+               children => [
+                       _Export => {
+                               callback => \&menu_export,
+                               accelerator => '<ctrl>E',
+                       },
+                       _Quit => {
+                               callback => sub { Gtk2->main_quit; },
+                               accelerator => '<ctrl>Q',
+                       },
+               ],
+       },
+];
+
+my $menu = Gtk2::SimpleMenu->new(menu_tree => $menu_tree);
+
+my $vbox = Gtk2::VBox->new(0, 0);
+$vbox->pack_start($menu->{widget}, 0, 0, 0);
+$vbox->pack_end($tree_sw, 1, 1, 0);
+$vbox->pack_end($layout_sw, 1, 1, 0);
+
+$window->add($vbox);
+$window->show_all();
+$window->set_size_request(500,500);
+
+Glib::Timeout->add(1000, "copy_tree", undef, Glib::G_PRIORITY_DEFAULT);
+copy_tree();
+
+Gtk2->main();
+