initial commit

This commit is contained in:
Levi Neely 2026-07-01 09:32:26 +02:00
commit 06b84e5ba9
5 changed files with 246 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Build artifacts
*.kwinscript
# Editor files
*~
*.swp
*.swo
.vscode/
.idea/
*.kate-swp
# OS files
.DS_Store
Thumbs.db
.directory
# Node (if using build tools)
node_modules/
package-lock.json
# Temporary files
*.log
*.tmp

170
contents/code/main.js Normal file
View File

@ -0,0 +1,170 @@
/**
* 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");
})();

13
contents/config/main.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<kcfgfile name=""/>
<group name="General">
<entry name="shortcut" type="String">
<label>Keyboard shortcut for scratchpad toggle</label>
<default>Meta+-</default>
</entry>
</group>
</kcfg>

22
install.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
# Install kwin-scratchpad KWin script
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PACKAGE_DIR="$SCRIPT_DIR/"
echo "Installing kwin-scratchpad..."
# Remove old version if present
kpackagetool6 --type=KWin/Script -r kwin-scratchpad 2>/dev/null || true
# Install
kpackagetool6 --type=KWin/Script -i "$PACKAGE_DIR"
# Enable the script
kwriteconfig6 --file kwinrc --group Plugins --key kwin-scratchpadEnabled true
# Reload KWin
qdbus6 org.kde.KWin /KWin reconfigure
echo "Done. Shortcut: Meta+- (configurable in System Settings > Shortcuts)"

18
metadata.json Normal file
View File

@ -0,0 +1,18 @@
{
"KPlugin": {
"Name": "Scratchpad",
"Description": "i3-like scratchpad: hide the active window, then cycle through hidden scratchpad windows on subsequent presses",
"Icon": "preferences-system-windows",
"Authors": [
{
"Name": "lkn"
}
],
"Id": "kwin-scratchpad",
"Version": "1.0",
"License": "GPLv3"
},
"X-Plasma-API": "javascript",
"X-Plasma-MainScript": "code/main.js",
"KPackageStructure": "KWin/Script"
}