Merge lp:~donadigo/screenshot-tool/box-selection into lp:~elementary-apps/screenshot-tool/trunk

Proposed by Adam Bieńkowski
Status: Merged
Approved by: Felipe Escoto
Approved revision: 223
Merged at revision: 219
Proposed branch: lp:~donadigo/screenshot-tool/box-selection
Merge into: lp:~elementary-apps/screenshot-tool/trunk
Diff against target: 599 lines (+203/-231)
4 files modified
src/Screenshot.vala (+1/-7)
src/ScreenshotWindow.vala (+87/-121)
src/Widgets/SaveDialog.vala (+0/-1)
src/Widgets/SelectionArea.vala (+115/-102)
To merge this branch: bzr merge lp:~donadigo/screenshot-tool/box-selection
Reviewer Review Type Date Requested Status
elementary Apps team Pending
Review via email: mp+304854@code.launchpad.net

Commit message

* Use box selection for area selection
* Ignore window struts

Description of the change

This branch changes the current behaviour of area selection to be a selection box rather than a static window which can be moved / resized.

It also fixes the issue with window struts which are now ignored by setting the window type to POPUP.

Previous iterations of this code were using "begin_resize_drag" which didn't really fit the purpose of box selection, so I changed it to manual handling of events much of how it is in the gnome-screenshot.

The diff also features a little bit of codestyle (and logic) fixes.

To post a comment you must log in.
221. By Adam Bieńkowski

Fix area command line argument

222. By Adam Bieńkowski

Public back to private

223. By Adam Bieńkowski

Actually close on escape from area selection

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Screenshot.vala'
2--- src/Screenshot.vala 2016-06-24 09:19:54 +0000
3+++ src/Screenshot.vala 2016-09-03 19:11:26 +0000
4@@ -142,13 +142,7 @@
5 } else {
6 window = new ScreenshotWindow.from_cmd (action, delay, grab_pointer, redact);
7 window.set_application (this);
8- window.show_all ();
9-
10- if (action != 3) {
11- window.take_clicked ();
12- } else {
13- window.present ();
14- }
15+ window.take_clicked ();
16 }
17
18 return 0;
19
20=== modified file 'src/ScreenshotWindow.vala'
21--- src/ScreenshotWindow.vala 2016-05-08 03:12:21 +0000
22+++ src/ScreenshotWindow.vala 2016-09-03 19:11:26 +0000
23@@ -47,9 +47,6 @@
24 private bool redact;
25 private int delay;
26
27- private Screenshot.Widgets.SelectionArea selection_area;
28- private Screenshot.Widgets.SaveDialog save_dialog;
29-
30 /**
31 * ScreenshotWindow Constructor
32 */
33@@ -189,33 +186,15 @@
34 */
35 all.toggled.connect (() => {
36 capture_mode = CaptureType.SCREEN;
37-
38- if (selection_area != null) {
39- selection_area.destroy ();
40- selection_area = null;
41- }
42 });
43
44 curr_window.toggled.connect (() => {
45 capture_mode = CaptureType.CURRENT_WINDOW;
46-
47- if (selection_area != null) {
48- selection_area.destroy ();
49- selection_area = null;
50- }
51 });
52
53 selection.toggled.connect (() => {
54 capture_mode = CaptureType.AREA;
55-
56- if (selection_area == null) {
57- selection_area = new Screenshot.Widgets.SelectionArea ();
58- selection_area.show_all ();
59- } else
60- selection_area.present ();
61-
62- set_transient_for (selection_area);
63- present();
64+ present ();
65 });
66
67 pointer_switch.notify["active"].connect (() => {
68@@ -241,84 +220,69 @@
69 take_btn.clicked.connect (take_clicked);
70 cancel_btn.clicked.connect (cancel_clicked);
71
72- focus_in_event.connect (() => {
73- if (selection_area != null && selection_area.is_visible ()) {
74- selection_area.present ();
75- this.present ();
76- } else {
77- this.present ();
78- }
79-
80- return false;
81- });
82-
83 // Pack the main grid into the window
84 Gtk.Box content = get_content_area () as Gtk.Box;
85 content.add (grid);
86 }
87
88- private bool grab_save (Gdk.Window win, bool extra_time) {
89+ private bool grab_save (Gdk.Window? win, bool extra_time) {
90 if (extra_time) {
91 redact_text (true);
92- Timeout.add (1000, () => {
93- grab_save (win, false);
94- return false;
95+ Timeout.add_seconds (1, () => {
96+ return grab_save (win, false);
97 });
98
99 return false;
100 }
101
102- Timeout.add (250, () => {
103- selection_area.set_opacity (1);
104- this.set_opacity (1);
105- return false;
106- });
107-
108- Gdk.Pixbuf screenshot;
109- Gdk.Rectangle win_rect;
110- int width, height;
111-
112- win_rect = Gdk.Rectangle ();
113-
114- width = win.get_width ();
115- height = win.get_height ();
116-
117- screenshot = Gdk.pixbuf_get_from_window (win, 0, 0, width, height);
118-
119- win_rect.x = 0;
120- win_rect.y = 0;
121- win_rect.width = width;
122- win_rect.height = height;
123-
124+ var win_rect = Gdk.Rectangle ();
125+ var root = Gdk.get_default_root_window ();
126+
127+ if (win == null) {
128+ win = root;
129+ }
130+
131+ Gdk.Pixbuf? screenshot;
132 if (capture_mode == CaptureType.AREA) {
133-
134- screenshot = new Gdk.Pixbuf.subpixbuf (screenshot, selection_area.x, selection_area.y, selection_area.w, selection_area.h);
135-
136- win_rect.x = selection_area.x;
137- win_rect.y = selection_area.y;
138- win_rect.width = selection_area.w;
139- win_rect.height = selection_area.h;
140+ Gdk.Rectangle selection_rect;
141+ win.get_frame_extents (out selection_rect);
142+
143+ screenshot = new Gdk.Pixbuf.subpixbuf (Gdk.pixbuf_get_from_window (root, 0, 0, root.get_width (), root.get_height ()),
144+ selection_rect.x, selection_rect.y, selection_rect.width, selection_rect.height);
145+
146+ win_rect.x = selection_rect.x;
147+ win_rect.y = selection_rect.y;
148+ win_rect.width = selection_rect.width;
149+ win_rect.height = selection_rect.height;
150+ } else {
151+ int width = win.get_width ();
152+ int height = win.get_height ();
153+
154+ screenshot = Gdk.pixbuf_get_from_window (win, 0, 0, width, height);
155+
156+ win_rect.x = 0;
157+ win_rect.y = 0;
158+ win_rect.width = width;
159+ win_rect.height = height;
160+ }
161+
162+ if (screenshot == null) {
163+ show_error_dialog ();
164+ return false;
165 }
166
167 if (mouse_pointer) {
168-
169- Gdk.Cursor cursor;
170- Gdk.Pixbuf cursor_pixbuf;
171-
172- cursor = new Gdk.Cursor.for_display (Gdk.Display.get_default (), Gdk.CursorType.LEFT_PTR);
173- cursor_pixbuf = cursor.get_image ();
174+ var cursor = new Gdk.Cursor.for_display (Gdk.Display.get_default (), Gdk.CursorType.LEFT_PTR);
175+ var cursor_pixbuf = cursor.get_image ();
176
177 if (cursor_pixbuf != null) {
178-
179- Gdk.DeviceManager manager;
180- Gdk.Device device;
181- Gdk.Rectangle cursor_rect;
182- int cx, cy;
183-
184- manager = Gdk.Display.get_default ().get_device_manager ();
185- device = manager.get_client_pointer ();
186+ var manager = Gdk.Display.get_default ().get_device_manager ();
187+ var device = manager.get_client_pointer ();
188+
189+ int cx, cy;
190 win.get_device_position (device, out cx, out cy, null);
191- cursor_rect = Gdk.Rectangle ();
192+
193+ var cursor_rect = Gdk.Rectangle ();
194
195 cursor_rect.x = cx + win_rect.x;
196 cursor_rect.y = cy + win_rect.y;
197@@ -335,12 +299,11 @@
198 redact_text (false);
199 }
200
201- save_dialog = new Screenshot.Widgets.SaveDialog (screenshot, settings, this);
202-
203+ var save_dialog = new Screenshot.Widgets.SaveDialog (screenshot, settings, this);
204 save_dialog.save_response.connect ((response, folder_dir, output_name, format) => {
205 save_dialog.destroy ();
206
207- if (response == true) {
208+ if (response) {
209 string[] formats = {".png", ".jpg", ".jpeg",".bmp", ".tiff"};
210 string output = output_name;
211
212@@ -353,16 +316,11 @@
213 try {
214 screenshot.save (file_name, format);
215
216- if (close_on_save == true) {
217+ if (close_on_save) {
218 this.destroy ();
219 }
220 } catch (GLib.Error e) {
221- Gtk.MessageDialog dialog = new Gtk.MessageDialog (this, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR,
222- Gtk.ButtonsType.CLOSE, _("Could not capture screenshot"));
223- dialog.secondary_text = _("Image not saved");
224- dialog.deletable = false;
225- dialog.run ();
226- dialog.destroy ();
227+ show_error_dialog ();
228 debug (e.message);
229 }
230 }
231@@ -376,6 +334,8 @@
232 this.destroy ();
233 });
234
235+ save_dialog.show_all ();
236+
237 return false;
238 }
239
240@@ -394,18 +354,12 @@
241 }
242
243 private void capture_screen () {
244- Gdk.Window win = null;
245-
246- win = Gdk.get_default_root_window();
247-
248- this.set_opacity (0);
249 this.hide ();
250- Timeout.add (delay * 1000 - (redact? 1000 : 0), () => {
251+
252+ Timeout.add_seconds (delay - (redact ? 1 : 0), () => {
253 this.present ();
254- grab_save (win, redact);
255- return false;
256+ return grab_save (null, redact);
257 });
258-
259 }
260
261 private void capture_window () {
262@@ -415,9 +369,8 @@
263
264 screen = Gdk.Screen.get_default ();
265
266- this.set_opacity (0);
267 this.hide ();
268- Timeout.add (delay * 1000 - (redact? 1000 : 0), () => {
269+ Timeout.add_seconds (delay - (redact ? 1 : 0), () => {
270 list = screen.get_window_stack ();
271 foreach (Gdk.Window item in list) {
272 if (screen.get_active_window () == item) {
273@@ -427,17 +380,15 @@
274
275 this.present ();
276
277- if (win != null)
278+ if (win != null) {
279 grab_save (win, redact);
280- else {
281+ } else {
282 Gtk.MessageDialog dialog = new Gtk.MessageDialog (this, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR,
283- Gtk.ButtonsType.CLOSE, _("Could not capture screenshot"));
284+ Gtk.ButtonsType.CLOSE, _("Could not capture screenshot"));
285 dialog.secondary_text = _("Couldn't find an active window");
286 dialog.deletable = false;
287 dialog.run ();
288 dialog.destroy ();
289-
290- this.set_opacity (1);
291 }
292
293 return false;
294@@ -445,22 +396,37 @@
295 }
296
297 private void capture_area () {
298- Gdk.Window win = null;
299-
300- win = Gdk.get_default_root_window();
301-
302- selection_area.set_opacity (0);
303- selection_area.hide ();
304- this.set_opacity (0);
305+ var selection_area = new Screenshot.Widgets.SelectionArea ();
306+ selection_area.show_all ();
307 this.hide ();
308
309- Timeout.add (delay * 1000 - (redact? 1000 : 0), () => {
310- this.present ();
311- selection_area.present ();
312- grab_save (win, redact);
313-
314- return false;
315- });
316+ selection_area.cancelled.connect (() => {
317+ selection_area.close ();
318+ if (close_on_save) {
319+ this.destroy ();
320+ } else {
321+ this.present ();
322+ }
323+ });
324+
325+ var win = selection_area.get_window ();
326+
327+ selection_area.captured.connect (() => {
328+ selection_area.close ();
329+ Timeout.add_seconds (delay - (redact ? 1 : 0), () => {
330+ this.present ();
331+ return grab_save (win, redact);
332+ });
333+ });
334+ }
335+
336+ private void show_error_dialog () {
337+ var dialog = new Gtk.MessageDialog (this, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR,
338+ Gtk.ButtonsType.CLOSE, _("Could not capture screenshot"));
339+ dialog.secondary_text = _("Image not saved");
340+ dialog.deletable = false;
341+ dialog.run ();
342+ dialog.destroy ();
343 }
344
345 private void redact_text (bool redact) {
346
347=== modified file 'src/Widgets/SaveDialog.vala'
348--- src/Widgets/SaveDialog.vala 2016-07-29 23:27:41 +0000
349+++ src/Widgets/SaveDialog.vala 2016-09-03 19:11:26 +0000
350@@ -50,7 +50,6 @@
351 folder_dir = settings.get_string ("folder-dir");
352
353 build (pixbuf, settings, parent);
354- show_all ();
355 name_entry.grab_focus ();
356 }
357
358
359=== modified file 'src/Widgets/SelectionArea.vala'
360--- src/Widgets/SelectionArea.vala 2016-03-09 14:14:00 +0000
361+++ src/Widgets/SelectionArea.vala 2016-09-03 19:11:26 +0000
362@@ -17,122 +17,135 @@
363 ***/
364
365 namespace Screenshot.Widgets {
366-
367- /**
368- * Code stolen from Eidete program with some adjustments.
369- */
370 public class SelectionArea : Granite.Widgets.CompositedWindow {
371-
372- private int[,] pos;
373-
374- public int x;
375- public int y;
376- public int w;
377- public int h;
378+ public signal void captured ();
379+ public signal void cancelled ();
380+
381+ private Gdk.Point start_point;
382+
383+ private bool dragging = false;
384+
385+ construct {
386+ type = Gtk.WindowType.POPUP;
387+ }
388
389 public SelectionArea () {
390-
391 stick ();
392 set_resizable (true);
393 set_deletable (false);
394 set_has_resize_grip (false);
395- set_default_geometry (640, 480);
396- set_type_hint (Gdk.WindowTypeHint.DIALOG);
397- events = Gdk.EventMask.BUTTON_MOTION_MASK | Gdk.EventMask.BUTTON1_MOTION_MASK |
398- Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK ;
399 set_skip_taskbar_hint (true);
400 set_skip_pager_hint (true);
401-
402- button_press_event.connect ((e) => {
403- Gdk.WindowEdge [] dir = {Gdk.WindowEdge.NORTH_WEST,
404- Gdk.WindowEdge.NORTH,Gdk.WindowEdge.NORTH_EAST,
405- Gdk.WindowEdge.EAST,Gdk.WindowEdge.SOUTH_EAST,Gdk.WindowEdge.SOUTH,
406- Gdk.WindowEdge.SOUTH_WEST,Gdk.WindowEdge.WEST};
407-
408- for (var i=0;i<8;i++){
409- if (in_quad (pos[i,0]-12, pos[i,1]-10, 24, 24, (int) e.x, (int) e.y)){
410- begin_resize_drag (dir[i], (int) e.button, (int) e.x_root, (int) e.y_root, e.time);
411-
412- return false;
413- }
414- }
415- begin_move_drag ((int) e.button, (int) e.x_root, (int) e.y_root, e.time);
416-
417- return false;
418- });
419-
420- configure_event.connect ((e) => {
421-
422- /**
423- * Check if coordinates are out of the screen and check
424- * if coordinate + width/height is out of the screen, then
425- * adjust coordinates to keep width and height (and aspect
426- * ratio) intact.
427- */
428- if(e.x < 0 || e.x > e.window.get_screen().get_width()) {
429- x = 0;
430- } else if (e.x + e.width > e.window.get_screen().get_width() && e.width < e.window.get_screen().get_width()) {
431- x = e.window.get_screen().get_width() - e.width;
432- } else {
433- x = e.x;
434- }
435-
436- if(e.y < 0) {
437- y = 0;
438- } else if (e.y + e.height >= e.window.get_screen().get_height() && e.height < e.window.get_screen().get_height()) {
439- y = e.window.get_screen().get_height() - e.height - 1;
440- } else {
441- y = e.y;
442- }
443-
444- /**
445- * Just in case an edge is still outside of the screen
446- * we'll modify the width/height if thats the case.
447- */
448- if (x + e.width > e.window.get_screen().get_width()) {
449- w = e.window.get_screen ().get_width() - x;
450- } else {
451- w = e.width;
452- }
453-
454- if(y + e.height > e.window.get_screen().get_height()) {
455- h = e.window.get_screen().get_height() - y;
456- } else {
457- h = e.height;
458- }
459-
460- return false;
461- });
462- }
463-
464- private bool in_quad (int qx, int qy, int qh, int qw, int x, int y){
465- return ((x>qx) && (x<(qx+qw)) && (y>qy) && (y<qy+qh));
466- }
467-
468- public override bool draw (Cairo.Context ctx){
469-
470- int w = this.get_allocated_width ();
471- int h = this.get_allocated_height ();
472- int r = 12;
473-
474- pos = {{1, 1}, // upper left
475- {w/2, 1}, // upper midpoint
476- {w-1, 1}, // upper right
477- {w-1, h/2}, // right midpoint
478- {w-1, h-1}, // lower right
479- {w/2, h-1}, // lower midpoint
480- {1, h-1}, // lower left
481- {1, h/2}}; // left midpoint
482+ set_keep_above (true);
483+
484+ var screen = get_screen ();
485+ set_default_size (screen.get_width (), screen.get_height ());
486+ }
487+
488+ public override bool button_press_event (Gdk.EventButton e) {
489+ if (dragging || e.button != 1) {
490+ return true;
491+ }
492+
493+ dragging = true;
494+
495+ start_point.x = (int)e.x_root;
496+ start_point.y = (int)e.y_root;
497+
498+ return true;
499+ }
500+
501+ public override bool button_release_event (Gdk.EventButton e) {
502+ if (!dragging || e.button != 1) {
503+ return true;
504+ }
505+
506+ dragging = false;
507+ captured ();
508+
509+ return true;
510+ }
511+
512+ public override bool motion_notify_event (Gdk.EventMotion e) {
513+ if (!dragging) {
514+ return true;
515+ }
516+
517+ int x = start_point.x;
518+ int y = start_point.y;
519+
520+ int width = (x - (int)e.x_root).abs ();
521+ int height = (y - (int)e.y_root).abs ();
522+ if (width < 1 || height < 1) {
523+ return true;
524+ }
525+
526+ x = int.min (x, (int)e.x_root);
527+ y = int.min (y, (int)e.y_root);
528+
529+ move (x, y);
530+ resize (width, height);
531+
532+ return true;
533+ }
534+
535+ public override bool key_press_event (Gdk.EventKey e) {
536+ if (e.keyval == Gdk.Key.Escape) {
537+ cancelled ();
538+ }
539+
540+ return true;
541+ }
542+
543+ public override void show_all () {
544+ base.show_all ();
545+ var manager = Gdk.Display.get_default ().get_device_manager ();
546+ var pointer = manager.get_client_pointer ();
547+ var keyboard = pointer.get_associated_device ();
548+ var window = get_window ();
549+
550+ var status = pointer.grab (window,
551+ Gdk.GrabOwnership.NONE,
552+ false,
553+ Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK,
554+ new Gdk.Cursor.for_display (window.get_display (), Gdk.CursorType.CROSSHAIR),
555+ Gtk.get_current_event_time ());
556+
557+ if (status != Gdk.GrabStatus.SUCCESS) {
558+ pointer.ungrab (Gtk.get_current_event_time ());
559+ }
560+
561+ if (keyboard != null) {
562+ status = keyboard.grab (window,
563+ Gdk.GrabOwnership.NONE,
564+ false,
565+ Gdk.EventMask.KEY_PRESS_MASK,
566+ null,
567+ Gtk.get_current_event_time ());
568+
569+ if (status != Gdk.GrabStatus.SUCCESS) {
570+ keyboard.ungrab (Gtk.get_current_event_time ());
571+ }
572+ }
573+ }
574+
575+ public new void close () {
576+ get_window ().set_cursor (null);
577+ base.close ();
578+ }
579+
580+ public override bool draw (Cairo.Context ctx) {
581+ if (!dragging) {
582+ return true;
583+ }
584+
585+ int w = get_allocated_width ();
586+ int h = get_allocated_height ();
587
588 ctx.rectangle (0, 0, w, h);
589 ctx.set_source_rgba (0.1, 0.1, 0.1, 0.2);
590 ctx.fill ();
591
592- for (var i=0;i<8;i++){
593- ctx.arc (pos[i,0], pos[i,1], r, 0.0, 2*3.14);
594- ctx.set_source_rgb (0.7, 0.7, 0.7);
595- ctx.fill ();
596- }
597 ctx.rectangle (0, 0, w, h);
598 ctx.set_source_rgb (0.7, 0.7, 0.7);
599 ctx.set_line_width (1.0);

Subscribers

People subscribed via source and target branches