Skip to content

Commit

Permalink
version 16.2: enable windows suggestions on edge tiling, add spanish …
Browse files Browse the repository at this point in the history
…and simplified chinese, add support to GNOME 48 and more
  • Loading branch information
domferr committed Feb 14, 2025
1 parent 73a1558 commit 8a1f216
Show file tree
Hide file tree
Showing 26 changed files with 1,743 additions and 227 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

[![release](https://img.shields.io/badge/Release_v16-blue?style=for-the-badge)]([https://ko-fi.com/domferr](https://github.com/domferr/tilingshell/releases))
![](https://img.shields.io/github/license/domferr/tilingshell?style=for-the-badge)
![](https://img.shields.io/badge/GNOME-42--47-e04196?style=for-the-badge&logo=gnome&logoColor=white)
![](https://img.shields.io/badge/GNOME-42--48-e04196?style=for-the-badge&logo=gnome&logoColor=white)
[![kofi](https://img.shields.io/badge/Donate_on_Ko--fi-purple?logo=ko-fi&style=for-the-badge)](https://ko-fi.com/domferr)
[![kofi](https://img.shields.io/badge/Patreon-F96854?style=for-the-badge&logo=patreon&logoColor=white)](https://patreon.com/domferr)
[![patreon](https://img.shields.io/badge/Patreon-F96854?style=for-the-badge&logo=patreon&logoColor=white)](https://patreon.com/domferr)

# Tiling Shell #

This is a Gnome Shell extension implementing modern windows tiling system by extending GNOME's default 2 columns to any layout you want! Can be installed on Gnome Shells from **40 to 47** on X11 and Wayland: the most recent GNOME Shell is supported, and older releases will include all the features and bug fixes!
This is a Gnome Shell extension implementing modern windows tiling system by extending GNOME's default 2 columns to any layout you want! Can be installed on Gnome Shells from **42 to 48** on X11 and Wayland: the most recent GNOME Shell is supported, and older releases will include all the features and bug fixes!

- 🤩 First and only extension that provides Windows 11's **snap assistant**
- 🖥️🖥️ **multiple monitors support**, even with different scaling factors!
Expand Down Expand Up @@ -40,6 +40,9 @@ Have issues, you want to suggest a new feature or contribute? Please open a new
| [⬇️](#tiling-buttons) **Tiling Buttons** | [⬇️](#per-workspace-layout) **Per-workspace layout** | [⬇️](#auto-tiling) **Auto-tiling** | [⬇️](#tiling-context-menu) **Tiling context menu** |
| [⬇️](#smart-border-radius) **Smart border radius** | [⬇️](#windows-suggestions) **Windows Suggestions**

## 🎉🎉 Tiling Shell's AWESOME Supporters!
Thank you to the :star2: **amazing** <a href="https://patreon.com/domferr"><img src="https://img.shields.io/badge/Patreons-F96854?logo=patreon&logoColor=white)" height="14px"/><a/> and **everyone** who donated on <a href="https://ko-fi.com/domferr"><img src="https://img.shields.io/badge/_Ko--fi-794bc4?logo=ko-fi&logoColor=white" height="14px"/><a/>! :medal_sports:Tomoyuki Kashiro and Markus Huggler on Patreon:medal_sports: and Nick, thy-fi, iatanas0v, Chris, wbezs, DaneshManoharan, Tamas, Ivan Banha and many more on Ko-fi! You are on a mission to **make Linux window management better for everyone**!

### Tiling System ###
When grabbing and moving a window, press <kbd>CTRL</kbd> key to show the tiling layout (you can choose another key from the preferences). When moving on a tile, it will highlight. Ungrab the window to place that window on the highlighted tile.

Expand Down Expand Up @@ -160,6 +163,7 @@ Right-click on a window to use the auto-tile buttons and the snap assistant from

In GNOME, different windows may have different border radius. Drawing a border around the focused window is hard because it is not possible to know the window border radius. All the existing extensions just draw a border with a static value, making the UI less polished. Tiling Shell, **dynamically** computes the focused window border radius at runtime. Moreover, if you have an extension or anything else who customize the border radius, the focused window border radius adapts as well! This can be enabled/disabled from the extension's preferences too.


<p align="center">
<img src="https://github.com/user-attachments/assets/cfaca5f9-d9b2-4739-9426-1aebb5f33c29" width=304 />
<img src="https://github.com/user-attachments/assets/8e68abff-66e5-4b85-a6ce-0bd2da7be166" width=332 />
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tilingshell",
"version": "16.0",
"version": "16.2",
"author": "Domenico Ferraro <[email protected]>",
"private": true,
"license": "GPL v2.0",
Expand Down
Binary file added resources/locale/es/LC_MESSAGES/tilingshell.mo
Binary file not shown.
Binary file modified resources/locale/it/LC_MESSAGES/tilingshell.mo
Binary file not shown.
Binary file added resources/locale/zh_CN/LC_MESSAGES/tilingshell.mo
Binary file not shown.
5 changes: 3 additions & 2 deletions resources/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
"44",
"45",
"46",
"47"
"47",
"48"
],
"version": 99,
"version-name": "16.1",
"version-name": "16.2",
"url": "https://github.com/domferr/tilingshell",
"settings-schema": "org.gnome.shell.extensions.tilingshell",
"gettext-domain": "tilingshell",
Expand Down
20 changes: 11 additions & 9 deletions src/components/editor/editorDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import Layout from '@/components/layout/Layout';
import Tile from '@/components/layout/Tile';
import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import { enableScalingFactorSupport, getMonitorScalingFactor } from '@utils/ui';
import {
enableScalingFactorSupport,
getMonitorScalingFactor,
setWidgetOrientation,
} from '@utils/ui';
import { _ } from '../../translations';

@registerGObjectClass
Expand Down Expand Up @@ -51,7 +55,6 @@ export default class EditorDialog extends ModalDialog.ModalDialog {
);

this._layoutsBoxLayout = new St.BoxLayout({
vertical: false, // horizontal box layout
styleClass: 'layouts-box-layout',
xAlign: Clutter.ActorAlign.CENTER,
});
Expand Down Expand Up @@ -83,7 +86,7 @@ export default class EditorDialog extends ModalDialog.ModalDialog {
}

private _makeLegendDialog(params: { onClose: () => void; path: string }) {
const suggestion1 = new St.BoxLayout({ vertical: false });
const suggestion1 = new St.BoxLayout();
// LEFT-CLICK to split a tile
suggestion1.add_child(
new St.Label({
Expand All @@ -105,7 +108,7 @@ export default class EditorDialog extends ModalDialog.ModalDialog {
}),
);

const suggestion2 = new St.BoxLayout({ vertical: false });
const suggestion2 = new St.BoxLayout();
// LEFT-CLICK + CTRL to split a tile vertically
suggestion2.add_child(
new St.Label({
Expand Down Expand Up @@ -146,7 +149,7 @@ export default class EditorDialog extends ModalDialog.ModalDialog {
}),
);

const suggestion3 = new St.BoxLayout({ vertical: false });
const suggestion3 = new St.BoxLayout();
// RIGHT-CLICK to delete a tile
suggestion3.add_child(
new St.Label({
Expand All @@ -169,7 +172,6 @@ export default class EditorDialog extends ModalDialog.ModalDialog {
);

const suggestion4 = new St.BoxLayout({
vertical: false,
xExpand: true,
margin_top: 16,
});
Expand All @@ -196,9 +198,9 @@ export default class EditorDialog extends ModalDialog.ModalDialog {
);

const legend = new St.BoxLayout({
vertical: true,
styleClass: 'legend',
});
setWidgetOrientation(legend, true);
legend.add_child(suggestion1);
legend.add_child(suggestion2);
legend.add_child(suggestion3);
Expand Down Expand Up @@ -237,10 +239,10 @@ export default class EditorDialog extends ModalDialog.ModalDialog {

params.layouts.forEach((lay, btnInd) => {
const box = new St.BoxLayout({
vertical: true,
xAlign: Clutter.ActorAlign.CENTER,
styleClass: 'layout-button-container',
});
setWidgetOrientation(box, true);
this._layoutsBoxLayout.add_child(box);
const btn = new LayoutButton(
box,
Expand Down Expand Up @@ -282,10 +284,10 @@ export default class EditorDialog extends ModalDialog.ModalDialog {
});

const box = new St.BoxLayout({
vertical: true,
xAlign: Clutter.ActorAlign.CENTER,
styleClass: 'layout-button-container',
});
setWidgetOrientation(box, true);
this._layoutsBoxLayout.add_child(box);
const newLayoutBtn = new LayoutButton(
box,
Expand Down
1 change: 0 additions & 1 deletion src/components/snapassist/snapAssist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class SnapAssistContent extends St.BoxLayout {
name: 'snap_assist_content',
xAlign: Clutter.ActorAlign.CENTER,
yAlign: Clutter.ActorAlign.CENTER,
vertical: false,
reactive: true,
styleClass: 'popup-menu-content snap-assistant',
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/tilingsystem/resizeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,9 +392,9 @@ class WindowClone extends St.Widget {
yAlign: Clutter.ActorAlign.CENTER,
xExpand: true,
yExpand: true,
vertical: true,
style: "spacing: 16px;"
});
setWidgetOrientation(box, true);
box.add_child(this._createAppIcon(window, APP_ICON_SIZE));
box.add_child(new St.Label({
xAlign: Clutter.ActorAlign.CENTER,
Expand Down
29 changes: 21 additions & 8 deletions src/components/tilingsystem/tilingLayout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { registerGObjectClass } from '@/utils/gjs';
import { Clutter, Mtk, Meta, St } from '@gi.ext';
import { Clutter, Mtk, Meta } from '@gi.ext';
import TilePreview, {
TilePreviewConstructorProperties,
} from '../tilepreview/tilePreview';
Expand All @@ -9,11 +9,12 @@ import Tile from '../layout/Tile';
import {
buildRectangle,
buildTileGaps,
clampPointInsideRect,
isPointInsideRect,
squaredEuclideanDistance,
} from '@utils/ui';
import TileUtils from '@components/layout/TileUtils';
import { logger } from '@utils/logger';
import { logger, rect_to_string } from '@utils/logger';
import GlobalState from '@utils/globalState';
import { KeyBindingsDirection } from '@keybindings';

Expand Down Expand Up @@ -355,9 +356,13 @@ export default class TilingLayout extends LayoutWidget<DynamicTilePreview> {
return [true, results];
}

// enlarge the side of the direction and search a tile that contains that point
// clamp to ensure we do not go outside of the container area (e.g. the screen)
public findNearestTileDirection(
source: Mtk.Rectangle,
direction: KeyBindingsDirection,
clamp: boolean,
enlarge: number,
): { rect: Mtk.Rectangle; tile: Tile } | undefined {
if (direction === KeyBindingsDirection.NODIRECTION) return undefined;

Expand All @@ -366,10 +371,6 @@ export default class TilingLayout extends LayoutWidget<DynamicTilePreview> {
y: source.y + source.height / 2,
};

// enlarge the side of the direction and search a tile that contains that point
// clamp to ensure we do not go outside of the container area (e.g. the screen)
const enlarge = 64;

switch (direction) {
case KeyBindingsDirection.RIGHT:
sourceCoords.x = source.x + source.width + enlarge;
Expand All @@ -392,8 +393,20 @@ export default class TilingLayout extends LayoutWidget<DynamicTilePreview> {
this._containerRect.width + this._containerRect.x ||
sourceCoords.y < this._containerRect.y ||
sourceCoords.y > this._containerRect.height + this._containerRect.y
)
return undefined;
) {
if (!clamp) return undefined;
// return undefined;
sourceCoords.x = Math.clamp(
sourceCoords.x,
this._containerRect.x,
this._containerRect.width + this._containerRect.x,
);
sourceCoords.y = Math.clamp(
sourceCoords.y,
this._containerRect.y,
this._containerRect.height + this._containerRect.y,
);
}

// uncomment to show debugging
/* global.windowGroup
Expand Down
11 changes: 11 additions & 0 deletions src/components/tilingsystem/tilingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ export class TilingManager {
direction: KeyBindingsDirection,
force: boolean,
spanFlag: boolean,
clamp: boolean,
): boolean {
let destination: { rect: Mtk.Rectangle; tile: Tile } | undefined;
if (spanFlag && window.get_maximized()) return false;
Expand Down Expand Up @@ -372,9 +373,17 @@ export class TilingManager {
tile: TileUtils.build_tile(rect, this._workArea),
};
} else if (window.get_monitor() === this._monitor.index) {
const maxGap = Math.max(
tilingLayout.innerGaps.right,
tilingLayout.innerGaps.left,
tilingLayout.innerGaps.right,
tilingLayout.innerGaps.bottom,
);
destination = tilingLayout.findNearestTileDirection(
windowRectCopy,
direction,
clamp,
3 * maxGap,
);
} else {
destination = tilingLayout.findNearestTile(windowRectCopy);
Expand All @@ -384,6 +393,8 @@ export class TilingManager {
if (
window.get_monitor() === this._monitor.index &&
destination &&
!window.maximizedHorizontally &&
!window.maximizedVertically &&
(window as ExtendedWindow).assignedTile &&
(window as ExtendedWindow).assignedTile?.x === destination.tile.x &&
(window as ExtendedWindow).assignedTile?.y === destination.tile.y &&
Expand Down
2 changes: 1 addition & 1 deletion src/components/windowBorderManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ If in the future we want to have MULTIPLE borders visible AT THE SAME TIME,
when the windows are restacked we have to restack the borders as well.
display.connect('restacked', (display) => {
let wg = Meta.get_window_group_for_display(display);
let wg = Meta.get_window_group_for_display(display); // From GNOME 48 use Meta.Compositor.get_window_group
forEachWindowInTheWindowGroup((win) => {
winBorder = getWindowBorder(win)
winActor = win.get_compositor_private()
Expand Down
3 changes: 2 additions & 1 deletion src/components/window_menu/overriddenWindowMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
enableScalingFactorSupport,
getMonitorScalingFactor,
getWindows,
setWidgetOrientation,
} from '@utils/ui';
import ExtendedWindow from '@components/tilingsystem/extendedWindow';
import TileUtils from '@components/layout/TileUtils';
Expand Down Expand Up @@ -235,9 +236,9 @@ export default class OverriddenWindowMenu extends GObject.Object {
yAlign: Clutter.ActorAlign.CENTER,
xExpand: true,
yExpand: true,
vertical: true,
style: 'spacing: 16px !important',
});
setWidgetOrientation(container, true);
layoutsPopupMenu.add_child(container);
const layoutsPerRow = 4;
const rows: St.BoxLayout[] = [];
Expand Down
7 changes: 3 additions & 4 deletions src/components/windowsSuggestions/suggestionsTilePreview.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { registerGObjectClass } from '@/utils/gjs';
import { GObject, St, Clutter, Mtk } from '@gi.ext';
import TilePreview from '../tilepreview/tilePreview';
import { buildBlurEffect } from '@utils/ui';
import { buildBlurEffect, setWidgetOrientation } from '@utils/ui';
import Tile from '@components/layout/Tile';
import MasonryLayoutManager from './masonryLayoutManager';

Expand All @@ -24,7 +24,7 @@ export default class SuggestionsTilePreview extends TilePreview {
};

private _blur: boolean;
private _container: St.Widget;
private _container: St.BoxLayout;
private _scrollView: St.ScrollView;

constructor(params: {
Expand Down Expand Up @@ -60,11 +60,11 @@ export default class SuggestionsTilePreview extends TilePreview {
this.layout_manager = new Clutter.BinLayout();

this._container = new St.BoxLayout({
vertical: true,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
style: `spacing: ${MASONRY_LAYOUT_SPACING}px;`,
});
setWidgetOrientation(this._container, true);
this._scrollView = new St.ScrollView({
style_class: 'vfade',
vscrollbar_policy: St.PolicyType.AUTOMATIC,
Expand Down Expand Up @@ -186,7 +186,6 @@ export default class SuggestionsTilePreview extends TilePreview {
// add each row
placements.forEach((row) => {
const rowBox = new St.BoxLayout({
vertical: false,
x_align: Clutter.ActorAlign.CENTER,
style: `spacing: ${MASONRY_LAYOUT_SPACING}px;`,
});
Expand Down
24 changes: 16 additions & 8 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,13 +446,6 @@ export default class TilingShellExtension extends Extension {
focus_window.unmaximize(Meta.MaximizeFlags.BOTH);
return;
}
const success = monitorTilingManager.onKeyboardMoveWindow(
focus_window,
direction,
false,
spanFlag,
);
if (success || direction === KeyBindingsDirection.NODIRECTION) return;

let displayDirection = Meta.DisplayDirection.DOWN;
switch (direction) {
Expand All @@ -466,12 +459,26 @@ export default class TilingShellExtension extends Extension {
displayDirection = Meta.DisplayDirection.UP;
break;
}

const neighborMonitorIndex = display.get_monitor_neighbor_index(
focus_window.get_monitor(),
displayDirection,
);

const success = monitorTilingManager.onKeyboardMoveWindow(
focus_window,
direction,
false,
spanFlag,
neighborMonitorIndex === -1, // clamp if there is NOT a monitor in this direction
);

if (
success ||
direction === KeyBindingsDirection.NODIRECTION ||
neighborMonitorIndex === -1
)
return;

// if the window is maximized, direction is UP and there is a monitor above, minimize the window
if (
(focus_window.maximizedHorizontally ||
Expand All @@ -493,6 +500,7 @@ export default class TilingShellExtension extends Extension {
direction,
true,
spanFlag,
false,
);
}

Expand Down
Loading

0 comments on commit 8a1f216

Please sign in to comment.