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