mirror of
https://github.com/FranP-code/wintile.git
synced 2025-10-13 00:33:46 +00:00
First commit
This commit is contained in:
19
README.md
Normal file
19
README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
WinTile: Windows 10 window tiling for GNOME
|
||||
===========================================
|
||||
WinTile is a hotkey driven window tiling system for GNOME that imitates the standard `Win-Arrow` keys of Windows 10, allowing you to maximize, maximize to sides, or 1/4 sized to corner a window using just `<Super>`+`<Arrows>`.
|
||||
|
||||
<img src='demo.gif'>
|
||||
|
||||
Selecting the Super key
|
||||
-----------------------
|
||||
By default, this extension uses `<Super><Control><Shift>`+`<Arrows>` to move windows. This is because `<Super>`+`<Arrows>` is reserved by GNOME in the keyboard shortcut settings. The below script will toggle these default key bindings so you can use `<Super>`+`<Arrows>` for this extension.
|
||||
|
||||
To use `<Super>`+`<Arrows>` for this extension:
|
||||
```
|
||||
$ bash ~/.local/share/gnome-shell/extensions/windows.window.tile@nowsci.com/setHotKey.sh Super
|
||||
```
|
||||
|
||||
To reset `<Super>`+`<Arrows>` to default for GNOME:
|
||||
```
|
||||
$ bash ~/.local/share/gnome-shell/extensions/windows.window.tile@nowsci.com/setHotKey.sh ControlShiftSuper
|
||||
```
|
||||
320
extension.js
Normal file
320
extension.js
Normal file
@@ -0,0 +1,320 @@
|
||||
const Lang = imports.lang
|
||||
const Meta = imports.gi.Meta
|
||||
const Shell = imports.gi.Shell
|
||||
const Main = imports.ui.main
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
let _close = 50;
|
||||
var debug = false;
|
||||
|
||||
var _log = function(){}
|
||||
if (debug)
|
||||
_log = log.bind(window.console);
|
||||
|
||||
|
||||
const KeyManager = new Lang.Class({
|
||||
Name: 'MyKeyManager',
|
||||
|
||||
_init: function() {
|
||||
this.grabbers = new Map()
|
||||
|
||||
global.display.connect(
|
||||
'accelerator-activated',
|
||||
Lang.bind(this, function(display, action, deviceId, timestamp){
|
||||
_log('Accelerator Activated: [display={}, action={}, deviceId={}, timestamp={}]',
|
||||
display, action, deviceId, timestamp)
|
||||
this._onAccelerator(action)
|
||||
}))
|
||||
},
|
||||
|
||||
listenFor: function(accelerator, callback){
|
||||
_log('Trying to listen for hot key [accelerator={}]', accelerator)
|
||||
let action = global.display.grab_accelerator(accelerator)
|
||||
|
||||
if(action == Meta.KeyBindingAction.NONE) {
|
||||
_log('Unable to grab accelerator [binding={}]', accelerator)
|
||||
} else {
|
||||
_log('Grabbed accelerator [action={}]', action)
|
||||
let name = Meta.external_binding_name_for_action(action)
|
||||
_log('Received binding name for action [name={}, action={}]',
|
||||
name, action)
|
||||
|
||||
_log('Requesting WM to allow binding [name={}]', name)
|
||||
Main.wm.allowKeybinding(name, Shell.ActionMode.ALL)
|
||||
|
||||
this.grabbers.set(action, {
|
||||
name: name,
|
||||
accelerator: accelerator,
|
||||
callback: callback,
|
||||
action: action
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_onAccelerator: function(action) {
|
||||
let grabber = this.grabbers.get(action)
|
||||
|
||||
if(grabber) {
|
||||
this.grabbers.get(action).callback()
|
||||
} else {
|
||||
_log('No listeners [action={}]', action)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function isClose(a, b) {
|
||||
if (a <= b && a > b - _close)
|
||||
return true;
|
||||
else if (a >= b && a < b + _close)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
function getPosition(app, space) {
|
||||
let window = app.get_frame_rect()
|
||||
_log("window.x: "+window.x+" window.y: "+window.y+" window.width: "+window.width+" window.height: "+window.height)
|
||||
_log("space.x: "+space.x+" space.y: "+space.y+" space.width: "+space.width+" space.height: "+space.height+" space.width/2: "+Math.floor(space.width/2)+" space.height/2: "+Math.floor(space.height/2))
|
||||
if (isClose(window.x, space.x) && isClose(window.y, space.y)) {
|
||||
// X,Y in upper left
|
||||
if (isClose(window.height, space.height) && isClose(window.width, space.width)) {
|
||||
// Maximized
|
||||
return "maximized"
|
||||
} else if (isClose(window.height, space.height) && isClose(window.width, space.width/2)) {
|
||||
// Left
|
||||
return "left"
|
||||
} else if (isClose(window.height, space.height/2) && isClose(window.width, space.width)) {
|
||||
// Top
|
||||
return "top"
|
||||
} else if (isClose(window.height, space.height/2) && isClose(window.width, space.width/2)) {
|
||||
// Top-left
|
||||
return "topleft"
|
||||
}
|
||||
} else if (isClose(window.x, space.width/2+space.x) && isClose(window.y, space.y)) {
|
||||
// X, Y in middle upper
|
||||
if (isClose(window.height, space.height) && isClose(window.width, space.width/2)) {
|
||||
// Right
|
||||
return "right"
|
||||
} else if (isClose(window.height, space.height/2) && isClose(window.width, space.width/2)) {
|
||||
// Top-right
|
||||
return "topright"
|
||||
}
|
||||
} else if (isClose(window.x, space.x) && isClose(window.y, space.height/2+space.y)) {
|
||||
// X, Y in middle left
|
||||
if (isClose(window.height, space.height/2) && isClose(window.width, space.width)) {
|
||||
// Bottom
|
||||
return "bottom"
|
||||
} else if (isClose(window.height, space.height/2) && isClose(window.width, space.width/2)) {
|
||||
// Bottom-left
|
||||
return "bottomleft"
|
||||
}
|
||||
} else if (isClose(window.x, space.width/2+space.x) && isClose(window.y, space.height/2+space.y)) {
|
||||
// X, Y in middle
|
||||
if (isClose(window.height, space.height/2) && isClose(window.width, space.width/2)) {
|
||||
// Bottom-right
|
||||
return "bottomright"
|
||||
}
|
||||
}
|
||||
// Floating
|
||||
return "floating"
|
||||
}
|
||||
|
||||
function placeWindow(loc, app) {
|
||||
_log("placeWindow: " + loc);
|
||||
let x, y, w, h = 0
|
||||
var space = app.get_work_area_current_monitor()
|
||||
switch (loc) {
|
||||
case "left":
|
||||
x = space.x;
|
||||
y = space.y;
|
||||
w = Math.floor(space.width/2);
|
||||
h = space.height;
|
||||
if (!app.maximizedVertically)
|
||||
app.maximize(Meta.MaximizeFlags.VERTICAL)
|
||||
if (app.maximized_horizontally)
|
||||
app.unmaximize(Meta.MaximizeFlags.HORIZONTAL);
|
||||
app.move_resize_frame(true, x, y, w, h)
|
||||
break;
|
||||
case "topleft":
|
||||
x = space.x;
|
||||
y = space.y;
|
||||
w = Math.floor(space.width/2);
|
||||
h = Math.floor(space.height/2);
|
||||
if (app.maximized_horizontally || app.maximizedVertically)
|
||||
app.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||||
app.move_resize_frame(true, x, y, w, h)
|
||||
break;
|
||||
case "bottomleft":
|
||||
x = space.x;
|
||||
y = Math.floor(space.height/2)+space.y;
|
||||
w = Math.floor(space.width/2);
|
||||
h = Math.floor(space.height/2);
|
||||
if (app.maximized_horizontally || app.maximizedVertically)
|
||||
app.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||||
app.move_resize_frame(true, x, y, w, h)
|
||||
break;
|
||||
case "right":
|
||||
x = Math.floor(space.width/2+space.x);
|
||||
y = space.y;
|
||||
w = Math.floor(space.width/2);
|
||||
h = space.height;
|
||||
if (!app.maximizedVertically)
|
||||
app.maximize(Meta.MaximizeFlags.VERTICAL)
|
||||
if (app.maximized_horizontally)
|
||||
app.unmaximize(Meta.MaximizeFlags.HORIZONTAL);
|
||||
app.move_resize_frame(true, x, y, w, h)
|
||||
break;
|
||||
case "topright":
|
||||
x = Math.floor(space.width/2+space.x);
|
||||
y = space.y;
|
||||
w = Math.floor(space.width/2);
|
||||
h = Math.floor(space.height/2);
|
||||
if (app.maximized_horizontally || app.maximizedVertically)
|
||||
app.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||||
app.move_resize_frame(true, x, y, w, h)
|
||||
break;
|
||||
case "bottomright":
|
||||
x = Math.floor(space.width/2+space.x);
|
||||
y = Math.floor(space.height/2)+space.y;
|
||||
w = Math.floor(space.width/2);
|
||||
h = Math.floor(space.height/2);
|
||||
if (app.maximized_horizontally || app.maximizedVertically)
|
||||
app.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||||
app.move_resize_frame(true, x, y, w, h)
|
||||
break;
|
||||
case "maximize":
|
||||
if (!app.maximized_horizontally || !app.maximizedVertically)
|
||||
app.maximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||||
break;
|
||||
case "floating":
|
||||
if (app.maximized_horizontally || app.maximizedVertically)
|
||||
app.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||||
break;
|
||||
}
|
||||
let window = app.get_frame_rect()
|
||||
_log("window.x: "+window.x+" window.y: "+window.y+" window.width: "+window.width+" window.height: "+window.height)
|
||||
}
|
||||
|
||||
function getMonitorArray() {
|
||||
var monitors = [];
|
||||
for (var i = 0; i < Main.layoutManager.monitors.length; i++) {
|
||||
monitors.push({ "index": i, "x": Main.layoutManager.monitors[i].x });
|
||||
}
|
||||
monitors.sort(function(a, b) {
|
||||
return a.x - b.x;
|
||||
});
|
||||
for (var i = 0; i < monitors.length; i++) {
|
||||
_log(JSON.stringify(monitors[i]));
|
||||
}
|
||||
//var monitors = Main.layoutManager.monitors;
|
||||
//monitors.sort(function(a, b) {
|
||||
//});
|
||||
//let monWidth = Main.layoutManager.monitors[mon].width;
|
||||
//let monHeight = Main.layoutManager.monitors[mon].height;
|
||||
//_log("mon: " + mon);
|
||||
}
|
||||
|
||||
function moveWindow(direction) {
|
||||
_log("moveWindow: " + direction);
|
||||
var app = global.display.focus_window;
|
||||
var space = app.get_work_area_current_monitor()
|
||||
let pos = getPosition(app, space);
|
||||
_log("pos: " + pos);
|
||||
//var monitors = getMonitorArray();
|
||||
var curMonitor = app.get_monitor();
|
||||
let monitorToLeft = -1;
|
||||
let monitorToRight = -1;
|
||||
for (var i = 0; i < Main.layoutManager.monitors.length; i++) {
|
||||
if (Main.layoutManager.monitors[i].x < Main.layoutManager.monitors[curMonitor].x && (monitorToLeft == -1 || (monitorToLeft >= 0 && Main.layoutManager.monitors[i].x > Main.layoutManager.monitors[monitorToLeft].x)))
|
||||
monitorToLeft = i;
|
||||
if (Main.layoutManager.monitors[i].x > Main.layoutManager.monitors[curMonitor].x && (monitorToRight == -1 || (monitorToRight >= 0 && Main.layoutManager.monitors[i].x < Main.layoutManager.monitors[monitorToRight].x)))
|
||||
monitorToRight = i;
|
||||
}
|
||||
_log("monitorToLeft: " + monitorToLeft);
|
||||
_log("monitorToRight " + monitorToRight);
|
||||
|
||||
switch (direction) {
|
||||
case "left":
|
||||
if (pos == "left" && monitorToLeft != -1) {
|
||||
app.move_to_monitor(monitorToLeft);
|
||||
placeWindow("right", app);
|
||||
} else if (pos == "topleft" && monitorToLeft != -1) {
|
||||
app.move_to_monitor(monitorToLeft);
|
||||
placeWindow("topright", app);
|
||||
} else if (pos == "bottomleft" && monitorToLeft != -1) {
|
||||
app.move_to_monitor(monitorToLeft);
|
||||
placeWindow("bottomright", app);
|
||||
} else if (pos == "topright") {
|
||||
placeWindow("topleft", app);
|
||||
} else if (pos == "bottomright") {
|
||||
placeWindow("bottomleft", app);
|
||||
} else {
|
||||
placeWindow("left", app);
|
||||
}
|
||||
break;
|
||||
case "right":
|
||||
if (pos == "right" && monitorToRight != -1) {
|
||||
app.move_to_monitor(monitorToRight);
|
||||
placeWindow("left", app);
|
||||
} else if (pos == "topright" && monitorToRight != -1) {
|
||||
app.move_to_monitor(monitorToRight);
|
||||
placeWindow("topleft", app);
|
||||
} else if (pos == "bottomright" && monitorToRight != -1) {
|
||||
app.move_to_monitor(monitorToRight);
|
||||
placeWindow("bottomleft", app);
|
||||
} else if (pos == "topleft") {
|
||||
placeWindow("topright", app);
|
||||
} else if (pos == "bottomleft") {
|
||||
placeWindow("bottomright", app);
|
||||
} else {
|
||||
placeWindow("right", app);
|
||||
}
|
||||
break;
|
||||
case "up":
|
||||
if (pos == "left")
|
||||
placeWindow("topleft", app);
|
||||
else if (pos == "bottomleft")
|
||||
placeWindow("left", app);
|
||||
else if (pos == "right")
|
||||
placeWindow("topright", app);
|
||||
else if (pos == "bottomright")
|
||||
placeWindow("right", app);
|
||||
else
|
||||
placeWindow("maximize", app);
|
||||
break;
|
||||
case "down":
|
||||
if (pos == "left")
|
||||
placeWindow("bottomleft", app);
|
||||
else if (pos == "topleft")
|
||||
placeWindow("left", app);
|
||||
else if (pos == "right")
|
||||
placeWindow("bottomright", app);
|
||||
else if (pos == "topright")
|
||||
placeWindow("right", app);
|
||||
else if (pos == "maximized")
|
||||
placeWindow("floating", app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function requestMove(direction) {
|
||||
Mainloop.timeout_add(10, function () {
|
||||
moveWindow(direction);
|
||||
});
|
||||
}
|
||||
|
||||
var enable = function() {
|
||||
let modifier = "<ctrl><super><shift>";
|
||||
let modifier2 = "<super>";
|
||||
let keyManager = new KeyManager()
|
||||
keyManager.listenFor(modifier+"left", function() { requestMove("left") })
|
||||
keyManager.listenFor(modifier+"right", function() { requestMove("right") })
|
||||
keyManager.listenFor(modifier+"up", function() { requestMove("up") })
|
||||
keyManager.listenFor(modifier+"down", function() { requestMove("down") })
|
||||
keyManager.listenFor(modifier2+"left", function() { requestMove("left") })
|
||||
keyManager.listenFor(modifier2+"right", function() { requestMove("right") })
|
||||
keyManager.listenFor(modifier2+"up", function() { requestMove("up") })
|
||||
keyManager.listenFor(modifier2+"down", function() { requestMove("down") })
|
||||
}
|
||||
|
||||
7
metadata.json
Normal file
7
metadata.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "WinTile: Windows 10 window tiling for GNOME",
|
||||
"description": "Window tiling based on Windows 10 hotkey patterns.",
|
||||
"uuid": "wintile@nowsci.com",
|
||||
"url": "https://github.com/fmstrat/wintile",
|
||||
"shell-version": ["3.28"]
|
||||
}
|
||||
25
setHotKey.sh
Executable file
25
setHotKey.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: setHotKey.sh <Super|ControlShiftSuper>"
|
||||
fi
|
||||
|
||||
if [ "$1" = "ControlShiftSuper" ]; then
|
||||
gsettings set org.gnome.desktop.wm.keybindings unmaximize "['<Super>Down', '<Alt>F5']" >/dev/null 2>&1
|
||||
gsettings set org.gnome.desktop.wm.keybindings maximize "['<Super>Up']" >/dev/null 2>&1
|
||||
gsettings set org.cinnamon.desktop.keybindings.wm push-tile-left "['<Super>Left']" >/dev/null 2>&1
|
||||
gsettings set org.gnome.mutter.keybindings toggle-tiled-left "['<Super>Left']" >/dev/null 2>&1
|
||||
gsettings set org.cinnamon.desktop.keybindings.wm push-tile-right "['<Super>Right']" >/dev/null 2>&1
|
||||
gsettings set org.gnome.mutter.keybindings toggle-tiled-right "['<Super>Right']" >/dev/null 2>&1
|
||||
echo -e "\nNow run Alt-F2 then R to restart GNOME.\n"
|
||||
fi
|
||||
|
||||
if [ "$1" = "Super" ]; then
|
||||
gsettings set org.gnome.desktop.wm.keybindings unmaximize "['<Control><Shift><Super>Down', '<Alt>F5']" >/dev/null 2>&1
|
||||
gsettings set org.gnome.desktop.wm.keybindings maximize "['<Control><Shift><Super>Up']" >/dev/null 2>&1
|
||||
gsettings set org.cinnamon.desktop.keybindings.wm push-tile-left "['<Control><Shift><Super>Left']" >/dev/null 2>&1
|
||||
gsettings set org.gnome.mutter.keybindings toggle-tiled-left "['<Control><Shift><Super>Left']" >/dev/null 2>&1
|
||||
gsettings set org.cinnamon.desktop.keybindings.wm push-tile-right "['<Control><Shift><Super>Right']" >/dev/null 2>&1
|
||||
gsettings set org.gnome.mutter.keybindings toggle-tiled-right "['<Control><Shift><Super>Right']" >/dev/null 2>&1
|
||||
echo -e "\nNow run Alt-F2 then R to restart GNOME.\n"
|
||||
fi
|
||||
Reference in New Issue
Block a user