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

Move notifications out of the way for overlapping dock windows #2056

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
58 changes: 50 additions & 8 deletions src/NotificationStack.vala
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ public class Gala.NotificationStack : Object {

private const int WIDTH = 300;

private int stack_y;
private int stack_width;
private int offset_stack_y;

private Mtk.Rectangle notification_area = { 0, 0, 0, 0 };

public Meta.Display display { get; construct; }

Expand All @@ -46,6 +47,13 @@ public class Gala.NotificationStack : Object {
monitor_manager.monitors_changed_internal.connect (update_stack_allocation);
display.workareas_changed.connect (update_stack_allocation);
update_stack_allocation ();

ShellClientsManager.get_instance ().positioned_window_created.connect ((window) => {
window.size_changed.connect (update_offset_y);
window.position_changed.connect (update_offset_y);
window.unmanaged.connect (update_offset_y);
update_offset_y ();
});
}

public void show_notification (Meta.WindowActor notification, bool animate)
Expand Down Expand Up @@ -103,7 +111,7 @@ public class Gala.NotificationStack : Object {
notification_x_pos = 0;
}

move_window (notification, notification_x_pos, stack_y + TOP_OFFSET + InternalUtils.scale_to_int (ADDITIONAL_MARGIN, scale));
move_window (notification, notification_x_pos, offset_stack_y + TOP_OFFSET + InternalUtils.scale_to_int (ADDITIONAL_MARGIN, scale));
notifications.insert (0, notification);
}

Expand All @@ -112,13 +120,15 @@ public class Gala.NotificationStack : Object {
var area = display.get_workspace_manager ().get_active_workspace ().get_work_area_for_monitor (primary);

var scale = display.get_monitor_scale (primary);
stack_width = InternalUtils.scale_to_int (WIDTH + MARGIN, scale);
var stack_width = InternalUtils.scale_to_int (WIDTH + MARGIN, scale);

stack_y = area.y;
notification_area.x = area.x + area.width - stack_width;
notification_area.y = area.y;
notification_area.width = stack_width;
}

private void update_positions (bool animate, float scale, float add_y = 0.0f) {
var y = stack_y + TOP_OFFSET + add_y + InternalUtils.scale_to_int (ADDITIONAL_MARGIN, scale);
var y = offset_stack_y + TOP_OFFSET + add_y + InternalUtils.scale_to_int (ADDITIONAL_MARGIN, scale);
var i = notifications.size;
var delay_step = i > 0 ? 150 / i : 0;
var iterator = 0;
Expand Down Expand Up @@ -158,6 +168,8 @@ public class Gala.NotificationStack : Object {

y += window.get_frame_rect ().height;
}

notification_area.height = (int) y - notification_area.y;
}

public void destroy_notification (Meta.WindowActor notification, bool animate) {
Expand All @@ -167,11 +179,11 @@ public class Gala.NotificationStack : Object {
notification.set_easing_mode (Clutter.AnimationMode.EASE_IN_QUAD);
notification.opacity = 0;

notification.x += stack_width;
notification.x += notification_area.width;
notification.restore_easing_state ();
} else {
notification.opacity = 0;
notification.x += stack_width;
notification.x += notification_area.width;
}

var primary = display.get_primary_monitor ();
Expand All @@ -181,6 +193,36 @@ public class Gala.NotificationStack : Object {
update_positions (animate, scale);
}

private void update_offset_y () {
int max_y = notification_area.y;
foreach (var window in display.list_all_windows ()) {
if (!ShellClientsManager.get_instance ().is_positioned_window (window)) {
continue;
}

if (!window.get_frame_rect ().overlap (notification_area)) {
continue;
}

// Ignore windows that are too big to fit even a single notification on screen
// Conveniently this will make us ignore the fullscreen invisible wingpanel window
var primary = display.get_primary_monitor ();
var area = display.get_workspace_manager ().get_active_workspace ().get_work_area_for_monitor (primary);
if (window.get_frame_rect ().height > area.height - notification_area.height / notifications.size) {
continue;
}

max_y = (int) Math.fmax (max_y, window.get_frame_rect ().y + window.get_frame_rect ().height);
}

if (max_y != offset_stack_y) {
offset_stack_y = max_y;

var scale = display.get_monitor_scale (display.get_primary_monitor ());
update_positions (true, scale);
}
}

/**
* This function takes care of properly updating both the actor
* position and the actual window position.
Expand Down
16 changes: 16 additions & 0 deletions src/ShellClients/ShellClientsManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ public class Gala.ShellClientsManager : Object {
return instance;
}

/**
* This will be emitted when a window requests to be positioned via the protocol methods
* or if a window that's a transient of a positioned window was created.
*/
public signal void positioned_window_created (Meta.Window window);

public WindowManager wm { get; construct; }

private NotificationsClient notifications_client;
Expand All @@ -43,6 +49,12 @@ public class Gala.ShellClientsManager : Object {
parse_mutter_hints (window);
});
}

wm.get_display ().window_created.connect ((window) => {
if (is_positioned_window (window)) {
positioned_window_created (window);
}
});
}

private async void start_clients () {
Expand Down Expand Up @@ -156,6 +168,8 @@ public class Gala.ShellClientsManager : Object {

// connect_after so we make sure the PanelWindow can destroy its barriers and struts
window.unmanaging.connect_after (() => windows.remove (window));

positioned_window_created (window);
}

/**
Expand Down Expand Up @@ -191,6 +205,8 @@ public class Gala.ShellClientsManager : Object {
centered_windows[window] = new CenteredWindow (wm, window);

window.unmanaging.connect_after (() => centered_windows.remove (window));

positioned_window_created (window);
}

public bool is_positioned_window (Meta.Window window) {
Expand Down