Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement zoom #739

Merged
merged 1 commit into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions compositor/WindowManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ namespace GreeterCompositor {
//ActivatableComponent? workspace_view = null;
//ActivatableComponent? window_overview = null;

private Zoom zoom;

//ScreenSaver? screensaver;

//Gee.LinkedList<ModalProxy> modal_stack = new Gee.LinkedList<ModalProxy> ();
Expand Down Expand Up @@ -135,6 +137,7 @@ namespace GreeterCompositor {

ui_group = new Clutter.Actor ();
ui_group.reactive = true;
update_ui_group_size ();
stage.add_child (ui_group);

int width, height;
Expand All @@ -161,6 +164,9 @@ namespace GreeterCompositor {
pointer_locator = new PointerLocator (this);
ui_group.add_child (pointer_locator);

unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
monitor_manager.monitors_changed.connect (update_ui_group_size);

/*keybindings*/

KeyBinding.set_custom_handler ("switch-to-workspace-first", () => {});
Expand Down Expand Up @@ -188,6 +194,8 @@ namespace GreeterCompositor {

KeyBinding.set_custom_handler ("show-desktop", () => {});

zoom = new Zoom (this);

/* orca (screenreader) doesn't listen to it's
org.gnome.desktop.a11y.applications screen-reader-enabled key
so we handle it ourselves
Expand All @@ -208,6 +216,25 @@ namespace GreeterCompositor {
});
}

private void update_ui_group_size () {
unowned var display = get_display ();

int max_width = 0;
int max_height = 0;

var num_monitors = display.get_n_monitors ();
for (int i = 0; i < num_monitors; i++) {
var geom = display.get_monitor_geometry (i);
var total_width = geom.x + geom.width;
var total_height = geom.y + geom.height;

max_width = (max_width > total_width) ? max_width : total_width;
max_height = (max_height > total_height) ? max_height : total_height;
}

ui_group.set_size (max_width, max_height);
}

private async void start_command (string[] command) {
if (Meta.Util.is_wayland_compositor ()) {
yield start_wayland (command);
Expand Down
141 changes: 141 additions & 0 deletions compositor/Zoom.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2022 elementary, Inc. (https://elementary.io)
* Copyright 2013 Tom Beckmann
* Copyright 2013 Rico Tzschichholz
* SPDX-License-Identifier: GPL-3.0-or-later
*/

public class GreeterCompositor.Zoom : Object {
private const float MIN_ZOOM = 1.0f;
private const float MAX_ZOOM = 10.0f;
private const float SHORTCUT_DELTA = 0.5f;
private const int ANIMATION_DURATION = 300;
private const uint MOUSE_POLL_TIME = 50;

public WindowManager wm { get; construct; }

private uint mouse_poll_timer = 0;
private float current_zoom = MIN_ZOOM;
private ulong wins_handler_id = 0UL;

public Zoom (WindowManager wm) {
Object (wm: wm);

unowned var display = wm.get_display ();
var schema = new GLib.Settings ("io.elementary.greeter-compositor.keybindings");

display.add_keybinding ("zoom-in", schema, Meta.KeyBindingFlags.NONE, (Meta.KeyHandlerFunc) zoom_in);
display.add_keybinding ("zoom-out", schema, Meta.KeyBindingFlags.NONE, (Meta.KeyHandlerFunc) zoom_out);
}

~Zoom () {
if (wm == null) {
return;
}

unowned var display = wm.get_display ();
display.remove_keybinding ("zoom-in");
display.remove_keybinding ("zoom-out");

if (mouse_poll_timer > 0) {
Source.remove (mouse_poll_timer);
mouse_poll_timer = 0;
}
}

[CCode (instance_pos = -1)]
private void zoom_in (Meta.Display display, Meta.Window? window,
Clutter.KeyEvent event, Meta.KeyBinding binding) {
zoom (SHORTCUT_DELTA, true, true);
}

[CCode (instance_pos = -1)]
private void zoom_out (Meta.Display display, Meta.Window? window,
Clutter.KeyEvent event, Meta.KeyBinding binding) {
zoom (-SHORTCUT_DELTA, true, true);
}

private inline Graphene.Point compute_new_pivot_point () {
unowned var wins = wm.ui_group;
Graphene.Point coords;
wm.get_display ().get_cursor_tracker ().get_pointer (out coords, null);
var new_pivot = Graphene.Point () {
x = coords.x / wins.width,
y = coords.y / wins.height
};

return new_pivot;
}

private void zoom (float delta, bool play_sound, bool animate) {
// Nothing to do if zooming out of our bounds is requested
if ((current_zoom <= MIN_ZOOM && delta < 0) || (current_zoom >= MAX_ZOOM && delta >= 0)) {
if (play_sound) {
Gdk.beep ();
}
return;
}

unowned var wins = wm.ui_group;
// Add timer to poll current mouse position to reposition window-group
// to show requested zoomed area
if (mouse_poll_timer == 0) {
wins.pivot_point = compute_new_pivot_point ();

mouse_poll_timer = Timeout.add (MOUSE_POLL_TIME, () => {
var new_pivot = compute_new_pivot_point ();
if (wins.pivot_point.equal (new_pivot)) {
return true;
}

wins.save_easing_state ();
wins.set_easing_mode (Clutter.AnimationMode.LINEAR);
wins.set_easing_duration (MOUSE_POLL_TIME);
wins.pivot_point = new_pivot;
wins.restore_easing_state ();
return true;
});
}

current_zoom += delta;
var animation_duration = animate ? ANIMATION_DURATION : 0;

if (wins_handler_id > 0) {
wins.disconnect (wins_handler_id);
wins_handler_id = 0;
}

if (current_zoom <= MIN_ZOOM) {
current_zoom = MIN_ZOOM;

if (mouse_poll_timer > 0) {
Source.remove (mouse_poll_timer);
mouse_poll_timer = 0;
}

wins.save_easing_state ();
wins.set_easing_mode (Clutter.AnimationMode.EASE_OUT_CUBIC);
wins.set_easing_duration (animation_duration);
wins.set_scale (MIN_ZOOM, MIN_ZOOM);
wins.restore_easing_state ();

if (animate) {
wins_handler_id = wins.transitions_completed.connect (() => {
wins.disconnect (wins_handler_id);
wins_handler_id = 0;
wins.set_pivot_point (0.0f, 0.0f);
});
} else {
wins.set_pivot_point (0.0f, 0.0f);
}

return;
}

wins.save_easing_state ();
wins.set_easing_mode (Clutter.AnimationMode.EASE_OUT_CUBIC);
wins.set_easing_duration (animation_duration);
wins.set_scale (current_zoom, current_zoom);
wins.restore_easing_state ();
}
}
3 changes: 2 additions & 1 deletion compositor/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ compositor_files = files(
'MediaFeedback.vala',
'PointerLocator.vala',
'Utils.vala',
'WindowManager.vala'
'WindowManager.vala',
'Zoom.vala'
)

executable(
Expand Down
14 changes: 14 additions & 0 deletions data/compositor.gschema.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema path="/io/elementary/greeter-compositor/keybindings/" id="io.elementary.greeter-compositor.keybindings">
<key type="as" name="zoom-in">
<default><![CDATA[['<Super>plus', '<Super>KP_Add']]]></default>
<summary>Zoom in</summary>
<description></description>
</key>
<key type="as" name="zoom-out">
<default><![CDATA[['<Super>minus', '<Super>KP_Subtract']]]></default>
<summary>Zoom out</summary>
</key>
</schema>
</schemalist>
6 changes: 6 additions & 0 deletions data/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ conf_data = configuration_data()
conf_data.set('GETTEXT_PACKAGE', meson.project_name())
conf_data.set('PROJECT_NAME', meson.project_name())

install_data(
'compositor.gschema.xml',
install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'glib-2.0', 'schemas'),
rename: 'io.elementary.greeter-compositor.gschema.xml'
)

desktop_in = configure_file (
input: meson.project_name() + '.desktop.in.in',
output: meson.project_name() + '.desktop.in',
Expand Down
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@ vapigen = find_program('vapigen', required: false)
if vapigen.found()
subdir('vapi')
endif

gnome.post_install(glib_compile_schemas: true)