Merge lp:~artem-anufrij/audience/library-context-menu into lp:~audience-members/audience/trunk
- library-context-menu
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Danielle Foré | ||||
Approved revision: | 679 | ||||
Merged at revision: | 666 | ||||
Proposed branch: | lp:~artem-anufrij/audience/library-context-menu | ||||
Merge into: | lp:~audience-members/audience/trunk | ||||
Diff against target: |
623 lines (+254/-90) 5 files modified
src/Objects/Video.vala (+51/-30) src/Services/LibraryManager.vala (+54/-36) src/Widgets/LibraryItem.vala (+72/-12) src/Widgets/LibraryPage.vala (+19/-6) src/Window.vala (+58/-6) |
||||
To merge this branch: | bzr merge lp:~artem-anufrij/audience/library-context-menu | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Danielle Foré | ux | Approve | |
Review via email: mp+306675@code.launchpad.net |
Commit message
Add right click menu to set artwork and move library items to trash
Description of the change
- 663. By Artem Anufrij
-
renamed context menu items
- 664. By Artem Anufrij
-
renamed context menu items
- 665. By Artem Anufrij
-
left click fixed
- 666. By Artem Anufrij
-
removed 'restore artwork'
- 667. By Artem Anufrij
-
context menu: renaming and setting artwork
- 668. By Artem Anufrij
-
added menu item 'move to trash
- 669. By Artem Anufrij
-
added menu item 'move to trash
- 670. By Artem Anufrij
-
added a separator
- 671. By Artem Anufrij
-
improved renaming videos
Danielle Foré (danrabbit) wrote : | # |
For the rename stack, you should probably use a crossfade instead of a slide since we're changing the item state and not really navigating or moving a thing.
I think "Move to Trash" needs to come with an undo pattern. Specifically both ctrl + z and I would like to make use of undo via in-app notifications like in Online Accounts.
In this latest revision, I seem to be missing the "Set Artwork" item. Is that intentional?
Artem Anufrij (artem-anufrij) wrote : | # |
"Set Artwork" is hidden for videos with a cover in same folder:
videos/myvideo.mpg
videos/cover.jpg
Artworks can be set only for videos without a cover file.
what do you think about that?
Danielle Foré (danrabbit) wrote : | # |
I think that ideally people don't have to open the file manager to change the artwork. The library view should be self sufficient.
- 672. By Artem Anufrij
-
ctrl+z for undo trashed files & set artwork for all items
- 673. By Artem Anufrij
-
improved thumbnails
Danielle Foré (danrabbit) wrote : | # |
* I can't confirm that Set Artwork works anymore
* Having a separator between rename and set artwork seems excessive since both of these items are about editing metadata
* Just making a note about using in-app notifications for undo like we talked in Slack
* If I move both items to trash, I think it should go back to the welcome screen instead of showing an empty Library
* Changing the name seems to change the artwork. I think that's probably not desired
- 674. By Artem Anufrij
-
added a notification box
- 675. By Artem Anufrij
-
back to welcomescreen after deleting last item
Danielle Foré (danrabbit) wrote : | # |
* I think when you've removed all items it should return to the welcome immediately and not wait until after you've clicked the close button. See online accounts. I think this makes it faster to get to the next action
* It looks like there's an issue where if you remove everything, go back to the welcome, then restore those items in files, then go back to the library, you'll get an empty app notification (just shows as a thin white bar)
- 676. By Artem Anufrij
-
removed 'rename' function; fixed 'set artwork'
- 677. By Artem Anufrij
-
moved notification box into main window
- 678. By Artem Anufrij
-
grab_focus for scrolled window
Danielle Foré (danrabbit) wrote : | # |
Hey right on. This seems to work fully as expected to me :)
- 679. By Artem Anufrij
-
fixed tiny line
Preview Diff
1 | === modified file 'src/Objects/Video.vala' |
2 | --- src/Objects/Video.vala 2016-09-22 15:39:32 +0000 |
3 | +++ src/Objects/Video.vala 2016-09-25 00:39:06 +0000 |
4 | @@ -24,13 +24,14 @@ |
5 | |
6 | public signal void poster_changed (); |
7 | public signal void title_changed (); |
8 | + public signal void trashed (Video video); |
9 | |
10 | public File video_file { get; private set; } |
11 | public string directory { get; construct set; } |
12 | public string file { get; construct set; } |
13 | |
14 | public string title { get; private set; } |
15 | - public int year { get; private set; } |
16 | + public int year { get; private set; default = -1;} |
17 | |
18 | public Gdk.Pixbuf? poster { get; private set; } |
19 | |
20 | @@ -38,6 +39,7 @@ |
21 | public string poster_cache_file { get; private set; } |
22 | |
23 | public string hash { get; construct set; } |
24 | + public string thumbnail_large_path { get; construct set;} |
25 | |
26 | public Video (string directory, string file, string mime_type) { |
27 | Object (directory: directory, file: file, mime_type: mime_type); |
28 | @@ -52,8 +54,9 @@ |
29 | this.extract_metadata (); |
30 | video_file = File.new_for_path (this.get_path ()); |
31 | |
32 | - hash = GLib.Checksum.compute_for_string (ChecksumType.MD5, this.get_path (), this.get_path ().length); |
33 | + hash = GLib.Checksum.compute_for_string (ChecksumType.MD5, video_file.get_uri (), video_file.get_uri ().length); |
34 | |
35 | + thumbnail_large_path = Path.build_filename (GLib.Environment.get_user_cache_dir (),"thumbnails", "large", hash + ".png"); |
36 | poster_cache_file = Path.build_filename (App.get_instance ().get_cache_directory (), hash + ".jpg"); |
37 | |
38 | notify["poster"].connect (() => { |
39 | @@ -69,7 +72,7 @@ |
40 | MatchInfo info; |
41 | if (manager.regex_year.match (this.title, 0, out info)) { |
42 | this.year = int.parse (info.fetch (0).substring (1, 4)); |
43 | - this.title = this.title.replace (info.fetch (0) + ")", ""); |
44 | + this.title = this.title.replace (info.fetch (0) + ")", "").strip (); |
45 | } |
46 | } |
47 | |
48 | @@ -85,7 +88,7 @@ |
49 | |
50 | ThreadFunc<void*> run = () => { |
51 | |
52 | - string poster_path = poster_cache_file; |
53 | + string? poster_path = poster_cache_file; |
54 | pixbuf = get_poster_from_file (poster_path); |
55 | |
56 | // POSTER in Cache exists |
57 | @@ -94,24 +97,9 @@ |
58 | return null; |
59 | } |
60 | |
61 | - // Try to find a POSTER in same folder of video file |
62 | - if (pixbuf == null) { |
63 | - poster_path = this.get_path () + ".jpg"; |
64 | - pixbuf = get_poster_from_file (poster_path); |
65 | - } |
66 | - |
67 | - if (pixbuf == null) { |
68 | - poster_path = Path.build_filename (this.directory, Audience.get_title (file) + ".jpg"); |
69 | - pixbuf = get_poster_from_file (poster_path); |
70 | - } |
71 | - |
72 | - foreach (string s in Audience.settings.poster_names) { |
73 | - if (pixbuf == null) { |
74 | - poster_path = Path.build_filename (this.directory, s); |
75 | - pixbuf = get_poster_from_file (poster_path); |
76 | - } else { |
77 | - break; |
78 | - } |
79 | + poster_path = get_native_poster_path (); |
80 | + if (poster_path != null) { |
81 | + pixbuf = get_poster_from_file (poster_path); |
82 | } |
83 | |
84 | // POSTER found |
85 | @@ -126,9 +114,8 @@ |
86 | } |
87 | |
88 | // Check if THUMBNAIL exists |
89 | - string? thumbnail_path = manager.get_thumbnail_path (video_file); |
90 | - if (thumbnail_path != null) { |
91 | - pixbuf = get_poster_from_file (thumbnail_path); |
92 | + if (File.new_for_path (thumbnail_large_path).query_exists ()) { |
93 | + pixbuf = get_poster_from_file (thumbnail_large_path); |
94 | Idle.add ((owned) callback); |
95 | return null; |
96 | } |
97 | @@ -158,11 +145,8 @@ |
98 | } |
99 | |
100 | private void dbus_finished (uint heandle) { |
101 | - if (poster == null) { |
102 | - string? thumbnail_path = manager.get_thumbnail_path (video_file); |
103 | - if (thumbnail_path != null) { |
104 | - poster = get_poster_from_file (thumbnail_path); |
105 | - } |
106 | + if (poster == null && File.new_for_path (thumbnail_large_path).query_exists ()) { |
107 | + poster = get_poster_from_file (thumbnail_large_path); |
108 | } |
109 | } |
110 | |
111 | @@ -192,5 +176,42 @@ |
112 | |
113 | return pixbuf; |
114 | } |
115 | + |
116 | + public string? get_native_poster_path () { |
117 | + string poster_path = this.get_path () + ".jpg"; |
118 | + File file_poster = File.new_for_path (poster_path); |
119 | + |
120 | + if (file_poster.query_exists ()) |
121 | + return poster_path; |
122 | + |
123 | + poster_path = Path.build_filename (this.directory, Audience.get_title (file) + ".jpg"); |
124 | + file_poster = File.new_for_path (poster_path); |
125 | + |
126 | + if (file_poster.query_exists ()) |
127 | + return poster_path; |
128 | + |
129 | + foreach (string s in Audience.settings.poster_names) { |
130 | + poster_path = Path.build_filename (this.directory, s); |
131 | + file_poster = File.new_for_path (poster_path); |
132 | + if (file_poster.query_exists ()) |
133 | + return poster_path; |
134 | + } |
135 | + |
136 | + return null; |
137 | + } |
138 | + |
139 | + public void set_new_poster (Gdk.Pixbuf? new_poster) { |
140 | + manager.clear_cache (this); |
141 | + poster = new_poster; |
142 | + } |
143 | + |
144 | + public void trash () { |
145 | + try { |
146 | + video_file.trash (); |
147 | + trashed (this); |
148 | + } catch (Error e) { |
149 | + warning (e.message); |
150 | + } |
151 | + } |
152 | } |
153 | } |
154 | |
155 | === modified file 'src/Services/LibraryManager.vala' |
156 | --- src/Services/LibraryManager.vala 2016-09-22 15:22:27 +0000 |
157 | +++ src/Services/LibraryManager.vala 2016-09-25 00:39:06 +0000 |
158 | @@ -27,6 +27,7 @@ |
159 | |
160 | public signal void video_file_detected (Audience.Objects.Video video); |
161 | public signal void video_file_deleted (string path); |
162 | + public signal void video_moved_to_trash (Audience.Objects.Video video); |
163 | public signal void finished (); |
164 | |
165 | public Regex regex_year { get; construct set; } |
166 | @@ -37,6 +38,8 @@ |
167 | private Gee.ArrayList<string> poster_hash; |
168 | private Gee.ArrayList<FileMonitor> monitoring_directories; |
169 | |
170 | + private Gee.ArrayList<Audience.Objects.Video> trashed_files; |
171 | + |
172 | public static LibraryManager instance = null; |
173 | public static LibraryManager get_instance () { |
174 | if (instance == null) { |
175 | @@ -50,6 +53,7 @@ |
176 | } |
177 | |
178 | construct { |
179 | + trashed_files = new Gee.ArrayList<Audience.Objects.Video> (); |
180 | poster_hash = new Gee.ArrayList<string> (); |
181 | monitoring_directories = new Gee.ArrayList<FileMonitor> (); |
182 | try { |
183 | @@ -123,38 +127,11 @@ |
184 | } |
185 | var video = new Audience.Objects.Video (source, name, file_info.get_content_type ()); |
186 | video_file_detected (video); |
187 | + video.trashed.connect (deleted_items); |
188 | poster_hash.add (video.hash + ".jpg"); |
189 | has_items = true; |
190 | } |
191 | |
192 | - public string? get_thumbnail_path (File file) { |
193 | - if (!file.is_native ()) { |
194 | - return null; |
195 | - } |
196 | - string? path = null; |
197 | - try { |
198 | - var info = file.query_info (FileAttribute.THUMBNAIL_PATH + "," + FileAttribute.THUMBNAILING_FAILED, FileQueryInfoFlags.NONE); |
199 | - path = info.get_attribute_as_string (FileAttribute.THUMBNAIL_PATH); |
200 | - var failed = info.get_attribute_boolean (FileAttribute.THUMBNAILING_FAILED); |
201 | - |
202 | - if (failed || path == null) { |
203 | - return null; |
204 | - } |
205 | - |
206 | - path = path.replace ("normal", "large"); |
207 | - |
208 | - File large_thumbnail = File.new_for_path (path); |
209 | - if (!large_thumbnail.query_exists ()) { |
210 | - return null; |
211 | - } |
212 | - } catch (Error e) { |
213 | - warning (e.message); |
214 | - return null; |
215 | - } |
216 | - |
217 | - return path; |
218 | - } |
219 | - |
220 | public void clear_cache (Audience.Objects.Video video) { |
221 | File file = File.new_for_path (video.poster_cache_file); |
222 | if (file.query_exists ()) { |
223 | @@ -163,20 +140,61 @@ |
224 | } |
225 | |
226 | public async void clear_unused_cache_files () { |
227 | - File directory = File.new_for_path (App.get_instance ().get_cache_directory ()); |
228 | + string[] hash_items = poster_hash.to_array (); |
229 | + ThreadFunc<void*> run = () => { |
230 | + |
231 | + File directory = File.new_for_path (App.get_instance ().get_cache_directory ()); |
232 | + try { |
233 | + var children = directory.enumerate_children (FileAttribute.STANDARD_NAME, 0); |
234 | + if (children != null) { |
235 | + FileInfo file_info; |
236 | + while ((file_info = children.next_file ()) != null) { |
237 | + foreach (unowned string hash_item in hash_items) { |
238 | + if (hash_item == file_info.get_name ()) { |
239 | + continue; |
240 | + } |
241 | + children.get_child (file_info).delete_async.begin (); |
242 | + } |
243 | + } |
244 | + } |
245 | + } catch (Error e) { |
246 | + warning (e.message); |
247 | + } |
248 | + |
249 | + return null; |
250 | + }; |
251 | + |
252 | try { |
253 | - var children = directory.enumerate_children (FileAttribute.STANDARD_NAME, 0); |
254 | - |
255 | - if (children != null) { |
256 | + new Thread<void*>.try (null, run); |
257 | + } catch (Error e) { |
258 | + error (e.message); |
259 | + } |
260 | + } |
261 | + |
262 | + private void deleted_items (Audience.Objects.Video video) { |
263 | + trashed_files.add (video); |
264 | + video_moved_to_trash (video); |
265 | + } |
266 | + |
267 | + public void undo_delete_item () { |
268 | + if (trashed_files.size > 0) { |
269 | + Audience.Objects.Video restore = trashed_files.last (); |
270 | + File trash = File.new_for_uri ("trash:///"); |
271 | + try { |
272 | + var children = trash.enumerate_children (FileAttribute.TRASH_ORIG_PATH, 0); |
273 | FileInfo file_info; |
274 | while ((file_info = children.next_file ()) != null) { |
275 | - if (!poster_hash.contains (file_info.get_name ())) { |
276 | - children.get_child (file_info).delete_async.begin (); |
277 | + string orinal_path = file_info.get_attribute_as_string (FileAttribute.TRASH_ORIG_PATH); |
278 | + if (orinal_path == restore.video_file.get_path ()) { |
279 | + File restore_file = File.new_for_uri ("trash:///" + restore.video_file.get_basename ()); |
280 | + restore_file.move (restore.video_file, 0); |
281 | + trashed_files.remove (restore); |
282 | + return; |
283 | } |
284 | } |
285 | + } catch (Error e) { |
286 | + error (e.message); |
287 | } |
288 | - } catch (Error e) { |
289 | - warning (e.message); |
290 | } |
291 | } |
292 | } |
293 | |
294 | === modified file 'src/Widgets/LibraryItem.vala' |
295 | --- src/Widgets/LibraryItem.vala 2016-09-21 17:12:09 +0000 |
296 | +++ src/Widgets/LibraryItem.vala 2016-09-25 00:39:06 +0000 |
297 | @@ -22,14 +22,21 @@ |
298 | namespace Audience { |
299 | public class LibraryItem : Gtk.FlowBoxChild { |
300 | |
301 | + Gtk.EventBox event_box; |
302 | Gtk.Grid grid; |
303 | public Audience.Objects.Video video { get; construct set; } |
304 | |
305 | Gtk.Image poster; |
306 | - Gtk.Label title; |
307 | + |
308 | + Gtk.Label title_label; |
309 | + |
310 | Gtk.Spinner spinner; |
311 | Gtk.Grid spinner_container; |
312 | |
313 | + Gtk.Menu context_menu; |
314 | + Gtk.MenuItem new_cover; |
315 | + Gtk.MenuItem move_to_trash; |
316 | + |
317 | public LibraryItem (Audience.Objects.Video video) { |
318 | Object (video: video); |
319 | } |
320 | @@ -60,8 +67,8 @@ |
321 | }); |
322 | |
323 | video.title_changed.connect (() => { |
324 | - title.label = video.title; |
325 | - title.show (); |
326 | + title_label.label = video.title; |
327 | + title_label.show (); |
328 | }); |
329 | |
330 | spinner_container = new Gtk.Grid (); |
331 | @@ -69,7 +76,7 @@ |
332 | spinner_container.width_request = Audience.Services.POSTER_WIDTH; |
333 | spinner_container.margin_top = spinner_container.margin_left = spinner_container.margin_right = 12; |
334 | spinner_container.get_style_context ().add_class ("card"); |
335 | - |
336 | + |
337 | spinner = new Gtk.Spinner (); |
338 | spinner.expand = true; |
339 | spinner.active = true; |
340 | @@ -85,16 +92,69 @@ |
341 | grid.valign = Gtk.Align.START; |
342 | grid.row_spacing = 12; |
343 | |
344 | - title = new Gtk.Label (video.title); |
345 | - title.justify = Gtk.Justification.CENTER; |
346 | - title.set_line_wrap (true); |
347 | - title.max_width_chars = 0; |
348 | - |
349 | + title_label = new Gtk.Label (video.title); |
350 | + title_label.justify = Gtk.Justification.CENTER; |
351 | + title_label.set_line_wrap (true); |
352 | + title_label.max_width_chars = 0; |
353 | |
354 | grid.attach (spinner_container, 0, 0, 1, 1); |
355 | - grid.attach (title, 0, 1, 1 ,1); |
356 | - |
357 | - this.add (grid); |
358 | + grid.attach (title_label, 0, 1, 1 ,1); |
359 | + |
360 | + context_menu = new Gtk.Menu (); |
361 | + new_cover = new Gtk.MenuItem.with_label (_("Set Artwork")); |
362 | + new_cover.activate.connect ( set_new_cover ); |
363 | + move_to_trash = new Gtk.MenuItem.with_label (_("Move to Trash")); |
364 | + move_to_trash.activate.connect ( move_video_to_trash ); |
365 | + |
366 | + context_menu.append (new_cover); |
367 | + context_menu.append (new Gtk.SeparatorMenuItem ()); |
368 | + context_menu.append (move_to_trash); |
369 | + context_menu.show_all (); |
370 | + |
371 | + event_box = new Gtk.EventBox (); |
372 | + event_box.button_press_event.connect (show_context_menu); |
373 | + event_box.add (grid); |
374 | + |
375 | + this.add (event_box); |
376 | + } |
377 | + |
378 | + private bool show_context_menu (Gtk.Widget sender, Gdk.EventButton evt) { |
379 | + if (evt.type == Gdk.EventType.BUTTON_PRESS && evt.button == 3) { |
380 | + context_menu.popup (null, null, null, evt.button, evt.time); |
381 | + return true; |
382 | + } |
383 | + |
384 | + return false; |
385 | + } |
386 | + |
387 | + private void set_new_cover () { |
388 | + var file = new Gtk.FileChooserDialog (_("Open"), Audience.App.get_instance ().mainwindow, Gtk.FileChooserAction.OPEN, |
389 | + _("_Cancel"), Gtk.ResponseType.CANCEL, _("_Open"), Gtk.ResponseType.ACCEPT); |
390 | + |
391 | + var image_filter = new Gtk.FileFilter (); |
392 | + image_filter.set_filter_name (_("Image files")); |
393 | + image_filter.add_mime_type ("image/*"); |
394 | + |
395 | + file.add_filter (image_filter); |
396 | + |
397 | + if (file.run () == Gtk.ResponseType.ACCEPT) { |
398 | + Gdk.Pixbuf? pixbuf = video.get_poster_from_file (file.get_file ().get_path ()); |
399 | + if (pixbuf != null) { |
400 | + try { |
401 | + pixbuf.save (video.video_file.get_path() + ".jpg", "jpeg"); |
402 | + video.set_new_poster (pixbuf); |
403 | + } catch (Error e) { |
404 | + warning (e.message); |
405 | + } |
406 | + video.initialize_poster.begin (); |
407 | + } |
408 | + } |
409 | + |
410 | + file.destroy (); |
411 | + } |
412 | + |
413 | + private void move_video_to_trash () { |
414 | + video.trash (); |
415 | } |
416 | } |
417 | } |
418 | |
419 | === modified file 'src/Widgets/LibraryPage.vala' |
420 | --- src/Widgets/LibraryPage.vala 2016-09-23 19:54:34 +0000 |
421 | +++ src/Widgets/LibraryPage.vala 2016-09-25 00:39:06 +0000 |
422 | @@ -20,13 +20,13 @@ |
423 | */ |
424 | |
425 | namespace Audience { |
426 | - public class LibraryPage : Gtk.ScrolledWindow { |
427 | + public class LibraryPage : Gtk.Grid { |
428 | |
429 | public signal void filter_result_changed (bool has_results); |
430 | |
431 | public Gtk.FlowBox view_movies; |
432 | - Audience.Services.LibraryManager manager; |
433 | - |
434 | + public Audience.Services.LibraryManager manager; |
435 | + public Gtk.ScrolledWindow scrolled_window; |
436 | bool poster_initialized = false; |
437 | int items_counter; |
438 | string query; |
439 | @@ -45,6 +45,9 @@ |
440 | query = ""; |
441 | items_counter = 0; |
442 | |
443 | + scrolled_window = new Gtk.ScrolledWindow (null, null); |
444 | + scrolled_window.expand = true; |
445 | + |
446 | view_movies = new Gtk.FlowBox (); |
447 | view_movies.margin = 24; |
448 | view_movies.homogeneous = true; |
449 | @@ -54,9 +57,15 @@ |
450 | view_movies.selection_mode = Gtk.SelectionMode.NONE; |
451 | view_movies.child_activated.connect (play_video); |
452 | |
453 | + scrolled_window.add (view_movies); |
454 | + |
455 | manager = Audience.Services.LibraryManager.get_instance (); |
456 | manager.video_file_detected.connect (add_item); |
457 | manager.video_file_deleted.connect (remove_item_from_path); |
458 | + manager.video_moved_to_trash.connect ((video) => { |
459 | + Audience.App.get_instance ().mainwindow.set_app_notification (_("Video '%s' Removed.").printf (video.title)); |
460 | + }); |
461 | + |
462 | manager.begin_scan (); |
463 | |
464 | map.connect (() => { |
465 | @@ -70,7 +79,7 @@ |
466 | view_movies.set_sort_func (video_sort_func); |
467 | view_movies.set_filter_func (video_filter_func); |
468 | |
469 | - add (view_movies); |
470 | + add (scrolled_window); |
471 | } |
472 | |
473 | private void add_item (Audience.Objects.Video video) { |
474 | @@ -105,6 +114,10 @@ |
475 | remove_item.begin (child as LibraryItem); |
476 | } |
477 | } |
478 | + |
479 | + if (!has_child ()) { |
480 | + Audience.App.get_instance ().mainwindow.navigate_back (); |
481 | + } |
482 | } |
483 | |
484 | private async void poster_initialisation () { |
485 | @@ -143,9 +156,9 @@ |
486 | view_movies.invalidate_filter (); |
487 | filter_result_changed (has_child ()); |
488 | } |
489 | - |
490 | + |
491 | public bool has_child () { |
492 | - if (view_movies.get_child_at_index (0) != null) { |
493 | + if (view_movies.get_children ().length () > 0) { |
494 | foreach (unowned Gtk.Widget child in view_movies.get_children ()) { |
495 | if (child.get_child_visible ()) { |
496 | return true; |
497 | |
498 | === modified file 'src/Window.vala' |
499 | --- src/Window.vala 2016-09-23 20:27:50 +0000 |
500 | +++ src/Window.vala 2016-09-25 00:39:06 +0000 |
501 | @@ -31,6 +31,8 @@ |
502 | private NavigationButton navigation_button; |
503 | private ZeitgeistManager zeitgeist_manager; |
504 | private Gtk.SearchEntry search_entry; |
505 | + private Gtk.Revealer app_notification; |
506 | + private Gtk.Label notification_label; |
507 | |
508 | // For better translation |
509 | const string navigation_button_welcomescreen = N_("Back"); |
510 | @@ -98,6 +100,14 @@ |
511 | player_page.notify["playing"].connect (() => { |
512 | set_keep_above (player_page.playing && settings.stay_on_top); |
513 | }); |
514 | + |
515 | + player_page.map.connect (() => { |
516 | + app_notification.visible = false; |
517 | + }); |
518 | + |
519 | + player_page.unmap.connect (() => { |
520 | + app_notification.visible = true; |
521 | + }); |
522 | |
523 | alert_view = new Granite.Widgets.AlertView ("", "", ""); |
524 | alert_view.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); |
525 | @@ -112,7 +122,41 @@ |
526 | main_stack.add_named (alert_view, "alert"); |
527 | main_stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT; |
528 | |
529 | - add (main_stack); |
530 | + notification_label = new Gtk.Label (""); |
531 | + var restore_button = new Gtk.Button.with_label (_("Restore")); |
532 | + restore_button.clicked.connect (() => { |
533 | + library_page.manager.undo_delete_item (); |
534 | + app_notification.reveal_child = false; |
535 | + main_stack.set_visible_child (library_page); |
536 | + }); |
537 | + |
538 | + var close_button = new Gtk.Button.from_icon_name ("close-symbolic", Gtk.IconSize.MENU); |
539 | + close_button.get_style_context ().add_class ("close-button"); |
540 | + close_button.clicked.connect (() => { |
541 | + app_notification.reveal_child = false; |
542 | + }); |
543 | + |
544 | + var notification_box = new Gtk.Grid (); |
545 | + notification_box.column_spacing = 12; |
546 | + notification_box.add (close_button); |
547 | + notification_box.add (notification_label); |
548 | + notification_box.add (restore_button); |
549 | + |
550 | + var notification_frame = new Gtk.Frame (null); |
551 | + notification_frame.get_style_context ().add_class ("app-notification"); |
552 | + notification_frame.add (notification_box); |
553 | + |
554 | + app_notification = new Gtk.Revealer (); |
555 | + app_notification.margin = 3; |
556 | + app_notification.halign = Gtk.Align.CENTER; |
557 | + app_notification.valign = Gtk.Align.START; |
558 | + app_notification.add (notification_frame); |
559 | + |
560 | + var overlay = new Gtk.Overlay (); |
561 | + overlay.add_overlay (main_stack); |
562 | + overlay.add_overlay (app_notification); |
563 | + |
564 | + add (overlay); |
565 | show_all (); |
566 | |
567 | navigation_button.hide (); |
568 | @@ -145,7 +189,8 @@ |
569 | if (event.button == Gdk.BUTTON_SECONDARY) { |
570 | player_page.playing = !player_page.playing; |
571 | } |
572 | - return false; |
573 | + |
574 | + return base.button_press_event(event); |
575 | }); |
576 | |
577 | window_state_event.connect ((e) => { |
578 | @@ -169,7 +214,7 @@ |
579 | |
580 | /** Returns true if the code parameter matches the keycode of the keyval parameter for |
581 | * any keyboard group or level (in order to allow for non-QWERTY keyboards) **/ |
582 | - protected bool match_keycode (int keyval, uint code) { |
583 | + public bool match_keycode (int keyval, uint code) { |
584 | Gdk.KeymapKey [] keys; |
585 | Gdk.Keymap keymap = Gdk.Keymap.get_default (); |
586 | if (keymap.get_entries_for_keyval (keyval, out keys)) { |
587 | @@ -275,13 +320,15 @@ |
588 | show_library (); |
589 | return true; |
590 | } |
591 | - } else if (main_stack.get_visible_child () == library_page && !search_entry.is_focus && e.str.strip ().length > 0) { |
592 | - search_entry.grab_focus (); |
593 | } else if (search_entry.visible) { |
594 | if (ctrl_pressed && match_keycode (Gdk.Key.f, keycode)) { |
595 | search_entry.grab_focus (); |
596 | + } else if (ctrl_pressed && match_keycode (Gdk.Key.z, keycode)) { |
597 | + library_page.manager.undo_delete_item (); |
598 | } else if (match_keycode (Gdk.Key.Escape, keycode)) { |
599 | search_entry.text = ""; |
600 | + } else if (!search_entry.is_focus && e.str.strip ().length > 0) { |
601 | + search_entry.grab_focus (); |
602 | } |
603 | } |
604 | |
605 | @@ -331,7 +378,7 @@ |
606 | navigation_button.label = navigation_button_welcomescreen; |
607 | navigation_button.show (); |
608 | main_stack.set_visible_child (library_page); |
609 | - library_page.grab_focus (); |
610 | + library_page.scrolled_window.grab_focus (); |
611 | } |
612 | |
613 | public void run_open_file (bool clear_playlist = false, bool force_play = true) { |
614 | @@ -457,4 +504,9 @@ |
615 | alert_view.icon_name = icon_name; |
616 | main_stack.set_visible_child_full ("alert", Gtk.StackTransitionType.NONE); |
617 | } |
618 | + |
619 | + public void set_app_notification (string text) { |
620 | + notification_label.label = text; |
621 | + app_notification.reveal_child = true; |
622 | + } |
623 | } |
Menu items are Title Case ;)
"Rename video title" is pretty excessive. In Files we just use "Rename"