Merge lp:~julien-spautz/granite/undo-close-tab into lp:~elementary-pantheon/granite/granite
- undo-close-tab
- Merge into granite
Status: | Merged |
---|---|
Merged at revision: | 589 |
Proposed branch: | lp:~julien-spautz/granite/undo-close-tab |
Merge into: | lp:~elementary-pantheon/granite/granite |
Diff against target: |
527 lines (+264/-57) 2 files modified
demo/main.vala (+49/-23) lib/Widgets/DynamicNotebook.vala (+215/-34) |
To merge this branch: | bzr merge lp:~julien-spautz/granite/undo-close-tab |
Related bugs: | |
Related blueprints: |
Undo Close Tab
(Medium)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
xapantu (community) | Approve | ||
Cody Garver (community) | Needs Fixing | ||
Danielle Foré | Approve | ||
David Gomes (community) | Needs Fixing | ||
Review via email: mp+155041@code.launchpad.net |
Commit message
Description of the change
Fixes bug #1020350 in granite by implementing this blueprint: https:/
I also updated granite-demo to allow duplicating and restoring tabs.
Existing applications will have to be patched to implement this feature, but this branch won't break anything, since it's disabled by default.
David Gomes (davidgomes) wrote : | # |
>void restore_
Also, maybe that function should be called void restore_
David Gomes (davidgomes) wrote : | # |
Newlines after lines 199, 186 too.
David Gomes (davidgomes) wrote : | # |
Also, the lines mentioned are diff lines.
Danielle Foré (danrabbit) wrote : | # |
Looks good here Julien. Nice work!
David Gomes (davidgomes) wrote : | # |
Daniel, don't you think that clicking the trash when there's only one tab to restore should just restore it instead of showing a list of to-restore tabs (that is actually 1 element long)? My 2 cents here.
Danielle Foré (danrabbit) wrote : | # |
David, hmm. Possibly, but maybe not because what if you're not sure what's in the trash?
David Gomes (davidgomes) wrote : | # |
I guess you're right. I'll finish this review tomorrow, it's a lot of code and it needs a lot of testing.
Also Daniel, which apps would ship the trash button? I can't think of any it'd look good on except for maybe Scratch.
It clutters the view on a web browser IMO.
Danielle Foré (danrabbit) wrote : | # |
Midori already includes the option to have the trash can (but in the toolbar). Do you think we should make showing the trash can optional?
Julien Spautz (julien-spautz) wrote : | # |
One thing I'm not sure about is what should happen when the closed tabs menu gets extremely long. In a web browser this can easily happen. The number of tabs (open and closed) can only grow, but never shrink. One solution would be to implement a maximum amount of closed tabs. There's no use in having a list with 50 closed tabs, also that's what history is for.
Danielle Foré (danrabbit) wrote : | # |
Hey Julien,
Perhaps we should look to Midori to see how their trash behaves. I agree with setting a reasonable limit. Perhaps 10 or 15?
I also noticed something small: I think the trash button should remain in the :active state while the menu is shown.
Julien Spautz (julien-spautz) wrote : | # |
:active meaning 'pressed'? I think it's currently using a normal button but I can change it to a toggle button. That should fix it…
Danielle Foré (danrabbit) wrote : | # |
Yep that's what I meant. Thanks!
Cody Garver (codygarver) wrote : | # |
This branch is delayed until DynamicNotebook is fixed.
Cody Garver (codygarver) wrote : | # |
bzr merge lp:~julien-spautz/granite/undo-close-tab
M demo/main.vala
M lib/Widgets/
Text conflict in lib/Widgets/
1 conflicts encountered.
- 556. By Julien Spautz
-
fixed conflicts
- 557. By Julien Spautz
-
fixed regressions
Julien Spautz (julien-spautz) wrote : | # |
The conflicts should now be resolved.
xapantu (xapantu) wrote : | # |
It looks good. However, I think that adding a tab member to the Tab class is not necessary and may be confusing. If you want to attach some data to a GObject, you can use the builtin set_data functions.
But apart from that, it looks great, so, I'll remove that and adapt the code in the demo before merging. Good work!
xapantu (xapantu) wrote : | # |
Oh, I read the code too quickly, I didn't see that it was actually used...
xapantu (xapantu) wrote : | # |
Merged, I just rename data to restore_data, the name data was a bit too generic for this in my opinion (and it is a public API, so we can't afford changing it every release ;) ).
Preview Diff
1 | === modified file 'demo/main.vala' |
2 | --- demo/main.vala 2013-06-17 00:40:26 +0000 |
3 | +++ demo/main.vala 2013-06-18 17:46:27 +0000 |
4 | @@ -377,35 +377,61 @@ |
5 | } |
6 | |
7 | private Granite.Widgets.DynamicNotebook create_dynamic_notebook () { |
8 | + |
9 | + int i = 3; |
10 | + |
11 | var dynamic_notebook = new Granite.Widgets.DynamicNotebook (); |
12 | |
13 | - var tab = new Granite.Widgets.Tab ("user@elementaryos: ~", |
14 | - new ThemedIcon ("empty"), |
15 | - new Gtk.Label ("Page 1")); |
16 | + dynamic_notebook.allow_duplication = true; |
17 | + dynamic_notebook.allow_restoring = true; |
18 | + |
19 | + var tab = new Granite.Widgets.Tab ("user1@elementaryos: ~", |
20 | + new ThemedIcon ("empty"), |
21 | + new Gtk.Label ("Page 1")); |
22 | + tab.data = "1"; |
23 | tab.working = true; |
24 | - |
25 | dynamic_notebook.insert_tab (tab, -1); |
26 | - dynamic_notebook.insert_tab (new Granite.Widgets.Tab ("user2@elementaryos: ~", |
27 | - new ThemedIcon ("empty"), |
28 | - new Gtk.Label ("Page 2")), -1); |
29 | + |
30 | + var tab2 = new Granite.Widgets.Tab ("user2@elementaryos: ~", |
31 | + new ThemedIcon ("empty"), |
32 | + new Gtk.Label ("Page 2")); |
33 | + tab2.data = "2"; |
34 | + dynamic_notebook.insert_tab (tab2, -1); |
35 | |
36 | dynamic_notebook.tab_added.connect ((t) => { |
37 | - t.page = new Gtk.Label ("newuser@elementaryos: ~"); |
38 | - t.label = "newuser@elementaryos: ~"; |
39 | - }); |
40 | - |
41 | - dynamic_notebook.tab_moved.connect ((t, p) => { |
42 | - print ("Moved tab %s to %i\n", t.label, p); |
43 | - }); |
44 | - |
45 | - dynamic_notebook.tab_switched.connect ((old_t, new_t) => { |
46 | - print ("Switched from %s to %s\n", old_t.label, new_t.label); |
47 | - }); |
48 | - |
49 | - dynamic_notebook.tab_removed.connect ((t) => { |
50 | - print ("Going to remove %s\n", t.label); |
51 | - return true; |
52 | - }); |
53 | + t.page = new Gtk.Label (@"Page $i"); |
54 | + t.label = @"user$i@elementaryos: ~"; |
55 | + t.data = i.to_string (); |
56 | + i++; |
57 | + }); |
58 | + |
59 | + dynamic_notebook.tab_restored.connect ((t) => { |
60 | + print ("Restored tab %s\n", t.label); |
61 | + t.page = new Gtk.Label ("Page " + t.data); |
62 | + }); |
63 | + |
64 | + dynamic_notebook.tab_duplicated.connect ((t) => { |
65 | + var num = t.data; |
66 | + var t2 = new Granite.Widgets.Tab (@"user$num@elementaryos: ~", |
67 | + new ThemedIcon ("empty"), |
68 | + new Gtk.Label (@"Page $num")); |
69 | + t2.data = t.data; |
70 | + dynamic_notebook.insert_tab (t2, -1); |
71 | + print ("Duplicated tab %s\n", t2.label); |
72 | + }); |
73 | + |
74 | + dynamic_notebook.tab_moved.connect ((t, p) => { |
75 | + print ("Moved tab %s to %i\n", t.label, p); |
76 | + }); |
77 | + |
78 | + dynamic_notebook.tab_switched.connect ((old_t, new_t) => { |
79 | + print ("Switched from %s to %s\n", old_t.label, new_t.label); |
80 | + }); |
81 | + |
82 | + dynamic_notebook.tab_removed.connect ((t) => { |
83 | + print ("Going to remove %s\n", t.label); |
84 | + return true; |
85 | + }); |
86 | |
87 | return dynamic_notebook; |
88 | } |
89 | |
90 | === modified file 'lib/Widgets/DynamicNotebook.vala' |
91 | --- lib/Widgets/DynamicNotebook.vala 2013-06-17 18:38:22 +0000 |
92 | +++ lib/Widgets/DynamicNotebook.vala 2013-06-18 17:46:27 +0000 |
93 | @@ -28,12 +28,15 @@ |
94 | * This is a standard tab which can be used in a notebook to form a tabbed UI. |
95 | */ |
96 | public class Tab : Gtk.Box { |
97 | + |
98 | Gtk.Label _label; |
99 | public string label { |
100 | get { return _label.label; } |
101 | set { _label.label = value; } |
102 | } |
103 | |
104 | + public string data { get; set; } |
105 | + |
106 | internal Gtk.EventBox page_container; |
107 | public Gtk.Widget page { |
108 | get { |
109 | @@ -101,7 +104,7 @@ |
110 | this.close = new Gtk.Button (); |
111 | |
112 | close.add (new Gtk.Image.from_icon_name ("window-close-symbolic", Gtk.IconSize.MENU)); |
113 | - close.tooltip_text = _("Close tab"); |
114 | + close.tooltip_text = _("Close Tab"); |
115 | close.relief = Gtk.ReliefStyle.NONE; |
116 | |
117 | var lbl = new Gtk.EventBox (); |
118 | @@ -147,7 +150,7 @@ |
119 | return true; |
120 | } |
121 | break; |
122 | - |
123 | + |
124 | case Gdk.ScrollDirection.DOWN: |
125 | case Gdk.ScrollDirection.RIGHT: |
126 | if (notebook.page < notebook.get_n_pages ()) { |
127 | @@ -156,14 +159,14 @@ |
128 | } |
129 | break; |
130 | } |
131 | - |
132 | + |
133 | return false; |
134 | }); |
135 | |
136 | lbl.button_press_event.connect ((e) => { |
137 | - |
138 | + |
139 | e.state &= MODIFIER_MASK; |
140 | - |
141 | + |
142 | if (e.button == 2 && e.state == 0 && close.visible) { |
143 | this.closed (); |
144 | } else if (e.button == 2 && e.state == Gdk.ModifierType.SHIFT_MASK && close.visible) { |
145 | @@ -174,25 +177,106 @@ |
146 | menu.popup (null, null, null, 3, e.time); |
147 | uint num_tabs = (this.get_parent () as Gtk.Container).get_children ().length (); |
148 | close_other_m.label = ngettext (_("Close Other Tab"), _("Close Other Tabs"), num_tabs - 1); |
149 | - if (num_tabs == 1) |
150 | - close_other_m.sensitive = false; |
151 | + close_other_m.sensitive = (num_tabs != 1); |
152 | } else { |
153 | return false; |
154 | } |
155 | - |
156 | + |
157 | return true; |
158 | }); |
159 | - |
160 | + |
161 | this.button_press_event.connect ((e) => { |
162 | return (e.type == Gdk.EventType.2BUTTON_PRESS || e.button != 1); |
163 | }); |
164 | - |
165 | + |
166 | page_container.button_press_event.connect (() => { return true; }); //dont let clicks pass through |
167 | close.clicked.connect ( () => this.closed () ); |
168 | working = false; |
169 | } |
170 | } |
171 | |
172 | + private class ClosedTabs : GLib.Object { |
173 | + |
174 | + public signal void restored (Tab tab); |
175 | + |
176 | + private struct Entry { |
177 | + string title; |
178 | + string data; |
179 | + GLib.Icon? icon; |
180 | + } |
181 | + |
182 | + private Entry[] closed_tabs; |
183 | + |
184 | + public ClosedTabs () { |
185 | + closed_tabs = {}; |
186 | + } |
187 | + |
188 | + public bool empty { |
189 | + get { |
190 | + return closed_tabs.length == 0; |
191 | + } |
192 | + } |
193 | + |
194 | + public void push (Tab tab) { |
195 | + foreach (var entry in closed_tabs) |
196 | + if (tab.data == entry.data) |
197 | + return; |
198 | + Entry tmp = { tab.label, tab.data, tab.icon }; |
199 | + closed_tabs += tmp; |
200 | + } |
201 | + |
202 | + public Tab pop () { |
203 | + assert (closed_tabs.length > 0); |
204 | + Entry entry = closed_tabs[closed_tabs.length - 1]; |
205 | + var tab = new Tab (entry.title); |
206 | + tab.data = entry.data; |
207 | + tab.icon = entry.icon; |
208 | + closed_tabs.resize (closed_tabs.length - 1); |
209 | + return tab; |
210 | + } |
211 | + |
212 | + public Tab? pick (string search) { |
213 | + var tab = (Tab) null; |
214 | + Entry[] copy = {}; |
215 | + |
216 | + foreach (var entry in closed_tabs) { |
217 | + if (entry.data != search) { |
218 | + copy += entry; |
219 | + } else { |
220 | + tab = new Tab (entry.title); |
221 | + tab.data = entry.data; |
222 | + tab.icon = entry.icon; |
223 | + } |
224 | + } |
225 | + |
226 | + closed_tabs = copy; |
227 | + return tab; |
228 | + } |
229 | + |
230 | + private Gtk.Menu _menu; |
231 | + public Gtk.Menu menu { |
232 | + get { |
233 | + _menu = new Gtk.Menu (); |
234 | + |
235 | + foreach (var entry in closed_tabs) { |
236 | + var item = new Gtk.ImageMenuItem.with_label (entry.title); |
237 | + item.set_always_show_image (true); |
238 | + if (entry.icon != null) { |
239 | + var icon = new Gtk.Image.from_gicon (entry.icon, Gtk.IconSize.MENU); |
240 | + item.set_image (icon); |
241 | + } |
242 | + _menu.prepend (item); |
243 | + |
244 | + item.activate.connect (() => { |
245 | + this.restored (pick (entry.data)); |
246 | + }); |
247 | + } |
248 | + |
249 | + return _menu; |
250 | + } |
251 | + } |
252 | + } |
253 | + |
254 | public class DynamicNotebook : Gtk.EventBox { |
255 | |
256 | /** |
257 | @@ -210,10 +294,10 @@ |
258 | set { notebook.show_tabs = value; } |
259 | } |
260 | |
261 | + /** |
262 | + * Toggle icon display |
263 | + */ |
264 | bool _show_icons; |
265 | - /** |
266 | - * Toggle icon display |
267 | - */ |
268 | public bool show_icons { |
269 | get { return _show_icons; } |
270 | set { |
271 | @@ -282,6 +366,19 @@ |
272 | } |
273 | } |
274 | |
275 | + /** |
276 | + * Allow restoring tabs |
277 | + */ |
278 | + bool _allow_restoring = false; |
279 | + public bool allow_restoring { |
280 | + get { return _allow_restoring; } |
281 | + set { |
282 | + _allow_restoring = value; |
283 | + restore_tab_m.visible = value; |
284 | + restore_button.visible = value; |
285 | + } |
286 | + } |
287 | + |
288 | public Tab current { |
289 | get { return tabs.nth_data (notebook.get_current_page ()); } |
290 | set { notebook.set_current_page (tabs.index (value)); } |
291 | @@ -298,6 +395,7 @@ |
292 | } |
293 | } |
294 | |
295 | + |
296 | public string group_name { |
297 | get { return notebook.group_name; } |
298 | set { notebook.group_name = value; } |
299 | @@ -308,6 +406,8 @@ |
300 | */ |
301 | public Gtk.Menu menu { get; private set; } |
302 | |
303 | + private ClosedTabs closed_tabs; |
304 | + |
305 | Gtk.Notebook notebook; |
306 | private Gtk.CssProvider button_fix; |
307 | |
308 | @@ -320,6 +420,13 @@ |
309 | public signal void tab_switched (Tab? old_tab, Tab new_tab); |
310 | public signal void tab_moved (Tab tab, int new_pos, bool new_window, int x, int y); |
311 | public signal void tab_duplicated (Tab duplicated_tab); |
312 | + public signal void tab_restored (Tab tab); |
313 | + |
314 | + private Gtk.MenuItem new_tab_m; |
315 | + private Gtk.MenuItem restore_tab_m; |
316 | + |
317 | + private Gtk.Button add_button; |
318 | + private Gtk.Button restore_button; // should be a Gtk.MenuButton when we have Gtk+ 3.6 |
319 | |
320 | private static const string CLOSE_BUTTON_STYLE = """ |
321 | * { |
322 | @@ -357,19 +464,72 @@ |
323 | this.add (this.notebook); |
324 | |
325 | menu = new Gtk.Menu (); |
326 | - |
327 | - var new_tab_m = new Gtk.MenuItem.with_label (_("New Tab")); |
328 | + new_tab_m = new Gtk.MenuItem.with_label (_("New Tab")); |
329 | + restore_tab_m = new Gtk.MenuItem.with_label (_("Undo Close Tab")); |
330 | + restore_tab_m.sensitive = false; |
331 | menu.append (new_tab_m); |
332 | - |
333 | + menu.append (restore_tab_m); |
334 | menu.show_all (); |
335 | |
336 | new_tab_m.activate.connect (() => { |
337 | insert_new_tab_at_end (); |
338 | }); |
339 | |
340 | + restore_tab_m.activate.connect (() => { |
341 | + restore_last_tab (); |
342 | + }); |
343 | + |
344 | + closed_tabs = new ClosedTabs (); |
345 | + closed_tabs.restored.connect ((tab) => { |
346 | + if (!allow_restoring) |
347 | + return; |
348 | + restore_button.sensitive = !closed_tabs.empty; |
349 | + insert_tab (tab, -1); |
350 | + this.tab_restored (tab); |
351 | + }); |
352 | + |
353 | + add_button = new Gtk.Button (); |
354 | + add_button.add (new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU)); |
355 | + add_button.margin_left = 6; |
356 | + add_button.relief = Gtk.ReliefStyle.NONE; |
357 | + add_button.tooltip_text = _("New Tab"); |
358 | + this.notebook.set_action_widget (add_button, Gtk.PackType.START); |
359 | + add_button.show_all (); |
360 | + add_button.get_style_context ().add_provider (button_fix, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); |
361 | + |
362 | + restore_button = new Gtk.Button (); |
363 | + restore_button.add (new Gtk.Image.from_icon_name ("user-trash-symbolic", Gtk.IconSize.MENU)); |
364 | + restore_button.margin_right = 5; |
365 | + restore_button.set_relief (Gtk.ReliefStyle.NONE); |
366 | + restore_button.tooltip_text = _("Closed Tabs"); |
367 | + restore_button.sensitive = false; |
368 | + this.notebook.set_action_widget (restore_button, Gtk.PackType.END); |
369 | + restore_button.show_all (); |
370 | + |
371 | + add_button.clicked.connect (() => { |
372 | + insert_new_tab_at_end (); |
373 | + }); |
374 | + |
375 | + restore_button.clicked.connect (() => { |
376 | + var menu = closed_tabs.menu; |
377 | + menu.attach_widget = restore_button; |
378 | + menu.show_all (); |
379 | + menu.popup (null, null, this.restore_menu_position, 1, 0); |
380 | + }); |
381 | + |
382 | + restore_tab_m.visible = allow_restoring; |
383 | + restore_button.visible = allow_restoring; |
384 | + |
385 | + this.size_allocate.connect (() => { |
386 | + this.recalc_size (); |
387 | + }); |
388 | + |
389 | this.button_press_event.connect ((e) => { |
390 | if (e.type == Gdk.EventType.2BUTTON_PRESS && e.button == 1) { |
391 | insert_new_tab_at_end (); |
392 | + } else if (e.button == 2 && allow_restoring) { |
393 | + restore_last_tab (); |
394 | + return true; |
395 | } else if (e.button == 3) { |
396 | menu.popup (null, null, null, 3, e.time); |
397 | } |
398 | @@ -377,28 +537,18 @@ |
399 | return false; |
400 | }); |
401 | |
402 | - var add = new Gtk.Button (); |
403 | - add.add (new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU)); |
404 | - add.margin_left = 6; |
405 | - add.relief = Gtk.ReliefStyle.NONE; |
406 | - add.tooltip_text = _("New tab"); |
407 | - this.notebook.set_action_widget (add, Gtk.PackType.START); |
408 | - add.show_all (); |
409 | - add.get_style_context ().add_provider (button_fix, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); |
410 | - |
411 | - add.clicked.connect ( () => { |
412 | - insert_new_tab_at_end (); |
413 | - }); |
414 | - |
415 | this.size_allocate.connect (() => { |
416 | this.recalc_size (); |
417 | }); |
418 | |
419 | this.key_press_event.connect ((e) => { |
420 | + |
421 | + e.state &= MODIFIER_MASK; |
422 | + |
423 | switch (e.keyval) { |
424 | case Gdk.Key.@w: |
425 | case Gdk.Key.@W: |
426 | - if ((e.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK) { |
427 | + if (e.state == Gdk.ModifierType.CONTROL_MASK) { |
428 | if (!tabs_closable) break; |
429 | remove_tab (current); |
430 | return true; |
431 | @@ -408,15 +558,18 @@ |
432 | |
433 | case Gdk.Key.@t: |
434 | case Gdk.Key.@T: |
435 | - if ((e.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK) { |
436 | + if (e.state == Gdk.ModifierType.CONTROL_MASK) { |
437 | insert_new_tab_at_end (); |
438 | return true; |
439 | + } else if (e.state == (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK) && allow_restoring) { |
440 | + restore_last_tab (); |
441 | + return true; |
442 | } |
443 | |
444 | break; |
445 | |
446 | case Gdk.Key.Page_Up: |
447 | - if ((e.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK) { |
448 | + if (e.state == Gdk.ModifierType.CONTROL_MASK) { |
449 | next_page (); |
450 | return true; |
451 | } |
452 | @@ -424,7 +577,7 @@ |
453 | break; |
454 | |
455 | case Gdk.Key.Page_Down: |
456 | - if ((e.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK) { |
457 | + if (e.state == Gdk.ModifierType.CONTROL_MASK) { |
458 | previous_page (); |
459 | return true; |
460 | } |
461 | @@ -447,6 +600,7 @@ |
462 | } |
463 | |
464 | break; |
465 | + |
466 | case Gdk.Key.@9: |
467 | if ((e.state & Gdk.ModifierType.MOD1_MASK) == Gdk.ModifierType.MOD1_MASK) { |
468 | notebook.page = notebook.get_n_pages () - 1; |
469 | @@ -470,6 +624,15 @@ |
470 | notebook.create_window.disconnect (on_create_window); |
471 | } |
472 | |
473 | + void restore_menu_position (Gtk.Menu menu, out int x, out int y, out bool p) { |
474 | + Gtk.Allocation button_alloc, menu_alloc; |
475 | + restore_button.get_allocation (out button_alloc); |
476 | + menu.get_allocation (out menu_alloc); |
477 | + restore_button.get_window ().get_origin (out x, out y); |
478 | + x += button_alloc.x - menu_alloc.width + button_alloc.width + 5; |
479 | + y += button_alloc.y + button_alloc.height + 1; |
480 | + } |
481 | + |
482 | void on_switch_page (Gtk.Widget page, uint pagenum) { |
483 | var new_tab = notebook.get_tab_label (page) as Tab; |
484 | |
485 | @@ -484,7 +647,7 @@ |
486 | unowned Gtk.Notebook on_create_window (Gtk.Widget page, int x, int y) { |
487 | var tab = notebook.get_tab_label (page) as Tab; |
488 | tab_moved (tab, 0, true, x, y); |
489 | - return null; |
490 | + return (Gtk.Notebook) null; |
491 | } |
492 | |
493 | private void recalc_size () { |
494 | @@ -504,6 +667,17 @@ |
495 | } |
496 | } |
497 | |
498 | + private void restore_last_tab () { |
499 | + if (!allow_restoring || closed_tabs.empty) |
500 | + return; |
501 | + |
502 | + var tab = closed_tabs.pop (); |
503 | + restore_button.sensitive = !closed_tabs.empty; |
504 | + restore_tab_m.sensitive = !closed_tabs.empty; |
505 | + insert_tab (tab, -1); |
506 | + this.tab_restored (tab); |
507 | + } |
508 | + |
509 | public void remove_tab (Tab tab) { |
510 | if (Signal.has_handler_pending (this, Signal.lookup ("tab-removed", typeof (DynamicNotebook)), 0, true)) { |
511 | var sure = tab_removed (tab); |
512 | @@ -512,8 +686,15 @@ |
513 | } |
514 | |
515 | var pos = get_tab_position (tab); |
516 | + |
517 | if (pos != -1) |
518 | notebook.remove_page (pos); |
519 | + |
520 | + if (tab.label != "" && tab.data != "") { |
521 | + closed_tabs.push (tab); |
522 | + restore_button.sensitive = !closed_tabs.empty; |
523 | + restore_tab_m.sensitive = !closed_tabs.empty; |
524 | + } |
525 | } |
526 | |
527 | [Deprecated (since=0.2)] |
Align lines 30, 31, 47, 53 and 54.
Newline after lines 54, 304, 150, 157, 189, 193.
Align line 57.
Remove all trailing whitespace (I wouldn't normally ask you to do this, but you added a lot of trailing whitespace on this branch).
That is just code style, I don't have time to read actual code or test it now.