diff --git a/src/Gestures/GestureTracker.vala b/src/Gestures/GestureTracker.vala index cf81ab272..2a92b67d4 100644 --- a/src/Gestures/GestureTracker.vala +++ b/src/Gestures/GestureTracker.vala @@ -204,18 +204,16 @@ public class Gala.GestureTracker : Object { } /** - * Connects a callback that will only be called if != 0 completions were made. + * Connects a callback that will be called as soon as the gesture finishes. * If with_gesture is false it will be called immediately, otherwise once {@link on_end} is emitted. */ - public void add_success_callback (bool with_gesture, owned OnEnd callback) { + public void add_end_callback (bool with_gesture, owned OnEnd callback) { if (!with_gesture) { callback (1, 1, min_animation_duration); } else { - ulong handler_id = on_end.connect ((percentage, completions, duration) => { - if (completions != 0) { - callback (percentage, completions, duration); - } - }); + ulong handler_id = on_end.connect ((percentage, cancel_action, duration) => + callback (percentage, cancel_action, duration) + ); handlers.add (handler_id); } } diff --git a/src/InternalUtils.vala b/src/InternalUtils.vala index 14cff7dec..1f5ccd599 100644 --- a/src/InternalUtils.vala +++ b/src/InternalUtils.vala @@ -388,15 +388,5 @@ namespace Gala { Clutter.get_default_backend ().get_default_seat ().bell_notify (); #endif } - - public static void update_transients_visible (Meta.Window window, bool visible) { - window.foreach_transient ((transient) => { - unowned var actor = (Meta.WindowActor) transient.get_compositor_private (); - - actor.visible = visible; - - return true; - }); - } } } diff --git a/src/ShellClients/HideTracker.vala b/src/ShellClients/HideTracker.vala index d0eda2e39..8d79ae2db 100644 --- a/src/ShellClients/HideTracker.vala +++ b/src/ShellClients/HideTracker.vala @@ -227,6 +227,10 @@ public class Gala.HideTracker : Object { } private void trigger_hide () { + if (hide_timeout_id != 0) { + return; + } + // Don't hide if we have transients, e.g. an open popover, dialog, etc. var has_transients = false; panel.window.foreach_transient (() => { diff --git a/src/ShellClients/PanelClone.vala b/src/ShellClients/PanelClone.vala deleted file mode 100644 index 056e73002..000000000 --- a/src/ShellClients/PanelClone.vala +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2024 elementary, Inc. (https://elementary.io) - * SPDX-License-Identifier: GPL-3.0-or-later - * - * Authored by: Leonhard Kargl - */ - -public class Gala.PanelClone : Object { - private const int ANIMATION_DURATION = 250; - - public WindowManager wm { get; construct; } - public unowned PanelWindow panel { get; construct; } - - public Pantheon.Desktop.HideMode hide_mode { - get { - return hide_tracker == null ? Pantheon.Desktop.HideMode.NEVER : hide_tracker.hide_mode; - } - set { - if (value == NEVER) { - hide_tracker = null; - show (); - return; - } else if (hide_tracker == null) { - hide_tracker = new HideTracker (wm.get_display (), panel); - hide_tracker.hide.connect (hide); - hide_tracker.show.connect (show); - } - - hide_tracker.hide_mode = value; - } - } - - public bool panel_hidden { get; private set; default = true; } - - private Meta.WindowActor actor; - - private GestureTracker default_gesture_tracker; - - private HideTracker? hide_tracker; - - public PanelClone (WindowManager wm, PanelWindow panel) { - Object (wm: wm, panel: panel); - } - - construct { - default_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION); - - actor = (Meta.WindowActor) panel.window.get_compositor_private (); - - notify["panel-hidden"].connect (() => { - // When hidden changes schedule an update to make sure it's actually - // correct since things might have changed during the animation - if (hide_tracker != null) { - hide_tracker.schedule_update (); - } - }); - - wm.get_display ().in_fullscreen_changed.connect (check_hide); - - Idle.add_once (() => { - if (hide_mode == NEVER) { - show (); - } else { - hide_tracker.schedule_update (); - } - }); - } - - private float calculate_translation_y (bool hidden) { - switch (panel.anchor) { - case TOP: - return hidden ? -actor.height : 0; - case BOTTOM: - return hidden ? actor.height : 0; - default: - return 0; - } - } - - private void hide () { - if (panel_hidden || default_gesture_tracker.recognizing) { - return; - } - - if (!Meta.Util.is_wayland_compositor ()) { - Utils.x11_set_window_pass_through (panel.window); - } - - if (panel.anchor != TOP && panel.anchor != BOTTOM) { - warning ("Animated hide not supported for side yet."); - return; - } - - InternalUtils.update_transients_visible (panel.window, false); - - new GesturePropertyTransition ( - actor, default_gesture_tracker, "translation-y", null, calculate_translation_y (true) - ).start (false, () => InternalUtils.update_transients_visible (panel.window, !panel_hidden)); - - default_gesture_tracker.add_success_callback (false, () => panel_hidden = true); - } - - private void show () { - if (!panel_hidden || default_gesture_tracker.recognizing || wm.get_display ().get_monitor_in_fullscreen (panel.window.get_monitor ())) { - return; - } - - if (!Meta.Util.is_wayland_compositor ()) { - Utils.x11_unset_window_pass_through (panel.window); - } - - new GesturePropertyTransition ( - actor, default_gesture_tracker, "translation-y", null, calculate_translation_y (false) - ).start (false, () => InternalUtils.update_transients_visible (panel.window, !panel_hidden)); - - default_gesture_tracker.add_success_callback (false, () => panel_hidden = false); - } - - private void check_hide () { - if (wm.get_display ().get_monitor_in_fullscreen (panel.window.get_monitor ())) { - hide (); - } else if (hide_mode == NEVER) { - show (); - } else { - hide_tracker.update_overlap (); - } - } -} diff --git a/src/ShellClients/PanelWindow.vala b/src/ShellClients/PanelWindow.vala index e7e13feac..f9a1b0587 100644 --- a/src/ShellClients/PanelWindow.vala +++ b/src/ShellClients/PanelWindow.vala @@ -5,22 +5,44 @@ * Authored by: Leonhard Kargl */ -public class Gala.PanelWindow : Object { +public class Gala.PanelWindow : ShellWindow { + private const int ANIMATION_DURATION = 250; + private static HashTable window_struts = new HashTable (null, null); public WindowManager wm { get; construct; } - public Meta.Window window { get; construct; } public Pantheon.Desktop.Anchor anchor { get; construct set; } - private WindowPositioner window_positioner; + public Pantheon.Desktop.HideMode hide_mode { + get { + return hide_tracker == null ? Pantheon.Desktop.HideMode.NEVER : hide_tracker.hide_mode; + } + set { + if (value == NEVER) { + hide_tracker = null; + show (); + make_exclusive (); + return; + } else if (hide_tracker == null) { + unmake_exclusive (); + + hide_tracker = new HideTracker (wm.get_display (), this); + hide_tracker.hide.connect (hide); + hide_tracker.show.connect (show); + } - private PanelClone clone; + hide_tracker.hide_mode = value; + } + } + + private GestureTracker default_gesture_tracker; + private HideTracker? hide_tracker; private int width = -1; private int height = -1; public PanelWindow (WindowManager wm, Meta.Window window, Pantheon.Desktop.Anchor anchor) { - Object (wm: wm, window: window, anchor: anchor); + Object (wm: wm, anchor: anchor, window: window, position: Position.from_anchor (anchor)); } construct { @@ -30,22 +52,26 @@ public class Gala.PanelWindow : Object { } }); - window.stick (); - - clone = new PanelClone (wm, this); - - unowned var display = wm.get_display (); - - window_positioner = new WindowPositioner (display, window, WindowPositioner.Position.from_anchor (anchor)); + notify["anchor"].connect (() => position = Position.from_anchor (anchor)); - notify["anchor"].connect (() => window_positioner.position = WindowPositioner.Position.from_anchor (anchor)); - - unowned var workspace_manager = display.get_workspace_manager (); + unowned var workspace_manager = window.display.get_workspace_manager (); workspace_manager.workspace_added.connect (update_strut); workspace_manager.workspace_removed.connect (update_strut); window.size_changed.connect (update_strut); window.position_changed.connect (update_strut); + + default_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION); + + window.display.in_fullscreen_changed.connect (() => { + if (wm.get_display ().get_monitor_in_fullscreen (window.get_monitor ())) { + hide (); + } else if (hide_mode == NEVER) { + show (); + } else { + hide_tracker.update_overlap (); + } + }); } #if HAS_MUTTER45 @@ -78,14 +104,16 @@ public class Gala.PanelWindow : Object { update_strut (); } - public void set_hide_mode (Pantheon.Desktop.HideMode hide_mode) { - clone.hide_mode = hide_mode; + private void hide () { + add_state (CUSTOM_HIDDEN, default_gesture_tracker, false); + } - if (hide_mode == NEVER) { - make_exclusive (); - } else { - unmake_exclusive (); + private void show () { + if (window.display.get_monitor_in_fullscreen (window.get_monitor ())) { + return; } + + remove_state (CUSTOM_HIDDEN, default_gesture_tracker, false); } private void make_exclusive () { @@ -93,7 +121,7 @@ public class Gala.PanelWindow : Object { } private void update_strut () { - if (clone.hide_mode != NEVER) { + if (hide_mode != NEVER) { return; } diff --git a/src/ShellClients/WindowPositioner.vala b/src/ShellClients/PositionedWindow.vala similarity index 85% rename from src/ShellClients/WindowPositioner.vala rename to src/ShellClients/PositionedWindow.vala index daf838f14..53cf9b5fa 100644 --- a/src/ShellClients/WindowPositioner.vala +++ b/src/ShellClients/PositionedWindow.vala @@ -5,7 +5,7 @@ * Authored by: Leonhard Kargl */ -public class Gala.WindowPositioner : Object { +public class Gala.PositionedWindow : Object { public enum Position { TOP, BOTTOM, @@ -21,7 +21,6 @@ public class Gala.WindowPositioner : Object { } } - public Meta.Display display { get; construct; } public Meta.Window window { get; construct; } /** * This may only be set after the window was shown. @@ -30,8 +29,8 @@ public class Gala.WindowPositioner : Object { public Position position { get; construct set; } public Variant? position_data { get; construct set; } - public WindowPositioner (Meta.Display display, Meta.Window window, Position position, Variant? position_data = null) { - Object (display: display, window: window, position: position, position_data: position_data); + public PositionedWindow (Meta.Window window, Position position, Variant? position_data = null) { + Object (window: window, position: position, position_data: position_data); } construct { @@ -41,7 +40,7 @@ public class Gala.WindowPositioner : Object { window.position_changed.connect (position_window); window.shown.connect (position_window); - unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager (); + unowned var monitor_manager = window.display.get_context ().get_backend ().get_monitor_manager (); monitor_manager.monitors_changed.connect (position_window); monitor_manager.monitors_changed_internal.connect (position_window); @@ -51,8 +50,8 @@ public class Gala.WindowPositioner : Object { private void position_window () { int x = 0, y = 0; - var window_rect = window.get_frame_rect (); + unowned var display = window.display; switch (position) { case CENTER: diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index 31e5b5c71..bbf49e656 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -16,7 +16,7 @@ public class Gala.ShellClientsManager : Object { instance = new ShellClientsManager (wm); } - public static ShellClientsManager? get_instance () { + public static unowned ShellClientsManager? get_instance () { return instance; } @@ -26,7 +26,7 @@ public class Gala.ShellClientsManager : Object { private ManagedClient[] protocol_clients = {}; private GLib.HashTable panel_windows = new GLib.HashTable (null, null); - private GLib.HashTable positioned_windows = new GLib.HashTable (null, null); + private GLib.HashTable positioned_windows = new GLib.HashTable (null, null); private ShellClientsManager (WindowManager wm) { Object (wm: wm); @@ -180,17 +180,37 @@ public class Gala.ShellClientsManager : Object { return; } - panel_windows[window].set_hide_mode (hide_mode); + panel_windows[window].hide_mode = hide_mode; } public void make_centered (Meta.Window window) requires (!is_itself_positioned (window)) { - positioned_windows[window] = new WindowPositioner (wm.get_display (), window, CENTER); + positioned_windows[window] = new ShellWindow (window, CENTER); // connect_after so we make sure that any queued move is unqueued window.unmanaging.connect_after ((_window) => positioned_windows.remove (_window)); } - private bool is_itself_positioned (Meta.Window window) { + public void add_state (ShellWindow.State state, GestureTracker gesture_tracker, bool with_gesture) { + foreach (var window in positioned_windows.get_values ()) { + window.add_state (state, gesture_tracker, with_gesture); + } + + foreach (var window in panel_windows.get_values ()) { + window.add_state (state, gesture_tracker, with_gesture); + } + } + + public void remove_state (ShellWindow.State state, GestureTracker gesture_tracker, bool with_gesture) { + foreach (var window in positioned_windows.get_values ()) { + window.remove_state (state, gesture_tracker, with_gesture); + } + + foreach (var window in panel_windows.get_values ()) { + window.remove_state (state, gesture_tracker, with_gesture); + } + } + + public bool is_itself_positioned (Meta.Window window) { return (window in positioned_windows) || (window in panel_windows) || NotificationStack.is_notification (window); } diff --git a/src/ShellClients/ShellWindow.vala b/src/ShellClients/ShellWindow.vala new file mode 100644 index 000000000..8327a9e7c --- /dev/null +++ b/src/ShellClients/ShellWindow.vala @@ -0,0 +1,117 @@ +/* + * Copyright 2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class Gala.ShellWindow : PositionedWindow { + [Flags] + public enum State { + CUSTOM_HIDDEN, + MULTITASKING_VIEW, + DESKTOP + } + + private const State HIDING_STATES = CUSTOM_HIDDEN | MULTITASKING_VIEW; + + private Meta.WindowActor actor; + private State pending_state = DESKTOP; + private State current_state = DESKTOP; + + private bool gesture_ongoing = false; + + public ShellWindow (Meta.Window window, Position position, Variant? position_data = null) { + base (window, position, position_data); + } + + construct { + actor = (Meta.WindowActor) window.get_compositor_private (); + } + + public void add_state (State state, GestureTracker gesture_tracker, bool with_gesture) { + pending_state |= state; + animate (pending_state, gesture_tracker, with_gesture); + } + + public void remove_state (State state, GestureTracker gesture_tracker, bool with_gesture) { + pending_state &= ~state; + animate (pending_state, gesture_tracker, with_gesture); + } + + private void animate (State new_state, GestureTracker gesture_tracker, bool with_gesture) { + if (new_state == current_state || gesture_ongoing) { + return; + } + + gesture_ongoing = true; + + update_visibility (true); + + new GesturePropertyTransition ( + actor, gesture_tracker, get_animation_property (), null, calculate_value ((new_state & HIDING_STATES) != 0) + ).start (with_gesture, () => update_visibility (false)); + + gesture_tracker.add_end_callback (with_gesture, (percentage, completions) => { + gesture_ongoing = false; + + if (completions != 0) { + current_state = new_state; + } + + if (!Meta.Util.is_wayland_compositor ()) { + if ((current_state & HIDING_STATES) != 0) { + Utils.x11_set_window_pass_through (window); + } else { + Utils.x11_unset_window_pass_through (window); + } + } + + if (pending_state != new_state) { // We have received new state while animating + animate (pending_state, gesture_tracker, false); + } else { + pending_state = current_state; + } + }); + } + + private void update_visibility (bool animating) { + var visible = (current_state & HIDING_STATES) == 0; + + actor.visible = animating || visible; + + unowned var manager = ShellClientsManager.get_instance (); + window.foreach_transient ((transient) => { + if (manager.is_itself_positioned (transient)) { + return true; + } + + unowned var actor = (Meta.WindowActor) transient.get_compositor_private (); + + actor.visible = visible && !animating; + + return true; + }); + } + + private string get_animation_property () { + switch (position) { + case TOP: + case BOTTOM: + return "translation-y"; + default: + return "opacity"; + } + } + + private Value calculate_value (bool hidden) { + switch (position) { + case TOP: + return hidden ? -actor.height : 0f; + case BOTTOM: + return hidden ? actor.height : 0f; + default: + return hidden ? 0u : 255u; + } + } +} diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index 923172552..e38d2e4d7 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -39,7 +39,6 @@ namespace Gala { private IconGroupContainer icon_groups; private Clutter.Actor workspaces; - private Clutter.Actor dock_clones; private Clutter.Actor primary_monitor_container; private Clutter.BrightnessContrastEffect brightness_effect; @@ -78,8 +77,6 @@ namespace Gala { icon_groups = new IconGroupContainer (display.get_monitor_scale (display.get_primary_monitor ())); - dock_clones = new Clutter.Actor (); - brightness_effect = new Clutter.BrightnessContrastEffect (); update_brightness_effect (); @@ -96,7 +93,6 @@ namespace Gala { primary_monitor_container.add_child (icon_groups); primary_monitor_container.add_child (workspaces); add_child (primary_monitor_container); - add_child (dock_clones); unowned var manager = display.get_workspace_manager (); manager.workspace_added.connect (add_workspace); @@ -629,7 +625,6 @@ namespace Gala { wm.background_group.hide (); wm.window_group.hide (); wm.top_window_group.hide (); - wm.shell_group.hide (); show (); grab_key_focus (); @@ -674,9 +669,9 @@ namespace Gala { } if (opening) { - show_docks (with_gesture, is_cancel_animation); + ShellClientsManager.get_instance ().add_state (MULTITASKING_VIEW, multitasking_gesture_tracker, with_gesture); } else { - hide_docks (with_gesture, is_cancel_animation); + ShellClientsManager.get_instance ().remove_state (MULTITASKING_VIEW, multitasking_gesture_tracker, with_gesture); } GestureTracker.OnEnd on_animation_end = (percentage, completions) => { @@ -692,9 +687,6 @@ namespace Gala { wm.background_group.show (); wm.window_group.show (); wm.top_window_group.show (); - wm.shell_group.show (); - - dock_clones.destroy_all_children (); wm.pop_modal (modal_proxy); } @@ -716,107 +708,6 @@ namespace Gala { } } - private void show_docks (bool with_gesture, bool is_cancel_animation) { - unowned GLib.List window_actors = display.get_window_actors (); - foreach (unowned Meta.WindowActor actor in window_actors) { - const int MAX_OFFSET = 200; - - if (actor.is_destroyed () || !actor.visible || actor.translation_y != 0) { - continue; - } - - unowned Meta.Window window = actor.get_meta_window (); - var monitor = window.get_monitor (); - - if (window.window_type != Meta.WindowType.DOCK) { - continue; - } - - if (NotificationStack.is_notification (window)) { - continue; - } - - if (display.get_monitor_in_fullscreen (monitor)) { - continue; - } - - var monitor_geom = display.get_monitor_geometry (monitor); - - var window_geom = window.get_frame_rect (); - var top = monitor_geom.y + MAX_OFFSET > window_geom.y; - var bottom = monitor_geom.y + monitor_geom.height - MAX_OFFSET < window_geom.y; - - if (!top && !bottom) { - continue; - } - - var initial_x = actor.x; - var initial_y = actor.y; - var target_y = (top) - ? actor.y - actor.height - : actor.y + actor.height; - - var clone = new SafeWindowClone (window, true); - dock_clones.add_child (clone); - - clone.set_position (initial_x, initial_y); - - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var y = GestureTracker.animation_value (initial_y, target_y, percentage); - clone.y = y; - }; - - GestureTracker.OnEnd on_animation_end = (percentage, completions) => { - if (completions == 0) { - return; - } - - clone.save_easing_state (); - clone.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - clone.set_easing_duration ((!is_cancel_animation && AnimationsSettings.get_enable_animations ()) ? ANIMATION_DURATION : 0); - clone.y = target_y; - clone.restore_easing_state (); - }; - - if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, 1, 0); - } else { - multitasking_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); - } - } - } - - private void hide_docks (bool with_gesture, bool is_cancel_animation) { - foreach (unowned var child in dock_clones.get_children ()) { - var dock = (Clutter.Clone) child; - var initial_y = dock.y; - var target_y = dock.source.y; - - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var y = GestureTracker.animation_value (initial_y, target_y, percentage); - dock.y = y; - }; - - GestureTracker.OnEnd on_animation_end = (percentage, completions) => { - if (completions == 0) { - return; - } - - dock.save_easing_state (); - dock.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - dock.set_easing_duration (AnimationsSettings.get_animation_duration (ANIMATION_DURATION)); - dock.y = target_y; - dock.restore_easing_state (); - }; - - if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, 1, 0); - } else { - multitasking_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); - } - } - } - private bool keybinding_filter (Meta.KeyBinding binding) { var action = Meta.Prefs.get_keybinding_action (binding.get_name ()); diff --git a/src/Widgets/SafeWindowClone.vala b/src/Widgets/SafeWindowClone.vala deleted file mode 100644 index 1f1f2b072..000000000 --- a/src/Widgets/SafeWindowClone.vala +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-3.0-or-later - * SPDX-FileCopyrightText: 2014 Tom Beckmann - * 2025 elementary, Inc. (https://elementary.io) - */ - -/** - * A clone for a MetaWindowActor that will guard against the - * meta_window_appears_focused crash by disabling painting the clone - * as soon as it gets unavailable. - */ -public class Gala.SafeWindowClone : Clutter.Clone { - public Meta.Window window { get; construct; } - - /** - * If set to true, the SafeWindowClone will destroy itself when the connected - * window is unmanaged - */ - public bool destroy_on_unmanaged { get; construct set; default = false; } - - /** - * Creates a new SafeWindowClone - * - * @param window The window to clone from - * @param destroy_on_unmanaged see destroy_on_unmanaged property - */ - public SafeWindowClone (Meta.Window window, bool destroy_on_unmanaged = false) { - var actor = (Meta.WindowActor) window.get_compositor_private (); - - Object (window: window, - source: actor, - destroy_on_unmanaged: destroy_on_unmanaged); - } - - construct { - if (source != null) - window.unmanaged.connect (reset_source); - } - - ~SafeWindowClone () { - window.unmanaged.disconnect (reset_source); - } - - private void reset_source () { - // actually destroying the clone will be handled somewhere else (unless we were - // requested to destroy it), we just need to make sure the clone doesn't attempt - // to draw a clone of a window that has been destroyed - source = null; - - if (destroy_on_unmanaged) - destroy (); - } -} diff --git a/src/meson.build b/src/meson.build index 974b561f5..d50a6c9f5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -46,10 +46,10 @@ gala_bin_sources = files( 'ShellClients/HideTracker.vala', 'ShellClients/ManagedClient.vala', 'ShellClients/NotificationsClient.vala', - 'ShellClients/PanelClone.vala', 'ShellClients/PanelWindow.vala', + 'ShellClients/PositionedWindow.vala', 'ShellClients/ShellClientsManager.vala', - 'ShellClients/WindowPositioner.vala', + 'ShellClients/ShellWindow.vala', 'Widgets/DwellClickTimer.vala', 'Widgets/IconGroup.vala', 'Widgets/IconGroupContainer.vala', @@ -57,7 +57,6 @@ gala_bin_sources = files( 'Widgets/MultitaskingView.vala', 'Widgets/PixelPicker.vala', 'Widgets/PointerLocator.vala', - 'Widgets/SafeWindowClone.vala', 'Widgets/ScreenShield.vala', 'Widgets/SelectionArea.vala', 'Widgets/Tooltip.vala',