Skip to content

Compatibility with Fullscreen Avoider #707

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

Draft
wants to merge 27 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bcbad9f
Patch monitor.inFullscreen to check PaperWM spaces
Lythenas Nov 22, 2023
2143e0c
Stop hiding topbar
Lythenas Nov 22, 2023
6b5a125
Move patch to patches.js
Lythenas Nov 23, 2023
1e83182
Hide our topbar when the real one is moved onto it
Lythenas Nov 23, 2023
41bbb92
Show correct info when panel is not on primary monitor
Lythenas Nov 23, 2023
2e0f9cc
Don't shrink workarea if panel was moved to secondary monitor
Lythenas Nov 23, 2023
862c0e6
Expose paperwm in global variable
Lythenas Nov 24, 2023
a40d6d1
Fix null check
Lythenas Nov 24, 2023
03f7196
Check position of monitor instead of backdrop
Lythenas Nov 25, 2023
33acbd9
Add undefined check to patch
Lythenas Nov 25, 2023
45d14fc
Always update topbar workspace indicator during switching
Lythenas Nov 25, 2023
270b2c4
Run layout after workspace switch animation
Lythenas Nov 27, 2023
dde175b
Fix topbar when swapping workspaces
Lythenas Dec 2, 2023
718af77
Don't change topbar style when updating window position indicator
Lythenas Dec 2, 2023
ee87d11
Set topbar elements visible by default
Lythenas Dec 2, 2023
24cc09b
Hide focus mode icon if topbar is hidden on ws
Lythenas Dec 2, 2023
333b1ce
Add helper Topbar.isOnMonitor
Lythenas Dec 2, 2023
dfd39d6
Only update topbar ws name if on the same monitor
Lythenas Dec 2, 2023
b1e9391
Fix detection when window starts in fullscreen
Lythenas Dec 25, 2023
4d829a8
Queue layout after panelBox position changed
Lythenas Dec 26, 2023
6d2ae74
Merge remote-tracking branch 'upstream/develop' into fullscreen-avoid…
Lythenas Dec 28, 2023
d780540
Use correct space in fixTopBar
Lythenas Dec 30, 2023
a0263a1
Re-layout when workarea changes
Lythenas Dec 30, 2023
0ed9646
Merge remote-tracking branch 'upstream/develop' into fullscreen-avoid…
Lythenas Jan 12, 2024
7c7f2c9
Fix jumpyness when entering fullscreen
Lythenas Jan 13, 2024
9d8799b
Detect floating and scratch windows as fullscreen
Lythenas Jan 13, 2024
d67b64e
Merge remote-tracking branch 'upstream/develop' into fullscreen-avoid…
Lythenas Mar 30, 2024
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
5 changes: 5 additions & 0 deletions extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default class PaperWM extends Extension {
Gestures, Keybindings, LiveAltTab, Navigator, Stackoverlay, Scratch,
Workspace, Tiling, Topbar, App,
];
Tiling = Tiling;

#userStylesheet = null;

Expand All @@ -67,6 +68,8 @@ export default class PaperWM extends Extension {
m.enable(this);
}
});

global.paperwm = this;
}

disable() {
Expand All @@ -79,6 +82,8 @@ export default class PaperWM extends Extension {
});

this.disableUserStylesheet();

global.paperwm = undefined;
}

/**
Expand Down
78 changes: 77 additions & 1 deletion patches.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as WindowManager from 'resource:///org/gnome/shell/ui/windowManager.js'
import * as WindowPreview from 'resource:///org/gnome/shell/ui/windowPreview.js';
import * as Params from 'resource:///org/gnome/shell/misc/params.js';

import { Utils, Tiling, Scratch, Settings, OverviewLayout } from './imports.js';
import { Utils, Tiling, Scratch, Settings, Topbar, OverviewLayout } from './imports.js';

/**
Some of Gnome Shell's default behavior is really sub-optimal when using
Expand All @@ -36,9 +36,11 @@ export function enable(extension) {
enableOverrides();
setupRuntimeDisables();
setupActions();
setupFullscreenAvoiderSupport();
}

export function disable() {
undoFullscreenAvoiderSupport();
disableOverrides();
restoreRuntimeDisables();
actions.forEach(a => global.stage.add_action(a));
Expand Down Expand Up @@ -636,3 +638,77 @@ export function _checkWorkspaces() {
this._checkWorkspacesId = 0;
return false;
}

function setupFullscreenAvoiderSupport() {
// Patch monitor objects prototype to check our space for the inFullscreen
// property.
const monitor1 = Main.layoutManager.monitors[0];
Object.defineProperties(
Object.getPrototypeOf(monitor1),
{
inFullscreen: {
// NOTE: Needs to be non-arrow function so `this` is bound
// correctly on call. This is necessary because we modify the
// prototype of multiple objects here.
get: function() {
// NOTE: This is wrapped in try-catch because an error here
// makes windows unclickable.
try {
// Find active space for monitor (this)
// NOTE: Indexing spaces.monitors[this] does not work
if (Tiling.spaces?.monitors) {
for (const [monitor, space] of Tiling.spaces.monitors) {
if (monitor.index == this.index) {
return space.hasFullScreenWindow();
}
}
}
// Check for scratch windows separately since they don't
// belong to a workspace
if (Scratch.getScratchWindows().some(w => w.get_monitor() == this.index && w.fullscreen)) {
return true;
}
} catch (e) {
console.error(e);
}
// should not be reached, just here in case there is an
// error above
console.error(new Error(`Failed to find space for monitor`));
return false;
},
enumerable: true,
}
});

signals.connect(Main.layoutManager.panelBox, "notify::position", () => {
try {
if (Tiling.spaces.monitors) {
for (const [_monitor, space] of Tiling.spaces.monitors) {
// console.debug(`Updating space ${space.name}`);
space.setSpaceTopbarElementsVisible();
Topbar.updateWorkspaceIndicator(space.index);
Topbar.fixTopBar();
}
}
} catch (e) {
console.error(e);
}
});
}

function undoFullscreenAvoiderSupport() {
const monitor1 = Main.layoutManager.monitors[0];
// Reset value to false. This might be incorrect, but will be updated by
// gnome again after some time.
Object.defineProperties(
Object.getPrototypeOf(monitor1),
{
inFullscreen: {
value: false,
writable: true,
enumerable: true,
}
}
);
signals.disconnect(Main.layoutManager.panelBox);
}
75 changes: 64 additions & 11 deletions tiling.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ export function enable(extension) {
});
};

signals.connect(global.display, 'workareas-changed', () => {
spaces.layout(false, {ensure: false});
});

if (Main.layoutManager._startingUp) {
// Defer workspace initialization until existing windows are accessible.
// Otherwise we're unable to restore the tiling-order. (when restarting
Expand Down Expand Up @@ -623,10 +627,9 @@ export class Space extends Array {
this.setSpaceTopbarElementsVisible(false);
}
// compensate to keep window position bar on all monitors
else if (Settings.prefs.show_window_position_bar) {
else if (Settings.prefs.show_window_position_bar && this.showTopBar) {
const panelBoxHeight = Topbar.panelBox.height;
const monitor = Main.layoutManager.primaryMonitor;
if (monitor !== this.monitor) {
if (!this.hasTopBar) {
workArea.y += panelBoxHeight;
workArea.height -= panelBoxHeight;
}
Expand Down Expand Up @@ -736,6 +739,8 @@ export class Space extends Array {
if (this._layoutQueued)
return;

console.log(new Error(this.name));

this._layoutQueued = true;
Utils.later_add(Meta.LaterType.RESIZE, () => {
this._layoutQueued = false;
Expand Down Expand Up @@ -826,6 +831,8 @@ export class Space extends Array {
}

addWindow(metaWindow, index, row) {
const previousHasFullScreenWindow = this.hasFullScreenWindow();

if (!this.selectedWindow)
this.selectedWindow = metaWindow;
if (this.indexOf(metaWindow) !== -1)
Expand Down Expand Up @@ -893,6 +900,16 @@ export class Space extends Array {
this.targetX = workArea.x + Math.round((workArea.width - this.cloneContainer.width) / 2);
}
this.emit('window-added', metaWindow, index, row);
if (previousHasFullScreenWindow !== this.hasFullScreenWindow()) {
// This means a window started in fullscreen.
//
// We (re-)emit this signal because the in-fullscreen-changed event
// was most likely already triggered before the window was
// registered with PaperWM. This mean space.hasFullScreenWindow()
// returned a (possibly) wrong value because the space did not know
// about the window.
global.display.emit("in-fullscreen-changed");
}
return true;
}

Expand Down Expand Up @@ -964,11 +981,15 @@ export class Space extends Array {
return true;
}

getFloatingWindows() {
return this._floating;
}

/**
* Returns true iff this space has a currently fullscreened window.
*/
hasFullScreenWindow() {
return this.getWindows().some(w => w.fullscreen);
return this.getWindows().some(w => w.fullscreen) || this.getFloatingWindows().some(w => w.fullscreen);
}

swap(direction, metaWindow) {
Expand Down Expand Up @@ -1471,7 +1492,12 @@ export class Space extends Array {
* Returns true if this space has the topbar.
*/
get hasTopBar() {
return this.monitor && this.monitor === Topbar.panelMonitor();
if (inPreview) {
// always show topbar in overview
return true;
}

return Topbar.isOnMonitor(this.monitor);
}

updateColor() {
Expand Down Expand Up @@ -1539,7 +1565,8 @@ border-radius: ${borderWidth}px;
enableWindowPositionBar(enable = true) {
const add =
enable &&
Settings.prefs.show_window_position_bar;
Settings.prefs.show_window_position_bar &&
this.showTopBar;
if (add) {
[this.windowPositionBarBackdrop, this.windowPositionBar]
.forEach(i => {
Expand Down Expand Up @@ -1622,7 +1649,7 @@ border-radius: ${borderWidth}px;
}

// if windowPositionBar is disabled ==> don't show elements
if (!Settings.prefs.show_window_position_bar) {
if (!Settings.prefs.show_window_position_bar || !this.showTopBar) {
setVisible(false);
return;
}
Expand Down Expand Up @@ -2061,6 +2088,10 @@ export const Spaces = class Spaces extends Map {
this.stack = this.mru();
}

layout(animate = true, options = {}) {
this.forEach(space => space.layout(animate, options));
}

/**
The monitors-changed signal can trigger _many_ times when
connection/disconnecting monitors.
Expand Down Expand Up @@ -2381,7 +2412,17 @@ export const Spaces = class Spaces extends Map {
});

// ensure after swapping that the space elements are shown correctly
// layout needed to properly fix topbar
let oldSpace = this.monitors.get(monitor);
let newMonitor = Main.layoutManager.monitors[i];
let newSpace = this.monitors.get(newMonitor);
oldSpace.layout();
newSpace.layout();
Topbar.fixTopBar();
this.setSpaceTopbarElementsVisible(true, { force: true });
if (oldSpace.hasFullScreenWindow() || newSpace.hasFullScreenWindow()) {
global.display.emit("in-fullscreen-changed");
}
}

switchWorkspace(wm, fromIndex, toIndex, animate = false) {
Expand Down Expand Up @@ -2434,7 +2475,11 @@ export const Spaces = class Spaces extends Map {
this.animateToSpace(
toSpace,
fromSpace,
doAnimate);
doAnimate,
// do a layout after the animation is complete
// (i.e. when everything is at the final position)
() => toSpace.layout(),
);

// Update panel to handle target workspace
signals.disconnect(Main.panel, this.touchSignal);
Expand All @@ -2447,7 +2492,7 @@ export const Spaces = class Spaces extends Map {
* See Space.setSpaceTopbarElementsVisible function for what this does.
* @param {boolean} visible
*/
setSpaceTopbarElementsVisible(visible = false, options = {}) {
setSpaceTopbarElementsVisible(visible = true, options = {}) {
this.forEach(s => {
s.setSpaceTopbarElementsVisible(visible, options);
});
Expand Down Expand Up @@ -2600,8 +2645,7 @@ export const Spaces = class Spaces extends Map {
newSpace = monitorSpaces[to];
this.selectedSpace = newSpace;

// if active (source space) is panelMonitor update indicator
if (currentSpace.monitor === Topbar.panelMonitor()) {
if (Topbar.isOnMonitor(currentSpace.monitor)) {
Topbar.updateWorkspaceIndicator(newSpace.index);
}

Expand Down Expand Up @@ -2943,6 +2987,15 @@ export const Spaces = class Spaces extends Map {
return [...this.values()].find(s => uuid === s.uuid);
}

spaceWithTopBar() {
for (const monitor in this.monitors) {
if (this.monitors[monitor].hasTopBar) {
return this.monitors[monitor];
}
}
return null;
}

get selectedSpace() {
return this._selectedSpace ?? this.activeSpace;
}
Expand Down
40 changes: 26 additions & 14 deletions topbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ export function enable (extension) {

signals.connect(Main.overview, 'showing', fixTopBar);
signals.connect(Main.overview, 'hidden', () => {
if (Tiling.spaces.selectedSpace.showTopBar)
return;
fixTopBar();
});

Expand Down Expand Up @@ -720,7 +718,7 @@ export function fixStyle() {
}

export function fixTopBar() {
let space = Tiling?.spaces?.monitors?.get(panelMonitor()) ?? false;
let space = Tiling?.spaces?.spaceWithTopBar() ?? false;
if (!space)
return;

Expand All @@ -732,17 +730,23 @@ export function fixTopBar() {
// check if is currently fullscreened (check focused-floating, focused-scratch, and selected/tiled window)
let fullscreen = focusIsFloatOrScratch ? focused.fullscreen : selected && selected.fullscreen;

if (normal && !space.showTopBar) {
panelBox.scale_y = 0; // Update the workarea to support hide top bar
panelBox.hide();
}
else if (normal && fullscreen) {
panelBox.hide();
}
else {
panelBox.scale_y = 1;
panelBox.show();
if (normal && space.hasTopBar) {
if (!space.showTopBar) {
panelBox.scale_y = 0; // Update the workarea to support hide top bar
panelBox.hide();
} else {
panelBox.scale_y = 1;
panelBox.show();
}
}

// if (normal && !fullscreen && !space.showTopBar) {
// panelBox.scale_y = 0; // Update the workarea to support hide top bar
// panelBox.hide();
// } else {
// panelBox.scale_y = 1;
// panelBox.show();
// }
}

export function fixWorkspaceIndicator() {
Expand All @@ -769,7 +773,7 @@ export function fixFocusModeIcon() {
export function updateWorkspaceIndicator(index) {
let spaces = Tiling.spaces;
let space = spaces?.spaceOf(workspaceManager.get_workspace_by_index(index));
if (space && space.monitor === panelMonitor()) {
if (space && isOnMonitor(space.monitor)) {
setWorkspaceName(space.name);

// also update focus mode
Expand All @@ -788,3 +792,11 @@ export function refreshWorkspaceIndicator() {
export function setWorkspaceName (name) {
menu && menu.setName(name);
}

export function isOnMonitor(monitor) {
// Check if panel position is the same as the monitor position (because
// it is always at position 0,0 relative to the monitor). This is useful
// when an extension moves the panel to another monitor.
const [panelBoxX, panelBoxY] = Main.layoutManager.panelBox.get_transformed_position();
return monitor && monitor.x == panelBoxX && monitor.y == panelBoxY;
}