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

Proposed by Victor Martinez
Status: Merged
Merge reported by: Tom Beckmann
Merged at revision: not available
Proposed branch: lp:~elementary-pantheon/granite/sidebar-paned
Merge into: lp:~elementary-pantheon/granite/granite
Diff against target: 352 lines (+337/-0)
2 files modified
lib/CMakeLists.txt (+1/-0)
lib/Widgets/SidebarPaned.vala (+336/-0)
To merge this branch: bzr merge lp:~elementary-pantheon/granite/sidebar-paned
Reviewer Review Type Date Requested Status
Tom Beckmann (community) Approve
Review via email: mp+118259@code.launchpad.net

Description of the change

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.

To post a comment you must log in.
Revision history for this message
Danielle Foré (danrabbit) wrote :

I'd like to say that I do think this widget is useful. I'd like to see all of our sidebar-using apps benefit from having an attractive thin style while maintaining a sane grab area.

337. By Victor Martinez

Fix remove()

Revision history for this message
Victor Martinez (victored) wrote :

Thanks for your comment Daniel :)

Revision history for this message
Sergey "Shnatsel" Davidoff (shnatsel) wrote :

Daniel, your wish should surface on the mailing list as soon as this is merged ;)

Revision history for this message
Tom Beckmann (tombeckmann) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/CMakeLists.txt'
2--- lib/CMakeLists.txt 2012-07-11 05:52:03 +0000
3+++ lib/CMakeLists.txt 2012-08-16 00:25:22 +0000
4@@ -80,6 +80,7 @@
5 Widgets/DecoratedWindow.vala
6 Widgets/LightWindow.vala
7 Widgets/StatusBar.vala
8+ Widgets/SidebarPaned.vala
9 Main.vala
10 config.vapi
11 PACKAGES
12
13=== added file 'lib/Widgets/SidebarPaned.vala'
14--- lib/Widgets/SidebarPaned.vala 1970-01-01 00:00:00 +0000
15+++ lib/Widgets/SidebarPaned.vala 2012-08-16 00:25:22 +0000
16@@ -0,0 +1,336 @@
17+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
18+/*
19+ * Copyright (c) 2012 Granite Developers
20+ *
21+ * This library is free software; you can redistribute it and/or
22+ * modify it under the terms of the GNU Lesser General Public License as
23+ * published by the Free Software Foundation; either version 2 of the
24+ * License, or (at your option) any later version.
25+ *
26+ * This is distributed in the hope that it will be useful,
27+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
28+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29+ * Lesser General Public License for more details.
30+ *
31+ * You should have received a copy of the GNU Lesser General Public
32+ * License along with this program; see the file COPYING. If not,
33+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
34+ * Boston, MA 02111-1307, USA.
35+ *
36+ * Authored by: Victor Eduardo <victoreduardm@gmail.com>
37+ */
38+
39+public class Granite.Widgets.SidebarPaned : Gtk.Overlay, Gtk.Orientable {
40+
41+ protected Gtk.Paned paned { get; private set; }
42+ private Gtk.EventBox? handle = null;
43+ private bool on_resize_mode = false;
44+ private Gdk.Cursor? arrow_cursor = null;
45+
46+ static const string STYLE_PROP_HANDLE_SIZE = "handle-size";
47+
48+ protected int handle_size {
49+ get {
50+ int size;
51+ style_get (STYLE_PROP_HANDLE_SIZE, out size);
52+ return size;
53+ }
54+ }
55+
56+ static construct {
57+ install_style_property (new ParamSpecInt (STYLE_PROP_HANDLE_SIZE,
58+ "Handle size",
59+ "Width of the invisible handle",
60+ 1, 50, 12,
61+ ParamFlags.READABLE));
62+ }
63+
64+ /**
65+ * PUBLIC API
66+ */
67+
68+ public Gtk.Orientation orientation {
69+ get { return this.paned.orientation; }
70+ set { set_orientation_internal (value); }
71+ }
72+
73+ public int position {
74+ get { return this.paned.position; }
75+ set { this.paned.position = value; }
76+ }
77+
78+ public bool position_set {
79+ get { return this.paned.position_set; }
80+ set { this.paned.position_set = value; }
81+ }
82+
83+ public void pack1 (Gtk.Widget child, bool resize, bool shrink) {
84+ this.paned.pack1 (child, resize, shrink);
85+ }
86+
87+ public void pack2 (Gtk.Widget child, bool resize, bool shrink) {
88+ this.paned.pack2 (child, resize, shrink);
89+ }
90+
91+ public void add1 (Gtk.Widget child) {
92+ this.paned.add1 (child);
93+ }
94+
95+ public void add2 (Gtk.Widget child) {
96+ this.paned.add2 (child);
97+ }
98+
99+ public new void remove (Gtk.Widget child) {
100+ this.paned.remove (child);
101+ }
102+
103+ public unowned Gtk.Widget? get_child1 () {
104+ return this.paned.get_child1 ();
105+ }
106+
107+ public unowned Gtk.Widget? get_child2 () {
108+ return this.paned.get_child2 ();
109+ }
110+
111+ public unowned Gdk.Window get_handle_window () {
112+ return this.handle.get_window ();
113+ }
114+
115+ public new void set_direction (Gtk.TextDirection dir) {
116+ this.paned.set_direction (dir);
117+ base.set_direction (dir);
118+ update_virtual_handle_position ();
119+ }
120+
121+ public new Gtk.TextDirection get_direction () {
122+ return this.paned.get_direction ();
123+ }
124+
125+ public new void set_default_direction (Gtk.TextDirection dir) {
126+ base.set_default_direction (dir);
127+ this.paned.set_default_direction (dir);
128+ }
129+
130+ public new Gtk.TextDirection get_default_direction () {
131+ return this.paned.get_default_direction ();
132+ }
133+
134+ public SidebarPaned () {
135+ this.paned.get_style_context ().add_class ("sidebar-pane-separator");
136+
137+ const string DEFAULT_STYLESHEET = """
138+ .sidebar-pane-separator {
139+ -GtkPaned-handle-size: 1px;
140+ }
141+ """;
142+
143+ const string FALLBACK_STYLESHEET = """
144+ GraniteWidgetsSidebarPaned .pane-separator {
145+ background-color: shade (@bg_color, 0.75);
146+ border-width: 0;
147+ }
148+ """;
149+
150+ set_theming (this.paned, DEFAULT_STYLESHEET, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
151+ set_theming (this.paned, FALLBACK_STYLESHEET, Gtk.STYLE_PROVIDER_PRIORITY_THEME);
152+ }
153+
154+
155+ /**
156+ * INTERNALS
157+ */
158+
159+ construct {
160+ push_composite_child ();
161+ this.paned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL);
162+ this.paned.set_composite_name ("paned");
163+ pop_composite_child ();
164+
165+ this.paned.expand = true;
166+
167+ this.add (this.paned);
168+
169+ Gdk.RGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
170+ this.override_background_color (0, transparent);
171+
172+ setup_handle ();
173+
174+ this.paned.size_allocate.connect_after (on_paned_size_allocate);
175+
176+ // The virtual handle will always follow the paned's position
177+ this.paned.notify["position"].connect (update_virtual_handle_position);
178+
179+ // We use POINTER_MOTION_HINT_MASK for performance reasons. It reduces the number
180+ // of motion events received.
181+ this.add_events (Gdk.EventMask.POINTER_MOTION_MASK
182+ | Gdk.EventMask.POINTER_MOTION_HINT_MASK);
183+
184+ // Set a proper initial status for internal widgets.
185+ this.position = -1;
186+ this.orientation = Gtk.Orientation.HORIZONTAL;
187+
188+ show_all ();
189+ }
190+
191+ private void setup_handle () {
192+ push_composite_child ();
193+ this.handle = new Gtk.EventBox ();
194+ this.handle.set_composite_name ("handle");
195+ pop_composite_child ();
196+
197+ Gdk.RGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
198+ this.handle.override_background_color (0, transparent);
199+
200+ this.add_overlay (handle);
201+
202+ this.handle.add_events (Gdk.EventMask.BUTTON_PRESS_MASK
203+ | Gdk.EventMask.BUTTON_RELEASE_MASK);
204+
205+ this.handle.button_press_event.connect (on_handle_button_press);
206+ this.handle.button_release_event.connect (on_handle_button_release);
207+ this.handle.grab_broken_event.connect (on_handle_grab_broken);
208+ this.handle.realize.connect (set_arrow_cursor);
209+ }
210+
211+ protected static void set_theming (Gtk.Widget widget, string stylesheet, int priority) {
212+ var css_provider = new Gtk.CssProvider ();
213+
214+ try {
215+ css_provider.load_from_data (stylesheet, -1);
216+ }
217+ catch (Error e) {
218+ warning (e.message);
219+ return_if_reached ();
220+ }
221+
222+ widget.get_style_context ().add_provider (css_provider, priority);
223+ }
224+
225+ public override bool motion_notify_event (Gdk.EventMotion e) {
226+ var device = e.device ?? Gtk.get_current_event_device ();
227+
228+ if (device == null) {
229+ var dev_manager = this.paned.get_display ().get_device_manager ();
230+ device = dev_manager.list_devices (Gdk.DeviceType.MASTER).nth_data (0);
231+ }
232+
233+ if (this.on_resize_mode && device != null) {
234+ var window = this.paned.get_window ();
235+
236+ if (window != null) {
237+ int x, y, pos = 0;
238+ window.get_device_position (device, out x, out y, null);
239+
240+ if (this.orientation == Gtk.Orientation.HORIZONTAL)
241+ pos = is_ltr() ? x : this.paned.get_allocated_width() - x;
242+ else
243+ pos = y;
244+
245+ if (this.paned.get_realized () && this.paned.get_mapped () && this.position_set)
246+ pos = pos.clamp (this.paned.min_position, this.paned.max_position);
247+
248+ this.position = pos;
249+ return true;
250+ }
251+ }
252+
253+ return false;
254+ }
255+
256+ private bool is_ltr () {
257+ var dir = get_direction ();
258+ if (dir == Gtk.TextDirection.NONE)
259+ dir = get_default_direction ();
260+ return dir == Gtk.TextDirection.LTR;
261+ }
262+
263+ private void set_orientation_internal (Gtk.Orientation orientation) {
264+ this.paned.orientation = orientation;
265+ bool horizontal = orientation == Gtk.Orientation.HORIZONTAL;
266+
267+ this.handle.hexpand = !horizontal;
268+ this.handle.vexpand = horizontal;
269+ this.handle.set_size_request (0, 0);
270+
271+ if (horizontal) {
272+ this.arrow_cursor = new Gdk.Cursor (Gdk.CursorType.SB_H_DOUBLE_ARROW);
273+ this.handle.margin_top = this.handle.margin_bottom = 0;
274+ this.handle.halign = Gtk.Align.START;
275+ this.handle.valign = Gtk.Align.FILL;
276+ } else {
277+ this.arrow_cursor = new Gdk.Cursor (Gdk.CursorType.SB_V_DOUBLE_ARROW);
278+ this.handle.margin_left = this.handle.margin_right = 0;
279+ this.handle.halign = Gtk.Align.FILL;
280+ this.handle.valign = Gtk.Align.START;
281+ }
282+
283+ on_paned_size_allocate ();
284+ update_virtual_handle_position ();
285+
286+ // Update cursor.
287+ set_arrow_cursor ();
288+ }
289+
290+ private void on_paned_size_allocate () {
291+ int size = this.handle_size;
292+ bool horizontal = this.orientation == Gtk.Orientation.HORIZONTAL;
293+
294+ // GtkPaned's handle disappears when one of its children is hidden, destroyed,
295+ // or simply hasn't been packed yet. The virtual handle reproduces that behavior.
296+ var paned_handle = this.paned.get_handle_window ();
297+ if (paned_handle != null) {
298+ this.handle.visible = paned_handle.is_visible ();
299+ size += horizontal ? paned_handle.get_width () : paned_handle.get_height ();
300+ }
301+
302+ if (horizontal)
303+ this.handle.set_size_request (size, -1);
304+ else
305+ this.handle.set_size_request (-1, size);
306+ }
307+
308+ private void update_virtual_handle_position () {
309+ int new_pos = this.position - this.handle_size / 2;
310+ new_pos = new_pos > 0 ? new_pos : 0;
311+
312+ if (this.orientation == Gtk.Orientation.HORIZONTAL) {
313+ bool is_ltr = is_ltr ();
314+ this.handle.halign = (is_ltr) ? Gtk.Align.START : Gtk.Align.END;
315+ this.handle.margin_left = (is_ltr) ? new_pos : 0;
316+ this.handle.margin_right = (is_ltr) ? 0 : new_pos;
317+ } else {
318+ this.handle.margin_top = new_pos;
319+ }
320+ }
321+
322+ private void set_arrow_cursor () {
323+ var window = this.handle.get_window ();
324+ if (window != null && window.get_cursor () != this.arrow_cursor)
325+ window.set_cursor (this.arrow_cursor);
326+ }
327+
328+ /**
329+ * Handle's Event Callbacks
330+ */
331+
332+ private bool on_handle_button_press (Gdk.EventButton e) {
333+ if (!this.on_resize_mode && e.button == Gdk.BUTTON_PRIMARY) {
334+ this.on_resize_mode = true;
335+ Gtk.grab_add (this.handle);
336+ return true;
337+ }
338+
339+ return false;
340+ }
341+
342+ private bool on_handle_button_release (Gdk.EventButton e) {
343+ this.on_resize_mode = false;
344+ Gtk.grab_remove (this.handle);
345+ return true;
346+ }
347+
348+ private bool on_handle_grab_broken (Gdk.EventGrabBroken e) {
349+ this.on_resize_mode = false;
350+ return true;
351+ }
352+}

Subscribers

People subscribed via source and target branches