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

Subscribers

People subscribed via source and target branches