elementaryart team mailing list archive
-
elementaryart team
-
Mailing list archive
-
Message #02240
[Merge] lp:~elementary-pantheon/granite/sidebar into lp:granite
Victor Eduardo has proposed merging lp:~elementary-pantheon/granite/sidebar into lp:granite.
Requested reviews:
elementary Pantheon team (elementary-pantheon)
For more details, see:
https://code.launchpad.net/~elementary-pantheon/granite/sidebar/+merge/120886
This is the sidebar widget sgringwe wrote for BeatBox; I just did some code cleanup. Let's discuss the things we like (or don't like) about the API here.
--
https://code.launchpad.net/~elementary-pantheon/granite/sidebar/+merge/120886
Your team elementaryart (old) is subscribed to branch lp:granite.
=== modified file 'lib/CMakeLists.txt'
--- lib/CMakeLists.txt 2012-08-05 01:32:28 +0000
+++ lib/CMakeLists.txt 2012-08-22 23:28:26 +0000
@@ -81,6 +81,7 @@
Widgets/LightWindow.vala
Widgets/StatusBar.vala
Widgets/SidebarPaned.vala
+ Widgets/Sidebar.vala
Main.vala
config.vapi
PACKAGES
=== added file 'lib/Widgets/Sidebar.vala'
--- lib/Widgets/Sidebar.vala 1970-01-01 00:00:00 +0000
+++ lib/Widgets/Sidebar.vala 2012-08-22 23:28:26 +0000
@@ -0,0 +1,511 @@
+/*-
+ * Copyright (c) 2011-2012 Scott Ringwelski <sgringwe@xxxxxxx>
+ *
+ * Originally Written by Scott Ringwelski for BeatBox Music Player and Granite Library
+ * BeatBox Music Player: http://www.launchpad.net/beat-box
+ * Granite Library: http://www.launchpad.net/granite
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/**
+ * NOTES: The iters returned are child model iters. To work with any function
+ * except for add, you need to to use convert_to_filter (child iter);
+ */
+public class Granite.Widgets.Sidebar : Gtk.TreeView {
+
+ private class CellRendererExpander : Gtk.CellRenderer {
+ public bool expanded;
+ public const int EXPANDER_SIZE = 8;
+
+ public CellRendererExpander () {
+ expanded = false;
+ }
+
+ public override void get_size (Gtk.Widget widget, Gdk.Rectangle? cell_area,
+ out int x_offset, out int y_offset, out int width, out int height) {
+ x_offset = 0;
+ y_offset = EXPANDER_SIZE / 2;
+ width = height = EXPANDER_SIZE;
+ }
+
+ public override void render (Cairo.Context context, Gtk.Widget widget,
+ Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gtk.CellRendererState flags) {
+ widget.get_style_context ().set_state (expanded ? Gtk.StateFlags.ACTIVE : Gtk.StateFlags.NORMAL);
+ widget.get_style_context ().render_expander (context, cell_area.x + EXPANDER_SIZE / 2,
+ cell_area.y + EXPANDER_SIZE / 2,
+ EXPANDER_SIZE, EXPANDER_SIZE);
+ }
+ }
+
+ public enum Column {
+ COLUMN_OBJECT,
+ COLUMN_WIDGET,
+ COLUMN_VISIBLE,
+ COLUMN_PIXBUF,
+ COLUMN_TEXT,
+ COLUMN_CLICKABLE
+ }
+
+ public bool autoExpanded;
+
+ public signal void clickable_clicked (Gtk.TreeIter iter);
+ public signal void true_selection_change (Gtk.TreeIter selected);
+
+ private Gtk.TreeStore tree;
+ private Gtk.TreeModelFilter filter;
+ private Gtk.TreeIter? selectedIter;
+
+ private Gtk.CellRendererText spacer;
+ private Gtk.CellRendererText secondary_spacer;
+ private Gtk.CellRendererPixbuf pix_cell;
+ private Gtk.CellRendererText text_cell;
+ private Gtk.CellRendererPixbuf clickable_cell;
+ private CellRendererExpander expander_cell;
+
+
+ public Sidebar () {
+ tree = new Gtk.TreeStore (6, typeof (Object), typeof (Gtk.Widget), typeof (bool), typeof (Gdk.Pixbuf), typeof (string), typeof (Gdk.Pixbuf));
+ filter = new Gtk.TreeModelFilter (tree, null);
+ set_model (filter);
+
+ Gtk.TreeViewColumn col = new Gtk.TreeViewColumn ();
+ col.title = "object";
+ this.insert_column (col, 0);
+
+ col = new Gtk.TreeViewColumn ();
+ col.title = "widget";
+ this.insert_column (col, 1);
+
+ col = new Gtk.TreeViewColumn ();
+ col.title = "visible";
+ this.insert_column (col, 2);
+
+ col = new Gtk.TreeViewColumn ();
+ col.title = "display";
+ col.expand = true;
+ this.insert_column (col, 3);
+
+ // add spacer
+ spacer = new Gtk.CellRendererText ();
+ col.pack_start (spacer, false);
+ col.set_cell_data_func (spacer, spacer_cell_data_func);
+ spacer.xpad = 8;
+
+ // secondary spacer
+ secondary_spacer = new Gtk.CellRendererText ();
+ col.pack_start (secondary_spacer, false);
+ col.set_cell_data_func (secondary_spacer, secondary_spacer_cell_data_func);
+ secondary_spacer.xpad = 8;
+
+ // add pixbuf
+ pix_cell = new Gtk.CellRendererPixbuf ();
+ col.pack_start (pix_cell, false);
+ col.set_cell_data_func (pix_cell, pixbuf_cell_data_func);
+ col.set_attributes (pix_cell, "pixbuf", Column.COLUMN_PIXBUF);
+
+ // add text
+ text_cell = new Gtk.CellRendererText ();
+ col.pack_start (text_cell, true);
+ col.set_cell_data_func (text_cell, string_cell_data_func);
+ col.set_attributes (text_cell, "markup", Column.COLUMN_TEXT);
+ text_cell.ellipsize = Pango.EllipsizeMode.END;
+ text_cell.xalign = 0.0f;
+ text_cell.xpad = 3;
+
+ // add clickable icon
+ clickable_cell = new Gtk.CellRendererPixbuf ();
+ col.pack_start (clickable_cell, false);
+ col.set_cell_data_func (clickable_cell, clickable_cell_data_func);
+ col.set_attributes (clickable_cell, "pixbuf", Column.COLUMN_CLICKABLE);
+ clickable_cell.mode = Gtk.CellRendererMode.ACTIVATABLE;
+ clickable_cell.xpad = 2;
+ clickable_cell.xalign = 1.0f;
+ clickable_cell.stock_size = 16;
+
+ // add expander
+ expander_cell = new CellRendererExpander ();
+ col.pack_start (expander_cell, false);
+ col.set_cell_data_func (expander_cell, expander_cell_data_func);
+
+ this.set_headers_visible (false);
+ //this.set_expander_column (get_column (3));
+ this.set_show_expanders (false);
+ filter.set_visible_column (Column.COLUMN_VISIBLE);
+ this.set_grid_lines (Gtk.TreeViewGridLines.NONE);
+ this.name = "SidebarContent";
+
+ // Setup theming
+ this.get_style_context ().add_class (Gtk.STYLE_CLASS_SIDEBAR);
+
+ this.get_selection ().changed.connect (selection_change);
+ this.button_press_event.connect (sidebar_click);
+ }
+
+
+ public bool remove_item (Gtk.TreeIter iter) {
+ Gtk.TreeIter parent;
+ if (tree.iter_parent (out parent, iter)) {
+ if (tree.iter_n_children (parent) > 1)
+ tree.set (parent, 2, true);
+ else
+ tree.set (parent, 2, false);
+ }
+
+ Gtk.Widget w;
+ tree.get (iter, Column.COLUMN_WIDGET, out w);
+ w.destroy ();
+
+ // destroy child row widgets as well
+ Gtk.TreeIter current;
+ if (tree.iter_children (out current, iter)) {
+ do {
+ tree.get (current, Column.COLUMN_WIDGET, out w);
+ w.destroy ();
+ }
+ while (tree.iter_next (ref current));
+ }
+
+ return tree.remove (iter);
+ }
+
+ // input MUST be a child iter
+ public void set_item_visibility (Gtk.TreeIter it, bool val) {
+ bool was = false;
+ tree.get (it, Column.COLUMN_VISIBLE, out was);
+ tree.set (it, Column.COLUMN_VISIBLE, val);
+
+ if (val && !was) {
+ warning ("error happening sidebar.vala...");
+ expand_row (filter.get_path (convert_to_filter (it)), true);
+ warning ("error finished");
+ }
+ }
+
+ public void set_item_name (Gtk.TreeIter it, string name) {
+ Gtk.TreeIter iter = convert_to_child (it);
+
+ tree.set (iter, Column.COLUMN_TEXT, name);
+ }
+
+ // parent should be filter iter
+ public bool set_item_name_from_object (Gtk.TreeIter parent, Object o, string name) {
+ Gtk.TreeIter realParent = convert_to_child (parent);
+ Gtk.TreeIter pivot;
+ tree.iter_children (out pivot, realParent);
+
+ do {
+ Object tempO;
+ tree.get (pivot, 0, out tempO);
+
+ if (tempO == o) {
+ tree.set (pivot, Column.COLUMN_TEXT, name);
+ return true;
+ }
+ else if (!tree.iter_next (ref pivot)) {
+ return false;
+ }
+
+ } while (true);
+ }
+
+ public Gtk.TreeIter? get_selected_iter () {
+ Gtk.TreeModel mod;
+ Gtk.TreeIter sel;
+
+ if (this.get_selection ().get_selected (out mod, out sel)) {
+ return sel;
+ }
+
+ return null;
+ }
+
+ public void set_selected_iter (Gtk.TreeIter iter) {
+ this.get_selection ().changed.disconnect (selection_change);
+ get_selection ().unselect_all ();
+
+ get_selection ().select_iter (iter);
+ this.get_selection ().changed.connect (selection_change);
+ selectedIter = iter;
+ }
+
+ public bool expand_item (Gtk.TreeIter iter, bool expanded) {
+ if (filter.iter_n_children (convert_to_filter (iter)) < 1)
+ return false;
+
+ Gtk.TreePath? path = filter.get_path (convert_to_filter (iter));
+
+ if (path == null || path.get_depth () > 1)
+ return false;
+
+ if (expanded)
+ return expand_row (path, false);
+
+ return collapse_row (path);
+ }
+
+ public bool item_expanded (Gtk.TreeIter? iter) {
+ if (iter != null)
+ return is_row_expanded (filter.get_path (convert_to_filter (iter)));
+
+ return false;
+ }
+
+ public Object? get_object (Gtk.TreeIter iter) {
+ Object o;
+ filter.get (iter, Column.COLUMN_OBJECT, out o);
+ return o;
+ }
+
+ public Gtk.Widget? get_widget (Gtk.TreeIter iter) {
+ Gtk.Widget w;
+ tree.get (iter, Column.COLUMN_WIDGET, out w);
+ return w;
+ }
+
+ public Gtk.Widget? get_selected_widget () {
+ Gtk.TreeModel m;
+ Gtk.TreeIter iter;
+
+ if (!this.get_selection ().get_selected (out m, out iter)) { // user has nothing selected, reselect last selected
+ //if (iter == null)
+ return null;
+ }
+
+ Gtk.Widget w;
+ m.get (iter, Column.COLUMN_WIDGET, out w);
+ return w;
+ }
+
+ public Object? get_selected_object () {
+ Gtk.TreeModel m;
+ Gtk.TreeIter iter;
+
+ if (!this.get_selection ().get_selected (out m, out iter)) { // user has nothing selected, reselect last selected
+ //if (iter == null)
+ return null;
+ }
+
+ Object o;
+ m.get (iter, Column.COLUMN_OBJECT, out o);
+ return o;
+ }
+
+ /* stops user from selecting the root nodes */
+ public void selection_change () {
+ Gtk.TreeModel model;
+ Gtk.TreeIter pending;
+
+ if (!this.get_selection ().get_selected (out model, out pending)) { // user has nothing selected, reselect last selected
+ if (selectedIter != null) {
+ this.get_selection ().select_iter (selectedIter);
+ }
+
+ return;
+ }
+
+ Gtk.TreePath path = model.get_path (pending);
+
+ if (path.get_depth () == 1) {
+ this.get_selection ().unselect_all ();
+ if (selectedIter != null)
+ this.get_selection ().select_iter (selectedIter);
+ }
+ else if (pending != selectedIter) {
+ selectedIter = pending;
+ true_selection_change (selectedIter);
+ }
+ }
+
+ /* click event functions */
+ private bool sidebar_click (Gdk.EventButton event) {
+ if (event.type == Gdk.EventType.BUTTON_PRESS && event.button == 1) {
+ // select one based on mouse position
+ Gtk.TreeIter iter;
+ Gtk.TreePath path;
+ Gtk.TreeViewColumn column;
+ int cell_x;
+ int cell_y;
+
+ this.get_path_at_pos ( (int)event.x, (int)event.y, out path, out column, out cell_x, out cell_y);
+
+ if (!filter.get_iter (out iter, path))
+ return false;
+
+ if (over_clickable (iter, column, (int)cell_x, (int)cell_y)) {
+ clickable_clicked (iter);
+ }
+ else if (over_expander (iter, column, (int)cell_x, (int)cell_y)) {
+ if (is_row_expanded (path))
+ this.collapse_row (path);
+ else
+ this.expand_row (path, true);
+ }
+ }
+
+ return false;
+ }
+
+ private bool over_clickable (Gtk.TreeIter iter, Gtk.TreeViewColumn col, int x, int y) {
+ Gdk.Pixbuf pix;
+ filter.get (iter, 5, out pix);
+
+ if (pix == null)
+ return false;
+
+ int cell_x;
+ int cell_width;
+ col.cell_get_position (clickable_cell, out cell_x, out cell_width);
+
+ if (x > cell_x && x < cell_x + cell_width)
+ return true;
+
+ return false;
+ }
+
+ private bool over_expander (Gtk.TreeIter iter, Gtk.TreeViewColumn col, int x, int y) {
+ if (filter.get_path (iter).get_depth () != 1)
+ return false;
+ else
+ return true;
+
+ /* for some reason, the pixbuf SOMETIMES takes space, somtimes doesn't so cope for that *
+ int pixbuf_start;
+ int pixbuf_width;
+ col.cell_get_position (pix_cell, out pixbuf_start, out pixbuf_width);
+ int text_start;
+ int text_width;
+ col.cell_get_position (text_cell, out text_start, out text_width);
+ int click_start;
+ int click_width;
+ col.cell_get_position (clickable_cell, out click_start, out click_width);
+ int total = text_start + text_width + click_width - pixbuf_start;
+
+ if (x > total)
+ return true;
+
+ return false;*/
+ }
+
+ /* Helpers for child->filter, filter->child */
+ public Gtk.TreeIter? convert_to_filter (Gtk.TreeIter? child) {
+ if (child == null)
+ return null;
+
+ Gtk.TreeIter rv;
+
+ if (filter.convert_child_iter_to_iter (out rv, child)) {
+ return rv;
+ }
+
+ return null;
+ }
+
+ public Gtk.TreeIter? convert_to_child (Gtk.TreeIter? filt) {
+ if (filt == null)
+ return null;
+
+ Gtk.TreeIter rv;
+ filter.convert_iter_to_child_iter (out rv, filt);
+
+ return rv;
+ }
+
+
+ private static void spacer_cell_data_func (Gtk.CellLayout layout, Gtk.CellRenderer renderer,
+ Gtk.TreeModel model, Gtk.TreeIter iter) {
+ Gtk.TreePath path = model.get_path (iter);
+ int depth = path.get_depth ();
+
+ renderer.visible = (depth > 1);
+ renderer.xpad = (depth > 1) ? 8 : 0;
+ }
+
+ private static void secondary_spacer_cell_data_func (Gtk.CellLayout layout, Gtk.CellRenderer renderer,
+ Gtk.TreeModel model, Gtk.TreeIter iter) {
+ Gtk.TreePath path = model.get_path (iter);
+ int depth = path.get_depth ();
+
+ renderer.visible = (depth > 2);
+ renderer.xpad = (depth > 1) ? 8 : 0;
+ }
+
+ private static void pixbuf_cell_data_func (Gtk.CellLayout layout, Gtk.CellRenderer renderer,
+ Gtk.TreeModel model, Gtk.TreeIter iter) {
+ Gtk.TreePath path = model.get_path (iter);
+
+ if (path.get_depth () == 1) {
+ renderer.visible = false;
+ }
+ else {
+ renderer.visible = true;
+ }
+ }
+
+ private static void string_cell_data_func (Gtk.CellLayout layout, Gtk.CellRenderer renderer,
+ Gtk.TreeModel model, Gtk.TreeIter iter) {
+ Gtk.TreePath path = model.get_path (iter);
+ int depth = path.get_depth ();
+ string text = "";
+ model.get (iter, Column.COLUMN_TEXT, out text);
+
+ if (depth == 1) {
+ ( (Gtk.CellRendererText)renderer).markup = "<b>" + text + "</b>";
+ }
+ else {
+ ( (Gtk.CellRendererText)renderer).markup = text;
+ }
+ }
+
+ private static void clickable_cell_data_func (Gtk.CellLayout layout, Gtk.CellRenderer renderer,
+ Gtk.TreeModel model, Gtk.TreeIter iter) {
+ Gtk.TreePath path = model.get_path (iter);
+
+ if (path.get_depth () == 1) {
+ renderer.visible = false;
+ }
+ else {
+ renderer.visible = true;
+ }
+ }
+
+ private void expander_cell_data_func (Gtk.CellLayout layout, Gtk.CellRenderer renderer,
+ Gtk.TreeModel model, Gtk.TreeIter iter) {
+ Gtk.TreePath path = model.get_path (iter);
+
+ renderer.visible = (path.get_depth () == 1);
+ ( (CellRendererExpander)renderer).expanded = is_row_expanded (path);
+ }
+
+ /* Convenient add/remove/edit methods */
+ public Gtk.TreeIter add_item (Gtk.TreeIter? parent, Object? o, Gtk.Widget? w, Gdk.Pixbuf? pixbuf, string text, Gdk.Pixbuf? clickable) {
+ Gtk.TreeIter iter;
+
+ tree.append (out iter, parent);
+ tree.set (iter, 0, o, 1, w, 2, true, 3, pixbuf, 4, text, 5, clickable);
+
+ if (parent != null) {
+ tree.set (parent, 2, true);
+ }
+ else {
+ tree.set (iter, 2, false);
+ }
+
+ expand_all ();
+ return iter;
+ }
+}
Follow ups