Merge lp:~elementary-pantheon/granite/sidebar-paned into lp:~elementary-pantheon/granite/granite
- sidebar-paned
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tom Beckmann (community) | Approve | ||
Review via email: mp+118259@code.launchpad.net |
Commit message
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 : | # |
- 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 | +} |
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.