/** * 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"); })();