diff --git a/src/Views/Wallpaper.vala b/src/Views/Wallpaper.vala index 43b79678..50071f6e 100644 --- a/src/Views/Wallpaper.vala +++ b/src/Views/Wallpaper.vala @@ -406,7 +406,9 @@ public class PantheonShell.Wallpaper : Switchboard.SettingsPage { return; } - var wallpaper = new WallpaperContainer (uri); + var wallpaper = new WallpaperContainer () { + uri = uri + }; wallpaper_model.insert_sorted (wallpaper, wallpapers_sort_function); wallpaper.trash.connect (() => { diff --git a/src/Widgets/WallpaperContainer.vala b/src/Widgets/WallpaperContainer.vala index 593295c3..0a710fb2 100644 --- a/src/Widgets/WallpaperContainer.vala +++ b/src/Widgets/WallpaperContainer.vala @@ -21,15 +21,24 @@ public class PantheonShell.WallpaperContainer : Granite.Bin { public signal void trash (); + // https://www.w3.org/WAI/WCAG21/Understanding/target-size.html + private const int TOUCH_TARGET_WIDTH = 44; + protected const int THUMB_WIDTH = 256; protected const int THUMB_HEIGHT = 144; protected Gtk.Picture image; + private GLib.Menu menu_model; + private GLib.SimpleAction remove_wallpaper_action; private Gtk.Revealer check_revealer; + private Gtk.EventControllerKey menu_key_controller; + private Gtk.GestureClick? click_controller; + private Gtk.GestureLongPress? long_press_controller; + private Gtk.PopoverMenu? context_menu; private string? thumb_path = null; - public string? uri { get; construct; default = null; } + public string? uri { get; set; default = null; } public uint64 creation_date = 0; public bool checked { @@ -48,10 +57,6 @@ public class PantheonShell.WallpaperContainer : Granite.Bin { } } - public WallpaperContainer (string uri) { - Object (uri: uri); - } - construct { image = new Gtk.Picture () { content_fit = COVER, @@ -80,58 +85,136 @@ public class PantheonShell.WallpaperContainer : Granite.Bin { halign = CENTER; valign = CENTER; + // So we can receive key events + focusable = true; child = overlay; - if (uri != null) { - var remove_wallpaper_action = new SimpleAction ("trash", null); - remove_wallpaper_action.activate.connect (() => trash ()); + remove_wallpaper_action = new SimpleAction ("trash", null); + remove_wallpaper_action.activate.connect (() => trash ()); - var action_group = new SimpleActionGroup (); - action_group.add_action (remove_wallpaper_action); + var action_group = new SimpleActionGroup (); + action_group.add_action (remove_wallpaper_action); - insert_action_group ("wallpaper", action_group); + insert_action_group ("wallpaper", action_group); - var file = File.new_for_uri (uri); - try { - var info = file.query_info ("*", FileQueryInfoFlags.NONE); + menu_model = new Menu (); + menu_model.append (_("Remove"), "wallpaper.trash"); - thumb_path = info.get_attribute_as_string (FileAttribute.THUMBNAIL_PATH); + notify["uri"].connect (construct_from_uri); + } - if (thumb_path != null && info.get_attribute_boolean (FileAttribute.THUMBNAIL_IS_VALID)) { - update_thumb.begin (); - } else { - generate_and_load_thumb (); - } + private void construct_from_uri () { + if (uri == null) { + remove_controller (click_controller); + remove_controller (long_press_controller); + remove_controller (menu_key_controller); - creation_date = info.get_attribute_uint64 (GLib.FileAttribute.TIME_CREATED); - remove_wallpaper_action.set_enabled (info.get_attribute_boolean (GLib.FileAttribute.ACCESS_CAN_DELETE)); - } catch (Error e) { - critical (e.message); + click_controller = null; + long_press_controller = null; + menu_key_controller = null; + + context_menu.unparent (); + context_menu = null; + + return; + } + + var file = File.new_for_uri (uri); + try { + var info = file.query_info ("*", FileQueryInfoFlags.NONE); + + thumb_path = info.get_attribute_as_string (FileAttribute.THUMBNAIL_PATH); + + if (thumb_path != null && info.get_attribute_boolean (FileAttribute.THUMBNAIL_IS_VALID)) { + update_thumb.begin (); + } else { + generate_and_load_thumb (); } - var menu_model = new Menu (); - menu_model.append (_("Remove"), "wallpaper.trash"); - - var context_menu = new Gtk.PopoverMenu.from_model (menu_model) { - halign = START, - has_arrow = false - }; - context_menu.set_parent (this); - - var secondary_click_gesture = new Gtk.GestureClick () { - button = Gdk.BUTTON_SECONDARY - }; - secondary_click_gesture.released.connect ((n_press, x, y) => { - secondary_click_gesture.set_state (CLAIMED); - context_menu.pointing_to = Gdk.Rectangle () { - x = (int) x, - y = (int) y - }; - context_menu.popup (); - }); - - add_controller (secondary_click_gesture); + creation_date = info.get_attribute_uint64 (GLib.FileAttribute.TIME_CREATED); + remove_wallpaper_action.set_enabled (info.get_attribute_boolean (GLib.FileAttribute.ACCESS_CAN_DELETE)); + } catch (Error e) { + critical (e.message); } + + context_menu = new Gtk.PopoverMenu.from_model (menu_model) { + halign = START, + has_arrow = false + }; + context_menu.set_parent (this); + + click_controller = new Gtk.GestureClick () { + button = 0, + exclusive = true + }; + click_controller.pressed.connect ((n_press, x, y) => { + var sequence = click_controller.get_current_sequence (); + var event = click_controller.get_last_event (sequence); + + if (event.triggers_context_menu ()) { + context_menu.halign = START; + menu_popup_at_pointer (context_menu, x, y); + + click_controller.set_state (CLAIMED); + click_controller.reset (); + } + }); + + long_press_controller = new Gtk.GestureLongPress () { + touch_only = true + }; + long_press_controller.pressed.connect ((x, y) => { + // Try to keep menu from under your hand + if (x > get_root ().get_width () / 2) { + context_menu.halign = END; + x -= TOUCH_TARGET_WIDTH; + } else { + context_menu.halign = START; + x += TOUCH_TARGET_WIDTH; + } + + menu_popup_at_pointer (context_menu, x, y - (TOUCH_TARGET_WIDTH * 0.75)); + }); + + menu_key_controller = new Gtk.EventControllerKey (); + menu_key_controller.key_released.connect ((keyval, keycode, state) => { + var mods = state & Gtk.accelerator_get_default_mod_mask (); + switch (keyval) { + case Gdk.Key.F10: + if (mods == Gdk.ModifierType.SHIFT_MASK) { + menu_popup_on_keypress (context_menu); + } + break; + case Gdk.Key.Menu: + case Gdk.Key.MenuKB: + menu_popup_on_keypress (context_menu); + break; + default: + return; + } + }); + + add_controller (click_controller); + add_controller (long_press_controller); + add_controller (menu_key_controller); + } + + private void menu_popup_on_keypress (Gtk.PopoverMenu popover) { + popover.halign = END; + popover.set_pointing_to (Gdk.Rectangle () { + x = (int) get_width (), + y = (int) get_height () / 2 + }); + popover.popup (); + } + + private void menu_popup_at_pointer (Gtk.PopoverMenu popover, double x, double y) { + var rect = Gdk.Rectangle () { + x = (int) x, + y = (int) y + }; + popover.pointing_to = rect; + popover.popup (); } private void generate_and_load_thumb () {