[Xfce4-commits] <design:master> More work on the mockup. Separators, markup, custom item renderer.

Jannis Pohlmann noreply at xfce.org
Sun May 29 22:46:01 CEST 2011


Updating branch refs/heads/master
         to e8cb4393724fdddd4f846a02740a04475235b9be (commit)
       from d97b5a3e27b4a3f124a8a8c1ac97727d8b2e012b (commit)

commit e8cb4393724fdddd4f846a02740a04475235b9be
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Sun May 29 22:43:49 2011 +0200

    More work on the mockup. Separators, markup, custom item renderer.

 .../treeview-mockup/vala/mockup.vala               |  399 ++++++++++++++++++--
 1 files changed, 365 insertions(+), 34 deletions(-)

diff --git a/thunar/shortcuts-pane/treeview-mockup/vala/mockup.vala b/thunar/shortcuts-pane/treeview-mockup/vala/mockup.vala
index 6c38346..5bf1d66 100644
--- a/thunar/shortcuts-pane/treeview-mockup/vala/mockup.vala
+++ b/thunar/shortcuts-pane/treeview-mockup/vala/mockup.vala
@@ -71,16 +71,36 @@ public class Category : GLib.Object {
     get { return _items; }
   }
 
-  public Category (string name) {
+  public Category (string? name) {
     this.name = name;
   }
 }
 
 
+public class SeparatorCategory : Category {
+  public SeparatorCategory () {
+    base (null);
+  }
+}
+
+
 
 public class ShortcutsModel : GLib.Object, TreeModel {
-  public static enum Column { ICON, NAME, ACTION_WIDGET }
-  private static Type[] ColumnTypes = { typeof (Icon), typeof (string), typeof (Button) };
+  public static enum Column {
+    ICON,
+    NAME,
+    ACTION_WIDGET,
+    SEPARATOR,
+    SENSITIVE,
+  }
+
+  private static Type[] ColumnTypes = {
+    typeof (Icon),
+    typeof (string),
+    typeof (Button),
+    typeof (bool),
+    typeof (bool)
+  };
 
   private GenericArray<Category> categories = new GenericArray<Category> ();
   private int stamp = (int) Random.next_int ();
@@ -98,15 +118,24 @@ public class ShortcutsModel : GLib.Object, TreeModel {
     var dvd = new DeviceShortcut ("Blank DVD+RW Disc", "media-optical-dvd", false);
     devices.items.add (dvd);
 
+    var separator = new SeparatorCategory ();
+    categories.add (separator);
+
     var places = new Category ("Places");
     categories.add (places);
 
     var home = new SystemShortcut ("ochosi", "user-home");
     places.items.add(home);
 
-    var desktop = new SystemShortcut ("desktop", "user-desktop");
+    var desktop = new SystemShortcut ("Desktop", "user-desktop");
     places.items.add (desktop);
 
+    var music = new SystemShortcut ("Music", "folder-music");
+    places.items.add (music);
+
+    separator = new SeparatorCategory ();
+    categories.add (separator);
+
     var network = new Category ("Network");
     categories.add (network);
 
@@ -122,7 +151,7 @@ public class ShortcutsModel : GLib.Object, TreeModel {
   }
 
   public int get_n_columns () {
-    return 3;
+    return ColumnTypes.length;
   }
 
   public Type get_column_type (int index) {
@@ -163,9 +192,16 @@ public class ShortcutsModel : GLib.Object, TreeModel {
   }
 
   public TreePath get_path (TreeIter iter) {
-    /* TODO implement me */
-    assert (false);
-    return new TreePath ();
+    int category_index = (int) iter.user_data;
+    int item_index = (int) iter.user_data2;
+
+    TreePath path = new TreePath ();
+
+    path.append_index (category_index);
+    if (item_index >= 0)
+      path.append_index (item_index);
+
+    return path;
   }
 
   public void get_value (TreeIter iter, int column, out Value value) {
@@ -179,20 +215,43 @@ public class ShortcutsModel : GLib.Object, TreeModel {
     assert (item_index < category.items.length);
 
     if (item_index < 0) {
-      if (column == Column.NAME)
-        value = "<b>" + category.name + "</b>";
-      else
+      switch (column) {
+      case Column.NAME:
+        value = "<span size='large' weight='medium' color='#353535'>" + category.name + "</span>";
+        break;
+      case Column.SEPARATOR:
+        bool is_separator = category is SeparatorCategory;
+        value = is_separator;
+        break;
+      case Column.SENSITIVE:
+        bool is_sensitive = !(category is SeparatorCategory);
+        value = is_sensitive;
+        break;
+      default:
         value = Value (ColumnTypes[column]);
+        break;
+      }
     } else {
       var item = category.items.get (item_index);
       assert (item != null);
 
-      if (column == Column.NAME)
+      switch (column) {
+      case Column.NAME:
         value = item.name;
-      else if (column == Column.ICON) {
+        break;
+      case Column.ICON:
         value = item.icon;
-      } else
+        break;
+      case Column.SEPARATOR:
+        value = false;
+        break;
+      case Column.SENSITIVE:
+        value = true;
+        break;
+      default:
         value = Value (ColumnTypes[column]);
+        break;
+      }
     }
   }
 
@@ -336,25 +395,277 @@ public class ShortcutsModel : GLib.Object, TreeModel {
 
 
 
-public class CellRendererFlexibleIcon : CellRendererPixbuf {
+public class ShortcutRenderer : CellRenderer {
+  public Icon icon { get; set; }
+  public string markup { get; set; }
+  public uint spacing { get; set; default = 6; }
+  public IconSize icon_size { get; set; default = IconSize.MENU; }
+
+  private Widget widget { get; private set; }
+  private Pango.Layout layout { get; set; }
+
+  private void update_widget (Widget widget) {
+    /* disconnect from old widget */
+
+    /* connect to new widget */
+
+    /* set the widget property */
+    this.widget = widget;
 
-  public override void get_size (Widget widget, Gdk.Rectangle? cell_area,
-                                 out int x_offset, out int y_offset,
-                                 out int width, out int height)
+    if (widget != null) {
+      /* allocate a new pango layout for this widget */
+      var context = widget.get_pango_context ();
+      layout = new Pango.Layout (context);
+
+      /* disable automatic text directio to use the directioned specified by GTK+ */
+      layout.set_auto_dir (false);
+
+      /* we don't want to interpret line separators in file names */
+      layout.set_single_paragraph_mode (true);
+    } else {
+      layout = null;
+    }
+  }
+
+  public override void get_size (Widget widget,
+                                 Gdk.Rectangle? cell_area,
+                                 out int x_offset,
+                                 out int y_offset,
+                                 out int width,
+                                 out int height)
   {
-    if (gicon != null) {
-      x_offset = 0;
-      y_offset = 0;
-      width = 16;
-      height = 16;
+    /* update the widget and regenerate the pango layout */
+    update_widget (widget);
+
+    /* compute icon and text size */
+    int icon_width;
+    int icon_height;
+    int text_width;
+    int text_height;
+    compute_icon_size (widget, out icon_width, out icon_height);
+    compute_text_size (widget, cell_area, out text_width, out text_height);
+
+    /* compute total width */
+    width = ((icon_width > 0) ? (icon_width + (int) spacing) : 0) + text_width;
+
+    /* compute total height */
+    height = int.max (icon_height, text_height);
+
+    /* compute offset depending on the widget direction */
+    if (cell_area != null) {
+      x_offset = (int) ((widget.get_direction () == TextDirection.RTL ? 1.0 - xalign : xalign) * (cell_area.width - width));
+      x_offset = int.max (x_offset, 0) + (int) xpad;
+
+      y_offset = (int) (yalign * (cell_area.height - height));
+      y_offset = int.max (y_offset, 0) + (int) ypad;
     } else {
       x_offset = 0;
       y_offset = 0;
-      width = 0;
-      height = 0;
     }
+
+    /* add padding */
+    width += (int) xpad * 2;
+    height += (int) ypad * 2;
   }
 
+  private void compute_icon_size (Widget widget,
+                                  out int width,
+                                  out int height)
+  {
+    /* initialize the width and height */
+    width = 0;
+    height = 0;
+
+    /* compute the size of the icon */
+    if (icon != null) {
+      if (!Gtk.icon_size_lookup (icon_size, out width, out height)) {
+        width = 16;
+        height = 16;
+      }
+    }
+  }
+
+  private void compute_text_size (Widget widget,
+                                  Gdk.Rectangle? cell_area,
+                                  out int width,
+                                  out int height)
+  {
+    /* TODO this is dirty, we should really use pango here and take
+     * ellipsizing into account */
+
+    layout.set_width (-1);
+    layout.set_wrap (Pango.WrapMode.CHAR);
+    layout.set_markup (markup, -1);
+
+    /* calculate the real text dimension */
+    layout.get_pixel_size (out width, out height);
+  }
+
+  public override void render (Gdk.Window window,
+                               Widget widget,
+                               Gdk.Rectangle background_area,
+                               Gdk.Rectangle cell_area,
+                               Gdk.Rectangle expose_area,
+                               CellRendererState flags)
+  {
+    /* update the widget and regenerate the pango layout */
+    update_widget (widget);
+
+    /* determine the state */
+    StateType state = StateType.NORMAL;
+    if ((flags & CellRendererState.SELECTED) != 0) {
+      if (widget.has_focus)
+        state = StateType.SELECTED;
+      else
+        state = StateType.ACTIVE;
+    } else if ((flags & CellRendererState.PRELIT) != 0
+               && widget.state == StateType.PRELIGHT)
+    {
+      state = StateType.PRELIGHT;
+    } else {
+      if (widget.state == StateType.INSENSITIVE)
+        state = StateType.INSENSITIVE;
+      else
+        state = StateType.NORMAL;
+    }
+
+    if (widget.get_direction () == TextDirection.RTL) {
+      int x = cell_area.x;
+
+      /* render the text */
+      render_text (window, widget, background_area, cell_area, expose_area, state, x, out x);
+
+      if (icon != null) {
+        x += (int) spacing;
+
+        /* render the icon */
+        render_icon (window, widget, background_area, cell_area, expose_area, state, x, out x);
+      }
+    } else {
+      int x = 0;
+
+      if (icon != null) {
+        /* compute the size of the icon */
+        int icon_width;
+        int icon_height;
+        compute_icon_size (widget, out icon_width, out icon_height);
+
+        /* compute icon area */
+        var icon_area = Gdk.Rectangle() {
+          x = cell_area.x + x,
+          y = cell_area.y,
+          width = int.min (cell_area.width - x, icon_width),
+          height = int.min (cell_area.height, icon_height)
+        };
+
+        /* render the icon */
+        render_icon (window, widget, background_area, icon_area, expose_area, state, x, out x);
+
+        x += (int) spacing;
+      }
+
+      /* compute text area */
+      var text_area = Gdk.Rectangle() {
+        x = cell_area.x + x,
+        y = cell_area.y,
+        width = cell_area.width - x,
+        height = cell_area.height
+      };
+
+      /* render the text */
+      render_text (window, widget, background_area, text_area, expose_area, state, x, out x);
+    }
+  }
+
+  private void render_icon (Gdk.Window window,
+                            Widget widget,
+                            Gdk.Rectangle background_area,
+                            Gdk.Rectangle cell_area,
+                            Gdk.Rectangle expose_area,
+                            StateType state,
+                            int x,
+                            out int new_x)
+  {
+    if (icon == null)
+      return;
+
+    Gdk.Pixbuf pixbuf = null;
+
+    /* load the icon */
+    if (icon is ThemedIcon) {
+      var icon_theme = IconTheme.get_default ();
+      var icon_info = icon_theme.lookup_by_gicon (icon, icon_size, IconLookupFlags.USE_BUILTIN);
+      if (icon_info != null) {
+        try {
+          pixbuf = icon_info.load_icon ();
+        } catch (Error e) {
+        }
+      }
+    } else if (icon is LoadableIcon) {
+    }
+
+    if (pixbuf == null)
+      return;
+
+    var icon_area = Gdk.Rectangle ();
+    var draw_area = Gdk.Rectangle ();
+
+    /* determine the real icon size */
+    icon_area.width = pixbuf.width;
+    icon_area.height = pixbuf.height;
+
+    /* scale down on demand */
+    if (icon_area.width > cell_area.width || icon_area.height > cell_area.height) {
+      pixbuf = pixbuf.scale_simple (cell_area.width, cell_area.height, Gdk.InterpType.BILINEAR);
+      icon_area.width = pixbuf.width;
+      icon_area.height = pixbuf.height;
+    }
+
+    icon_area.x = cell_area.x + (cell_area.width - icon_area.width) / 2;
+    icon_area.y = cell_area.y + (cell_area.height - icon_area.height) / 2;
+
+    new_x = x + icon_area.width;
+
+    if (expose_area.intersect (icon_area, out draw_area)) {
+      Gdk.draw_pixbuf (window, widget.style.black_gc, pixbuf,
+                       draw_area.x - icon_area.x,
+                       draw_area.y - icon_area.y,
+                       draw_area.x,
+                       draw_area.y,
+                       draw_area.width,
+                       draw_area.height,
+                       Gdk.RgbDither.NORMAL,
+                       0, 0);
+    }
+  }
+
+  private void render_text (Gdk.Window window,
+                            Widget widget,
+                            Gdk.Rectangle background_area,
+                            Gdk.Rectangle cell_area,
+                            Gdk.Rectangle expose_area,
+                            StateType state,
+                            int x,
+                            out int new_x)
+  {
+    layout.set_width (-1);
+    layout.set_wrap (Pango.WrapMode.CHAR);
+    layout.set_markup (markup, -1);
+
+    /* calculate the real text dimension */
+    int width;
+    int height;
+    layout.get_pixel_size (out width, out height);
+
+    int x_offset = 0;
+    int y_offset = 0;
+
+    Gtk.paint_layout (widget.style, window, state, true,
+                      expose_area, widget, "cellrenderertext",
+                      cell_area.x + x_offset + (int) xpad,
+                      cell_area.y + y_offset + (int) ypad,
+                      layout);
+  }
 }
 
 
@@ -375,23 +686,43 @@ public class ShortcutsView : Frame {
     model = new ShortcutsModel ();
 
     view = new TreeView ();
+    view.set_border_width (6);
     view.set_model (model);
-    view.set_headers_visible (true);
-    view.set_level_indentation (-16);
+    view.set_headers_visible (false);
+    view.set_level_indentation (-14);
     scrollwin.add (view);
     view.show ();
 
-    var icon_renderer = new CellRendererFlexibleIcon ();
+    view.get_selection ().set_select_function (select_row);
 
-    var name_renderer = new CellRendererText ();
+    var shortcut_renderer = new ShortcutRenderer ();
 
     var column = new TreeViewColumn ();
-    column.set_title ("Name");
-    column.pack_start (icon_renderer, false);
-    column.set_attributes (icon_renderer, "gicon", ShortcutsModel.Column.ICON);
-    column.pack_start (name_renderer, true);
-    column.set_attributes (name_renderer, "markup", ShortcutsModel.Column.NAME);
+    column.set_title ("Shortcut");
+    column.pack_start (shortcut_renderer, true);
+    column.set_attributes (shortcut_renderer,
+                           "icon", ShortcutsModel.Column.ICON,
+                           "markup", ShortcutsModel.Column.NAME,
+                           "sensitive", ShortcutsModel.Column.SENSITIVE);
     view.append_column(column);
+
+    view.expand_all ();
+  }
+
+  private bool select_row (TreeSelection selection,
+                           TreeModel model,
+                           TreePath path,
+                           bool currently_selected)
+  {
+    TreeIter iter;
+
+    if (model.get_iter (out iter, path)) {
+      bool is_separator = false;
+      model.get (iter, ShortcutsModel.Column.SEPARATOR, out is_separator, -1);
+      return !is_separator;
+    } else {
+      return false;
+    }
   }
 }
 



More information about the Xfce4-commits mailing list