← Back to team overview

elementaryart team mailing list archive

[Merge] lp:~elementary-pantheon/granite/sidebar-paned into lp:granite

 

Victor Eduardo has proposed merging lp:~elementary-pantheon/granite/sidebar-paned into lp:granite.

Requested reviews:
  elementary Pantheon team (elementary-pantheon)

For more details, see:
https://code.launchpad.net/~elementary-pantheon/granite/sidebar-paned/+merge/118259

This is Noise's SidebarPaned widget. I'm proposing it for merging into Granite because I thought it would be useful in other apps as well (Files, BeatBox, etc.)

In case you don't want to add yet-another widget to the library, or simply don't like the implementation, please reject it.
-- 
https://code.launchpad.net/~elementary-pantheon/granite/sidebar-paned/+merge/118259
Your team elementaryart (old) is subscribed to branch lp:granite.
=== modified file 'lib/CMakeLists.txt'
--- lib/CMakeLists.txt	2012-07-11 05:52:03 +0000
+++ lib/CMakeLists.txt	2012-08-05 01:47:17 +0000
@@ -80,6 +80,7 @@
     Widgets/DecoratedWindow.vala
     Widgets/LightWindow.vala
     Widgets/StatusBar.vala
+    Widgets/SidebarPaned.vala
     Main.vala
     config.vapi
 PACKAGES

=== added file 'lib/Widgets/SidebarPaned.vala'
--- lib/Widgets/SidebarPaned.vala	1970-01-01 00:00:00 +0000
+++ lib/Widgets/SidebarPaned.vala	2012-08-05 01:47:17 +0000
@@ -0,0 +1,332 @@
+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
+/*
+ * Copyright (c) 2012 Granite Developers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; see the file COPYING.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authored by: Victor Eduardo <victoreduardm@xxxxxxxxx>
+ */
+
+public class Granite.Widgets.SidebarPaned : Gtk.Overlay, Gtk.Orientable {
+
+    protected Gtk.Paned paned { get; private set; }
+    private Gtk.EventBox? handle = null;
+    private bool on_resize_mode = false;
+    private Gdk.Cursor? arrow_cursor = null;
+
+    static const string STYLE_PROP_HANDLE_SIZE = "handle-size";
+
+    protected int handle_size {
+        get {
+            int size;
+            style_get (STYLE_PROP_HANDLE_SIZE, out size);
+            return size;
+        }
+    }
+
+    static construct {
+        install_style_property (new ParamSpecInt (STYLE_PROP_HANDLE_SIZE,
+                                                  "Handle size",
+                                                  "Width of the invisible handle",
+                                                  1, 50, 12,
+                                                  ParamFlags.READABLE));
+    }
+
+    /**
+     * PUBLIC API
+     */
+
+    public Gtk.Orientation orientation {
+        get { return this.paned.orientation; }
+        set { set_orientation_internal (value); }
+    }
+
+    public int position {
+        get { return this.paned.position; }
+        set { this.paned.position = value; }
+    }
+
+    public bool position_set {
+        get { return this.paned.position_set; }
+        set { this.paned.position_set = value; }    
+    }
+
+    public void pack1 (Gtk.Widget child, bool resize, bool shrink) {
+        this.paned.pack1 (child, resize, shrink);
+    }
+
+    public void pack2 (Gtk.Widget child, bool resize, bool shrink) {
+        this.paned.pack2 (child, resize, shrink);
+    }
+
+    public void add1 (Gtk.Widget child) {
+        this.paned.add1 (child);
+    }
+
+    public void add2 (Gtk.Widget child) {
+        this.paned.add2 (child);
+    }
+
+    public unowned Gtk.Widget? get_child1 () {
+        return this.paned.get_child1 ();
+    }
+
+    public unowned Gtk.Widget? get_child2 () {
+        return this.paned.get_child2 ();
+    }
+
+    public unowned Gdk.Window get_handle_window () {
+        return this.handle.get_window ();
+    }
+
+    public new void set_direction (Gtk.TextDirection dir) {
+        this.paned.set_direction (dir);
+        base.set_direction (dir);
+        update_virtual_handle_position ();
+    }
+
+    public new Gtk.TextDirection get_direction () {
+        return this.paned.get_direction ();
+    }
+
+    public new void set_default_direction (Gtk.TextDirection dir) {
+        base.set_default_direction (dir);
+        this.paned.set_default_direction (dir);
+    }
+
+    public new Gtk.TextDirection get_default_direction () {
+        return this.paned.get_default_direction ();
+    }
+
+    public SidebarPaned () {
+        this.paned.get_style_context ().add_class ("sidebar-pane-separator");
+
+        const string DEFAULT_STYLESHEET = """
+            .sidebar-pane-separator {
+                -GtkPaned-handle-size: 1px;
+            }
+        """;
+
+        const string FALLBACK_STYLESHEET = """
+            GraniteWidgetsSidebarPaned .pane-separator {
+                background-color: shade (@bg_color, 0.75);
+                border-width: 0;
+            }
+        """;
+
+        set_theming (this.paned, DEFAULT_STYLESHEET, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+        set_theming (this.paned, FALLBACK_STYLESHEET, Gtk.STYLE_PROVIDER_PRIORITY_THEME);
+    }
+
+
+    /**
+     * INTERNALS
+     */
+
+    construct {
+        push_composite_child ();
+        this.paned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL);
+        this.paned.set_composite_name ("paned");
+        pop_composite_child ();
+
+        this.paned.expand = true;
+
+        this.add (this.paned);
+
+        Gdk.RGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
+        this.override_background_color (0, transparent);
+
+        setup_handle ();
+
+        this.paned.size_allocate.connect_after (on_paned_size_allocate);
+
+        // The virtual handle will always follow the paned's position
+        this.paned.notify["position"].connect (update_virtual_handle_position);
+
+        // We use POINTER_MOTION_HINT_MASK for performance reasons. It reduces the number
+        // of motion events received.
+        this.add_events (Gdk.EventMask.POINTER_MOTION_MASK
+                         | Gdk.EventMask.POINTER_MOTION_HINT_MASK);
+
+        // Set a proper initial status for internal widgets.
+        this.position = -1;
+        this.orientation = Gtk.Orientation.HORIZONTAL;
+
+        show_all ();
+    }
+
+    private void setup_handle () {
+        push_composite_child ();
+        this.handle = new Gtk.EventBox ();
+        this.handle.set_composite_name ("handle");
+        pop_composite_child ();
+
+        Gdk.RGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
+        this.handle.override_background_color (0, transparent);
+
+        this.add_overlay (handle);
+
+        this.handle.add_events (Gdk.EventMask.BUTTON_PRESS_MASK
+                               | Gdk.EventMask.BUTTON_RELEASE_MASK);
+
+        this.handle.button_press_event.connect (on_handle_button_press);
+        this.handle.button_release_event.connect (on_handle_button_release);
+        this.handle.grab_broken_event.connect (on_handle_grab_broken);
+        this.handle.realize.connect (set_arrow_cursor);
+    }
+
+    protected static void set_theming (Gtk.Widget widget, string stylesheet, int priority) {
+        var css_provider = new Gtk.CssProvider ();
+
+        try {
+            css_provider.load_from_data (stylesheet, -1);
+        }
+        catch (Error e) {
+            warning (e.message);
+            return_if_reached ();
+        }
+
+        widget.get_style_context ().add_provider (css_provider, priority);
+    }
+
+    public override bool motion_notify_event (Gdk.EventMotion e) {
+        var device = e.device ?? Gtk.get_current_event_device ();
+
+        if (device == null) {
+            var dev_manager = this.paned.get_display ().get_device_manager ();
+            device = dev_manager.list_devices (Gdk.DeviceType.MASTER).nth_data (0);
+        }
+
+        if (this.on_resize_mode && device != null) {
+            var window = this.paned.get_window ();
+
+            if (window != null) {
+                int x, y, pos = 0;
+                window.get_device_position (device, out x, out y, null);
+
+                if (this.orientation == Gtk.Orientation.HORIZONTAL)
+                    pos = is_ltr() ? x : this.paned.get_allocated_width() - x;
+                else
+                    pos = y;
+
+                if (this.paned.get_realized () && this.paned.get_mapped () && this.position_set)
+                    pos = pos.clamp (this.paned.min_position, this.paned.max_position);
+
+                this.position = pos;
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private bool is_ltr () {
+        var dir = get_direction ();
+        if (dir == Gtk.TextDirection.NONE)
+            dir = get_default_direction ();
+        return dir == Gtk.TextDirection.LTR;
+    }
+
+    private void set_orientation_internal (Gtk.Orientation orientation) {
+        this.paned.orientation = orientation;
+        bool horizontal = orientation == Gtk.Orientation.HORIZONTAL;
+
+        this.handle.hexpand = !horizontal;
+        this.handle.vexpand = horizontal;
+        this.handle.set_size_request (0, 0);
+
+        if (horizontal) {
+            this.arrow_cursor = new Gdk.Cursor (Gdk.CursorType.SB_H_DOUBLE_ARROW);
+            this.handle.margin_top = this.handle.margin_bottom = 0;
+            this.handle.halign = Gtk.Align.START;
+            this.handle.valign = Gtk.Align.FILL;
+        } else {
+            this.arrow_cursor = new Gdk.Cursor (Gdk.CursorType.SB_V_DOUBLE_ARROW);
+            this.handle.margin_left = this.handle.margin_right = 0;
+            this.handle.halign = Gtk.Align.FILL;
+            this.handle.valign = Gtk.Align.START;
+        }
+
+        on_paned_size_allocate ();
+        update_virtual_handle_position ();
+
+        // Update cursor.
+        set_arrow_cursor ();
+    }
+
+    private void on_paned_size_allocate () {
+        int size = this.handle_size;
+        bool horizontal = this.orientation == Gtk.Orientation.HORIZONTAL;
+
+        // GtkPaned's handle disappears when one of its children is hidden, destroyed,
+        // or simply hasn't been packed yet. The virtual handle reproduces that behavior.
+        var paned_handle = this.paned.get_handle_window ();
+        if (paned_handle != null) {
+            this.handle.visible = paned_handle.is_visible ();
+            size += horizontal ? paned_handle.get_width () : paned_handle.get_height ();
+        }
+
+        if (horizontal)
+            this.handle.set_size_request (size, -1);
+        else
+            this.handle.set_size_request (-1, size);
+    }
+
+    private void update_virtual_handle_position () {
+        int new_pos = this.position - this.handle_size / 2;
+        new_pos = new_pos > 0 ? new_pos : 0;
+
+        if (this.orientation == Gtk.Orientation.HORIZONTAL) {
+            bool is_ltr = is_ltr ();
+            this.handle.halign = (is_ltr) ? Gtk.Align.START : Gtk.Align.END;
+            this.handle.margin_left = (is_ltr) ? new_pos : 0;
+            this.handle.margin_right = (is_ltr) ? 0 : new_pos;
+        } else {
+            this.handle.margin_top = new_pos;
+        }
+    }
+
+    private void set_arrow_cursor () {
+        var window = this.handle.get_window ();
+        if (window != null && window.get_cursor () != this.arrow_cursor)
+            window.set_cursor (this.arrow_cursor);
+    }
+
+    /**
+     * Handle's Event Callbacks
+     */
+
+    private bool on_handle_button_press (Gdk.EventButton e) {
+        if (!this.on_resize_mode && e.button == Gdk.BUTTON_PRIMARY) {
+            this.on_resize_mode = true;
+            Gtk.grab_add (this.handle);
+            return true;
+        }
+
+        return false;
+    }
+
+    private bool on_handle_button_release (Gdk.EventButton e) {
+        this.on_resize_mode = false;
+        Gtk.grab_remove (this.handle);
+        return true;
+    }
+
+    private bool on_handle_grab_broken (Gdk.EventGrabBroken e) {
+        this.on_resize_mode = false;
+        return true;
+    }
+}
\ No newline at end of file


Follow ups