A window tiling extension for Gnome.
OTHER License
Acknowledgement: The original idea and the first implementation of gTile was developed by vibou with the help from multiple contributors. gTile is now a community supported project and licensed under the GPL v2+.
The preferred installation is through Gnome Extensions.
Alternatively, the most recent stable version may also be downloaded as a distributable archive (gtile.dist.tgz
) through the GitHub releases page.
# Replace `VERSION` with the most recent release.
wget https://github.com/gTile/gTile/releases/download/VERSION/gtile.dist.tgz
# The `-f` flag will perform an upgrade, if necessary.
gnome-extensions install -f ~/Downloads/gtile.dist.tgz
# The changes only become effective once the shell session was restarted.
# In case the extension was installed for the first time it must be enabled afterwards.
gnome-extensions enable gTile@vibou
Alternatively, you can build and install the latest version from GitHub. Make sure to have a working git
and npm
installation.
git clone https://github.com/gTile/gTile.git
cd gTile
npm ci
npm run build:dist
npm run install:extension
After restarting your Gnome shell session you can enable the extension via:
gnome-extensions enable gTile@vibou
The extension can be configured through a dedicated preferences dialog. You can open the extension settings either through the Gnome Extensions app, or by executing the following command in your terminal:
gnome-extensions prefs gTile@vibou
Note that most settings are applied either immediately or after toggling the gTile overlay. There is one exception though. When the theme is changed, the extension needs to be disabled and then enabled again for the new theme to become effective.
gTile can be used either through its graphical user interface ("overlay") or exclusively with the keyboard via shortcuts.
<Super>+<Enter>
(default)Option A: Mouse usage:
Option B: Keyboard usage:
Space
to change the grid scheme [optional]Enter
and the window will resizeEscape
to cancel resizeOption C: Using the keyboard with configured presets:
Resize presets are defined in the preferences window (Resize Presets tab).
3x3 1:1 1:1, 2:2 2:2
on the first press will put the window on the top left corner, on the second press - in the middle of the screen.Presets have a format of "[grid size] [top left coordinate]:[bottom right coordinate]".
3x3 1:1 1:1, 2:2 2:2
(when 2:2 2:2
is triggered, gTile will use 3x3 grid size).You can also resize windows using keyboard shortcuts directly.
There are 3 groups of pre-configured shortcuts, representing the following grid schemes:
Super
+Alt
Super
+Control
Super
+Shift
These "grid selectors" are then combined with a keypad number to define the window placement.
Default shortcuts for Super
+Alt
+[KP_1..KP_9]
Shortcut | Description |
---|---|
Super +Alt +KP_1
|
Bottom left quarter of screen |
Super +Alt +KP_2
|
Bottom half |
Super +Alt +KP_3
|
Bottom right quarter |
Super +Alt +KP_4
|
Center left |
Super +Alt +KP_5
|
Center |
Super +Alt +KP_6
|
Center right |
Super +Alt +KP_7
|
Top left quarter |
Super +Alt +KP_8
|
Top half |
Super +Alt +KP_9
|
Top right quarter |
Notes:
You can do auto tiling for all windows on screen
Super
+KP_Enter
or clicking on gTile icon[1..9,0]
(total 10 available) for vertical columns autotiling, or M
for "main" autotiling.
M
multiple times to cycle through variants.The autotiling layouts can be customized through GridSpec
, a tiny DSL used to describe complex grids. The following Wirth notation describes the syntax of GridSpec
.
gridspec = [ ( colspec | rowspec ) ] .
colspec = "cols" "(" cellspec { "," cellspec } ")" .
rowspec = "rows" "(" cellspec { "," cellspec } ")" .
cellspec = <number> [ ( "d" | ":" ( colspec | rowspec ) ) ] .
Some examples of valid GridSpec
definitions:
cols(1, 3, 1)
rows(1, 1:col(3,3,3), 1d)
dynamic
row, which affects how autotiling does place windows in it. Usually, autotiling assigns at most 1 window to each cell. Cells declared as dynamic
may hold as many windows as necessary. Windows placed into a dynamic cell all get the same share of this cell. For instance, if 2 windows were to be placed in the dynamic cell above, they would each take 50% of the rows width.In general, the autotiling algorithm works as follows:
GridSpec
, find the largest non-dynamic cell (if there are multiple largest cells, the first one is used).
You can expand window - it will fill all available space up to borders with neighbors.
Default keyboard shortcut is <Alt><Ctrl>s
Autogrow works only when gTile window is activated
<Super>+<Enter>
<Alt><Ctrl>s
. Your windows will snap to neighbors.Escape
By default, windows snap seamlessly to one another. You can optionally configure an inset and window spacing to apply a margin between windows (aka spacing) or the screen edges (aka insets). The illustration below depicts how insets and margins are applied to the windows.
gTile is intended to supplement existing Gnome-shell keyboard shortcuts.
Here are some useful Gnome built-ins to keep in mind when configuring gTile:
Shortcut | Description |
---|---|
Super +Up
|
Maximize |
Super +Down
|
Un-Maximize (return to size and position previous to maximizing) |
Super +Left/Right
|
left/right half of screen |
Shift +Super +Up/Down/Left/Right
|
move window to adjacent monitor/workspace |
To build and develop the extension node
, npm
, git
and a few standard GNU utilities are required. Check the scripts
section of package.json
to see which GNU utilities are used.
To get started, checkout the repository and install the required dependencies:
git clone https://github.com/gTile/gTile.git
cd gTile
npm ci
Testing changes can be tedious at times because an extension cannot be updated in-place. The Gnome shell session must be restarted in order for changes to take effect. In Wayland it is usually much easier to start a nested session instead. Check the GJS documentation on how to best debug extensions. Note that nested gnome sessions can take time to load and does not allow to test multi-screen setups. I personally found it most productive to have a second remote system which I used to continuously test the extension on. For this purpose I added a simple new script to package.json
:
{
"scripts": {
"install:remote": "scp gtile.dist.tgz remotehost:~/Downloads && ssh remotehost gnome-extensions install -f ~/Downloads/gtile.dist.tgz",
}
}
Deploying a change is as simple as executing npm run build:dist && npm run install:remote
and restart the Gnome session on the remote system. When developing locally use the npm run install:extension
command instead.
Functional tests are written in TypeScript and must be transpiled via tsc
before AVA can run the tests. The following command takes care of everything and is best be used to run the tests in a one-off fashion.
# For a one-off run of all tests
npm run test
When actively developing tests, it is more convinient (and also faster) to let tsc
continuously transpile the tests and have them run by AVA.
# Let the TypeScript compiler watch for changes and auto-transpile files
npx tsc -w
# In a separate terminal: Run the AVA test runner in watch mode
npm run test -- --watch
Note: All tests are strictly kept in the test/
directory, in isolation to the src/
directory. Test files do not have a special naming convention such as a required .spec.ts
suffix. Thus, keeping tests in a separate directory allows them to be transpiled on-demand when needed and avoids pollution of the redistributable build.
There are some domain-specific terms used throughout the entire code base. This table ought to be a glossary for these terms. Some of these may also translate 1:1 into a TypeScript interface. Check the src/types/
directory.
Term | Meaning |
---|---|
GridSpec |
A simple domain specific language (DSL) used to describe complex grids with weighted col and row sizes and the ability to have cols and rows span over multiple cells. |
Grid |
A rectangular 2D grid that has a specified number of columns and rows. |
Grid size |
Refers to the dimensions of a grid, i.e. its number of columns and rows |
(Grid) offset |
A relative coordinate to a tile in a grid. The top-left tile in a grid is considered to have an offset of (0, 0) . The offset (2, 5) would refer to the tile in the 3rd column and the 6th row. |
(Grid) selection |
A rectangular selection of multiple tiles within a grid. It is defined in term of two offsets which specify the position of two opposite corner tiles of the selection. |
Preset |
Ambiguously used. Can refer to the user-specified grid size (presets), i.e. the list of grid sizes available for the user to choose in the overlay. Alternatively, this refers to the user-defined presets that auto-move & auto-resize the focused window according the configured preset. |
(Monitor) Inset |
A user-configurable screen margin respected by all gTile features (window placement, autogrow, autotiling, …). It causes windows to keep a fixed distance (in pixel) to the monitor edge(s). |
(Window) Spacing |
A user-configurable window spacing that causes windows to have an invisible border to them. The spacing does not(!) apply towards the screen edges. Windows with a spacing are still able to align with the screen edge, i.e., unless an inset is configured. |
dist - Contents will be distributed 1:1 whenever the extension is build
├── images - Assets that are referenced in the CSS stylesheet
├── schemas - Contains the GSettings definitions. Required for reading/writing settings
├── metadata.json - The extension metadata.json as recognized by Gnome
└── stylesheet.css - The stylesheets file as recognized by Gnome
src - The TypeScript root directory
├── core - Contains the main orchestration classes that are instantiated as singleton
├── types - Contains _only_ TypeScript types that do NOT(!) emit code
├── ui - Contains reusable UI building blocks utilized by the core classes
│ They do not contain any logic other than strictly related to rendering
├── util - Reusable helper classes or functions
├── extension.ts - The entry point for the extension invoked by Gnome shell
└── prefs.ts - The entry point for the preferences dialog
test - Tests are kept separate from src/ and are not transpiled by "npm run build"
Note that src/types/
must not contain any files that emit actual JS runtime code. Transpiled files in dist/types/
(as emited by tsc
during transpilation) are deleted by the postbuild
script in package.json
.
The code base follows the SOLID paradigm up to an extent. Although it doesn't strictly follow the paradigm it is definitely architectured with these principles in mind. Try to stick with these principles when changing the architecture, e.g., to ease adaptation for different desktop environments.
The extension makes use of the GJS mechanisms where possible. In particular, the UI components make extensive use of GObject Properties and Signals for synchronization purposes. UI components are modeled as general purpose, composable components. In particular, they do not contain any logic other than strictly related to rendering. Business logic is supposed to reside in an orchestrating class or function.
The project does intentionally avoid the use of linters such as prettier or eslint. Quick comprehension is more important than following strict code formatting rules. That being said, please try to comply with the implicit code style used throughout the code base. In particular:
.editorconfig
file - best with a plugin that automatically applies the rules.private
visibility specifier.At the time of its release, this extension was quite possibly the most TypeScript conformant extension in the Gnome ecosystem. It is in the interest of the author to honor that by utilizing the capabilities of TypeScript where possible to achieve maximum type-safety during compile time.
When developing in the Gnome ecosystem, these are the primary go-to resources to refer to.