kwin-scratchpad/contents/code/main.js

171 lines
4.7 KiB
JavaScript

/**
* KWin Scratchpad — i3-like hide/show/cycle behavior.
*
* Two shortcuts:
* Meta+Shift+- : Move active window to scratchpad (hide it).
* Meta+- : Show/hide/cycle through scratchpad windows.
*
* Cycle behavior (Meta+-):
* - If a scratchpad window is visible: hide it.
* - If no scratchpad window is visible: show the next one.
* - Wraps around the list.
*
* A window leaves the scratchpad ONLY when:
* - It is closed.
* - It is explicitly moved to a desktop.
*/
(function () {
"use strict";
// --- State ---
var scratchpad = []; // ordered list of windows in the scratchpad
var visibleWindow = null; // the currently-visible scratchpad window, or null
var nextIndex = 0; // which window to show on the next "show" press
// --- Helpers ---
function indexInScratchpad(win) {
for (var i = 0; i < scratchpad.length; i++) {
if (scratchpad[i] === win) return i;
}
return -1;
}
function isScratchpadWindow(win) {
return indexInScratchpad(win) >= 0;
}
function removeScratchpadWindow(win) {
var i = indexInScratchpad(win);
if (i < 0) return false;
scratchpad.splice(i, 1);
if (win === visibleWindow) {
visibleWindow = null;
}
// Keep nextIndex in bounds
if (scratchpad.length === 0) {
nextIndex = 0;
} else if (nextIndex >= scratchpad.length) {
nextIndex = 0;
}
return true;
}
function hideWindow(win) {
win.minimized = true;
win.skipTaskbar = true;
win.skipPager = true;
win.skipSwitcher = true;
}
function showWindow(win) {
win.desktops = [workspace.currentDesktop];
win.minimized = false;
win.skipTaskbar = true;
win.skipPager = true;
win.skipSwitcher = true;
workspace.activeWindow = win;
}
function restoreWindow(win) {
win.minimized = false;
win.skipTaskbar = false;
win.skipPager = false;
win.skipSwitcher = false;
}
// --- Shortcut 1: Add to scratchpad (Meta+Shift+-) ---
function scratchpadAdd() {
var active = workspace.activeWindow;
if (!active) return;
if (active.desktopWindow || active.dock || active.specialWindow) return;
if (isScratchpadWindow(active)) return;
scratchpad.push(active);
hideWindow(active);
print("Scratchpad: added — " + active.caption + " (" + scratchpad.length + " total)");
}
// --- Shortcut 2: Show/hide/cycle (Meta+-) ---
function scratchpadCycle() {
if (scratchpad.length === 0) return;
// If a scratchpad window is currently visible — hide it. Full stop.
if (visibleWindow !== null) {
hideWindow(visibleWindow);
// Advance nextIndex past the one we just hid
var idx = indexInScratchpad(visibleWindow);
if (idx >= 0) {
nextIndex = (idx + 1) % scratchpad.length;
}
visibleWindow = null;
return;
}
// No scratchpad window is visible — show the next one.
if (nextIndex >= scratchpad.length) {
nextIndex = 0;
}
visibleWindow = scratchpad[nextIndex];
showWindow(visibleWindow);
}
// --- Track window closure ---
workspace.windowRemoved.connect(function (win) {
removeScratchpadWindow(win);
});
// --- Track desktop changes (window explicitly moved to a desktop) ---
function watchDesktopChange(win) {
win.desktopsChanged.connect(function () {
if (!isScratchpadWindow(win)) return;
// If it's the visible one and not minimized, this is likely our
// own showWindow() call — ignore.
if (win === visibleWindow && !win.minimized) {
return;
}
// User moved it to a desktop — release from scratchpad.
removeScratchpadWindow(win);
restoreWindow(win);
print("Scratchpad: window released to desktop — " + win.caption);
});
}
var clients = workspace.stackingOrder;
for (var i = 0; i < clients.length; i++) {
watchDesktopChange(clients[i]);
}
workspace.windowAdded.connect(function (win) {
watchDesktopChange(win);
});
// --- Register shortcuts ---
registerShortcut(
"ScratchpadAdd",
"Move Window to Scratchpad",
"Meta+Shift+-",
scratchpadAdd
);
registerShortcut(
"ScratchpadCycle",
"Scratchpad Show/Hide/Cycle",
"Meta+-",
scratchpadCycle
);
print("KWin Scratchpad loaded");
})();