Merge lp:~jeremywootten/pantheon-files/drag-tab-to-new-window into lp:~elementary-apps/pantheon-files/trunk

Proposed by Jeremy Wootten
Status: Merged
Approved by: Danielle Foré
Approved revision: 1970
Merged at revision: 2121
Proposed branch: lp:~jeremywootten/pantheon-files/drag-tab-to-new-window
Merge into: lp:~elementary-apps/pantheon-files/trunk
Diff against target: 371 lines (+162/-51)
4 files modified
src/Application.vala (+81/-26)
src/QuicklistHandler.vala (+1/-1)
src/View/Sidebar.vala (+1/-1)
src/View/Window.vala (+79/-23)
To merge this branch: bzr merge lp:~jeremywootten/pantheon-files/drag-tab-to-new-window
Reviewer Review Type Date Requested Status
Danielle Foré ux Approve
Review via email: mp+291111@code.launchpad.net

Commit message

Form new windows by dragging tabs; limit rate and number of new windows.

Description of the change

This branch addresses some issues around window creation.

1) Implement forming new windows by dragging tabs out of the window. The window is created where dropped.
2) A limit is put on the rate of window creation using the keyboard (Ctrl-N).
3) Only one function in Application is now allowed to create windows.

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

I think your idea about hiding the sidebar on new windows makes sense, but you've introduced an issue where if you close that first window it's impossible to get the sidebar back. Even killing everything and resetting the dconf key, I can't get the sidebar back. We should probably enforce that if you close the "main window" the sidebar will show on one of the other windows.

If I hold down Ctrl N, after the 3rd window everything crashes

review: Needs Fixing
Revision history for this message
Danielle Foré (danrabbit) wrote :

The expander is super ugly :/

Revision history for this message
Jeremy Wootten (jeremywootten) wrote :

Expander now removed. Instead the sidebar is allowed to shrink to zero width and is hidden or revealed just by changing the position of the paned (mouse) or by pressing F9 (keyboard). The shortcut only affects the currently focused window and does not change the "show-sidebar" setting.

The visible sidebar width (paned position not the sidebar allocated width) is saved when the first window is closed.

On reopening the visible width of the sidebar is restored to the larger of the minimum sidebar width (default = 96) or the saved width, unless the "show-sidebar" setting is false.

1970. By Jeremy Wootten

Implement drag tab to new window

Revision history for this message
Jeremy Wootten (jeremywootten) wrote :

As requested, the branch has been pruned to contain only the drag to new tab feature. A new branch will be created based on this one, to implement the auto-tiling and sidebar in new window management.

Revision history for this message
Danielle Foré (danrabbit) wrote :

I can confirm that this branch solves the two issues now attached to it.

review: Approve (ux)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Application.vala'
2--- src/Application.vala 2015-08-17 08:55:18 +0000
3+++ src/Application.vala 2016-04-21 11:55:35 +0000
4@@ -31,6 +31,7 @@
5 private Gtk.RecentManager recent;
6
7 private const int MARLIN_ACCEL_MAP_SAVE_DELAY = 15;
8+ private const uint MAX_WINDOWS = 25;
9
10 public int window_count { get; private set; }
11
12@@ -109,7 +110,7 @@
13 #endif
14
15 window_count = 0;
16- this.window_added.connect (() => {window_count++;});
17+ this.window_added.connect_after (() => {window_count++;});
18 this.window_removed.connect (() => {window_count--;});
19 }
20
21@@ -273,38 +274,78 @@
22 open_tabs (files);
23 else {
24 /* Open windows at each requested location. */
25- foreach (var file in files)
26- open_window (file);
27- }
28- }
29-
30- public void create_window (File location = File.new_for_path (Environment.get_home_dir ()),
31- Gdk.Screen screen = Gdk.Screen.get_default (),
32- Marlin.ViewMode viewmode = Marlin.ViewMode.PREFERRED) {
33-
34- open_window (location, screen, viewmode);
35- }
36-
37- private void open_window (File? location, Gdk.Screen screen = Gdk.Screen.get_default (), Marlin.ViewMode viewmode = Marlin.ViewMode.PREFERRED) {
38- (add_view_window (screen)).add_tab (location, viewmode);
39- }
40-
41- private Marlin.View.Window add_view_window (Gdk.Screen screen) {
42- var window = new Marlin.View.Window (this, screen);
43- this.add_window (window as Gtk.Window);
44- plugins.interface_loaded (window as Gtk.Widget);
45- return window;
46+ foreach (var file in files) {
47+ create_window (file);
48+ }
49+ }
50+ }
51+
52+ /* All window creation should be done via this function */
53+ public Marlin.View.Window? create_window (File location = File.new_for_path (Environment.get_home_dir ()),
54+ Marlin.ViewMode viewmode = Marlin.ViewMode.PREFERRED,
55+ int x = -1, int y = -1) {
56+
57+ if (this.get_windows ().length () >= MAX_WINDOWS) {
58+ return null;
59+ }
60+
61+ Marlin.View.Window win;
62+ Gdk.Rectangle? new_win_rect = null;
63+ Gdk.Screen screen = Gdk.Screen.get_default ();
64+ var aw = this.get_active_window ();
65+ if (aw != null) {
66+ /* This is not the first window - determine size and position of new window */
67+ int w, h;
68+ aw.get_size (out w, out h);
69+ /* Calculate difference between the visible width of the window and the width returned by Gtk+,
70+ * which might include client side decorations (shadow) in some versions (bug 756618).
71+ * Assumes top_menu stretches full visible width. */
72+ var tm_aw = ((Marlin.View.Window)aw).top_menu.get_allocated_width ();
73+ int shadow_width = (w - tm_aw) / 2;
74+ shadow_width -= 10; //Allow a small gap between adjacent windows
75+ screen = aw.get_screen ();
76+ if (x <= 0 || y <= 0) {
77+ /* Place holder for auto-tiling code. If missing then new window will be placed
78+ * at the default position (centre of screen) */
79+ } else { /* New window is a dropped tab */
80+ /* Move new window so that centre of upper edge just inside the window is at mouse
81+ * cursor position. This makes it easier for used to readjust window position with mouse if required.
82+ */
83+ x -= (shadow_width + w / 2);
84+ y -= (shadow_width + 6);
85+ new_win_rect = {x, y, w, h};
86+ }
87+ }
88+
89+ /* New window will not size or show itself if new_win_rect is not null */
90+ win = new Marlin.View.Window (this, screen, new_win_rect == null);
91+ this.add_window (win as Gtk.Window);
92+ plugins.interface_loaded (win as Gtk.Widget);
93+
94+ if (!win.is_first_window) { /* First window will restore tabs itself */
95+ win.add_tab (location, viewmode);
96+ }
97+
98+ if (new_win_rect != null) {
99+ move_resize_window (win, new_win_rect);
100+ win.show ();
101+ }
102+
103+ return win;
104 }
105
106 private void open_tabs (File[]? files, Gdk.Screen screen = Gdk.Screen.get_default ()) {
107 Marlin.View.Window window = null;
108
109 /* Get the first window, if any, else create a new window */
110- if (windows_exist ())
111+ if (windows_exist ()) {
112 window = (this.get_windows ()).data as Marlin.View.Window;
113- else
114- window = add_view_window (screen);
115-
116+ } else {
117+ window = create_window ();
118+ if (window == null) { /* Maximum number of windows reached */
119+ return;
120+ }
121+ }
122 if (files == null) {
123 /* Restore session if settings allow */
124 if (!Preferences.settings.get_boolean ("restore-tabs") || window.restore_tabs () < 1) {
125@@ -323,4 +364,18 @@
126 unowned List<weak Gtk.Window> windows = this.get_windows ();
127 return (windows != null && windows.data != null);
128 }
129+
130+ private void move_resize_window (Gtk.Window win, Gdk.Rectangle? rect) {
131+ if (rect == null) {
132+ return;
133+ }
134+
135+ if (rect.x > 0 && rect.y > 0) {
136+ win.move (rect.x, rect.y);
137+ }
138+ if (rect.width > 0 && rect.height > 0) {
139+ win.resize (rect.width, rect.height);
140+ }
141+ win.show ();
142+ }
143 }
144
145=== modified file 'src/QuicklistHandler.vala'
146--- src/QuicklistHandler.vala 2015-05-03 06:44:42 +0000
147+++ src/QuicklistHandler.vala 2016-04-21 11:55:35 +0000
148@@ -104,7 +104,7 @@
149 menuitem.property_set ("label", bookmark.label);
150 menuitem.item_activated.connect (() => {
151 var location = bookmark.get_location ();
152- Marlin.Application.get ().create_window (location, Gdk.Screen.get_default ());
153+ Marlin.Application.get ().create_window (location);
154 });
155
156 ql.child_add_position (menuitem, index);
157
158=== modified file 'src/View/Sidebar.vala'
159--- src/View/Sidebar.vala 2016-04-17 21:21:55 +0000
160+++ src/View/Sidebar.vala 2016-04-21 11:55:35 +0000
161@@ -1214,7 +1214,7 @@
162 var location = mount.get_default_location ();
163 if (flags == Marlin.OpenFlag.NEW_WINDOW) {
164 var app = Marlin.Application.get ();
165- app.create_window (location, window.get_screen ());
166+ app.create_window (location);
167 } else if (flags == Marlin.OpenFlag.NEW_TAB) {
168 window.add_tab (location, Marlin.ViewMode.CURRENT);
169 } else {
170
171=== modified file 'src/View/Window.vala'
172--- src/View/Window.vala 2016-04-12 08:47:09 +0000
173+++ src/View/Window.vala 2016-04-21 11:55:35 +0000
174@@ -57,6 +57,7 @@
175 public Chrome.ViewSwitcher view_switcher;
176 public Gtk.InfoBar info_bar;
177 public Granite.Widgets.DynamicNotebook tabs;
178+ private Gtk.Paned lside_pane;
179 public Marlin.Places.Sidebar sidebar;
180 public ViewContainer? current_tab = null;
181 public uint window_number;
182@@ -86,7 +87,8 @@
183 action_edit_path ();
184 }
185
186- public Window (Marlin.Application app, Gdk.Screen myscreen) {
187+ public Window (Marlin.Application app, Gdk.Screen myscreen, bool show_window = true) {
188+
189 /* Capture application window_count and active_window before they can change */
190 window_number = app.window_count;
191 application = app;
192@@ -107,12 +109,23 @@
193
194 connect_signals ();
195 make_bindings ();
196- show ();
197+
198+ if (show_window) { /* otherwise Application will size and show window */
199+ if (Preferences.settings.get_boolean("maximized")) {
200+ maximize();
201+ } else {
202+ resize (Preferences.settings.get_int("window-width"),
203+ Preferences.settings.get_int("window-height"));
204+ }
205+ show ();
206+ }
207 }
208
209 private void build_window () {
210- var lside_pane = new Gtk.Paned (Gtk.Orientation.HORIZONTAL);
211+ lside_pane = new Gtk.Paned (Gtk.Orientation.HORIZONTAL);
212 lside_pane.show ();
213+ /* Only show side bar in first window - (to be confirmed) */
214+
215 lside_pane.pack1 (sidebar, false, false);
216 lside_pane.pack2 (tabs, true, false);
217
218@@ -131,20 +144,30 @@
219 }
220
221 /** Apply preferences */
222- lside_pane.position = Preferences.settings.get_int ("sidebar-width");
223- get_action ("show_sidebar").set_state (Preferences.settings.get_boolean ("show-sidebar"));
224 get_action ("show_hidden").set_state (Preferences.settings.get_boolean ("show-hiddenfiles"));
225
226- set_default_size (Preferences.settings.get_int("window-width"),
227- Preferences.settings.get_int("window-height"));
228+ var show_sidebar_pref = Preferences.settings.get_boolean ("show-sidebar");
229+ get_action ("show_sidebar").set_state (show_sidebar_pref);
230+ show_sidebar (true);
231
232- if (Preferences.settings.get_boolean("maximized"))
233- maximize();
234+ if (is_first_window) {
235+ window_position = Gtk.WindowPosition.CENTER;
236+ } else { /* Allow new window created by tab dragging to be positioned where dropped */
237+ window_position = Gtk.WindowPosition.NONE;
238+ }
239 }
240
241 private void construct_sidebar () {
242 sidebar = new Marlin.Places.Sidebar (this);
243- sidebar.show ();
244+ }
245+
246+ public void show_sidebar (bool show = true) {
247+ var show_sidebar = (get_action ("show_sidebar")).state.get_boolean ();
248+ if (show && show_sidebar) {
249+ lside_pane.position = Preferences.settings.get_int ("sidebar-width");
250+ } else {
251+ lside_pane.position = 0;
252+ }
253 }
254
255 private void construct_notebook () {
256@@ -152,6 +175,8 @@
257 tabs.show_tabs = true;
258 tabs.allow_restoring = true;
259 tabs.allow_duplication = true;
260+ tabs.allow_new_window = true;
261+
262 this.configure_event.connect_after ((e) => {
263 tabs.set_size_request (e.width / 2, -1);
264 return false;
265@@ -296,6 +321,16 @@
266 add_tab (File.new_for_uri (((tab.page as ViewContainer).uri)));
267 });
268
269+ tabs.tab_moved.connect ((tab, x, y) => {
270+ var vc = tab.page as ViewContainer;
271+ ((Marlin.Application) application).create_window (vc.location, real_mode (vc.view_mode), x, y);
272+ /* A crash occurs if the original tab is removed while processing the signal */
273+ GLib.Idle.add (() => {
274+ remove_tab (vc);
275+ return false;
276+ });
277+ });
278+
279 sidebar.request_focus.connect (() => {
280 return !current_tab.locked_focus && !top_menu.locked_focus;
281 });
282@@ -306,11 +341,12 @@
283 }
284
285 private void make_bindings () {
286- /*Preference bindings */
287- Preferences.settings.bind("show-sidebar", sidebar, "visible", SettingsBindFlags.DEFAULT);
288+ if (is_first_window) {
289+ /*Preference bindings */
290+ Preferences.settings.bind("show-sidebar", sidebar, "visible", SettingsBindFlags.GET);
291+ Preferences.settings.bind("sidebar-width", lside_pane, "position", SettingsBindFlags.DEFAULT);
292
293- /* keyboard shortcuts bindings */
294- if (is_first_window) {
295+ /* keyboard shortcuts bindings */
296 unowned Gtk.BindingSet binding_set = Gtk.BindingSet.by_class (get_class ());
297 Gtk.BindingEntry.add_signal (binding_set, Gdk.keyval_from_name ("BackSpace"), 0, "go_up", 0);
298 Gtk.BindingEntry.add_signal (binding_set, Gdk.keyval_from_name ("XF86Back"), 0, "go_back", 0);
299@@ -444,8 +480,10 @@
300 tab.close ();
301 }
302
303- public void add_window(File location, Marlin.ViewMode mode){
304- ((Marlin.Application) application).create_window (location, screen, real_mode (mode));
305+ public void add_window (File location = File.new_for_path (Environment.get_home_dir ()),
306+ Marlin.ViewMode mode = Marlin.ViewMode.PREFERRED,
307+ int x = -1, int y = -1) {
308+ ((Marlin.Application) application).create_window (location, real_mode (mode), x, y);
309 }
310
311 private void undo_actions_set_insensitive () {
312@@ -491,8 +529,19 @@
313 }
314 }
315
316+ private bool adding_window = false;
317 private void action_new_window (GLib.SimpleAction action, GLib.Variant? param) {
318- (application as Marlin.Application).create_window ();
319+ /* Limit rate of adding new windows using the keyboard */
320+ if (adding_window) {
321+ return;
322+ } else {
323+ adding_window = true;
324+ add_window ();
325+ GLib.Timeout.add (500, () => {
326+ adding_window = false;
327+ return false;
328+ });
329+ }
330 }
331
332 private void action_quit (GLib.SimpleAction action, GLib.Variant? param) {
333@@ -680,7 +729,10 @@
334 private void change_state_show_sidebar (GLib.SimpleAction action) {
335 bool state = !action.state.get_boolean ();
336 action.set_state (new GLib.Variant.boolean (state));
337- Preferences.settings.set_boolean ("show-sidebar", state);
338+ if (!state) {
339+ Preferences.settings.set_int ("sidebar-width", lside_pane.position);
340+ }
341+ show_sidebar (state);
342 }
343
344 private void connect_to_server () {
345@@ -789,11 +841,7 @@
346 }
347
348 private void save_geometries () {
349- Gtk.Allocation sidebar_alloc;
350- sidebar.get_allocation (out sidebar_alloc);
351-
352- if (sidebar_alloc.width > 1)
353- Preferences.settings.set_int("sidebar-width", sidebar_alloc.width);
354+ save_sidebar_width ();
355
356 bool is_maximized = (bool) get_window().get_state() & Gdk.WindowState.MAXIMIZED;
357
358@@ -807,6 +855,14 @@
359 Preferences.settings.set_boolean("maximized", is_maximized);
360 }
361
362+ private void save_sidebar_width () {
363+ var sw = lside_pane.get_position ();
364+ var mw = Preferences.settings.get_int("minimum-sidebar-width");
365+
366+ sw = int.max (sw, mw);
367+ Preferences.settings.set_int("sidebar-width", sw);
368+ }
369+
370 private void save_tabs () {
371 VariantBuilder vb = new VariantBuilder (new VariantType ("a(uss)"));
372

Subscribers

People subscribed via source and target branches

to all changes: