First commit

This commit is contained in:
Fmstrat
2019-03-26 14:35:30 -04:00
commit 4c0ecfcdf7
5 changed files with 371 additions and 0 deletions

19
README.md Normal file
View 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
```

BIN
demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

320
extension.js Normal file
View 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
View 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
View 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