mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 04:04:09 +02:00
A float button in every window's title bar
Frametop's own window decoration (decoration/), a QML decoration for KWin's Aurorae engine drawn like Breeze, puts a float button left of Close. It's the Keep Below button with its own glyph: the KWin script floats a window when keep-below is set and docks it when it's cleared, and keeps the flag set on every floating window, so the button shows "back to the desktop" there. The session script installs the decoration for the Frametop desktop only; decoration/apply.sh switches a running desktop to it or back to Breeze. Not yet tried in a running KWin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
86c89f90af
commit
f32187824a
9 files changed
+498
-5
No files matched your search
+35
-4
@@ -12,6 +12,7 @@ const SERVICE = "org.frametop.Float", PATH = "/Float", IFACE = "org.frametop.Flo
|
||||
let screens = 0; // outputs WL-0 .. WL-<screens - 1> are screens; the rest are spares
|
||||
let polling = false;
|
||||
const watched = {}; // id -> true once its signals are connected
|
||||
let marking = false; // the script itself is setting keep-below (see mark)
|
||||
|
||||
function send(ev) {
|
||||
callDBus(SERVICE, PATH, IFACE, "Event", JSON.stringify(ev));
|
||||
@@ -64,7 +65,8 @@ function watch(w) {
|
||||
watched[id] = true;
|
||||
const onSpare = () => isSpare(w.output);
|
||||
w.frameGeometryChanged.connect(() => { if (onSpare()) report("geometry", w); });
|
||||
w.outputChanged.connect(() => report("output", w));
|
||||
w.outputChanged.connect(() => { report("output", w); mark(w); });
|
||||
w.keepBelowChanged.connect(() => keepBelowChanged(w));
|
||||
w.interactiveMoveResizeStarted.connect(() => {
|
||||
if (onSpare()) send({ev: "move-start", id: id, move: w.move, resize: w.resize, frame: rect(w.frameGeometry)});
|
||||
});
|
||||
@@ -90,6 +92,33 @@ workspace.windowActivated.connect(w => {
|
||||
});
|
||||
workspace.windowList().forEach(watch);
|
||||
|
||||
// Keep-below means "floating" in the Frametop desktop. The title bar's float button (Frametop's
|
||||
// window decoration, decoration/) is the Keep Below button, so setting the flag on a window on
|
||||
// the screens floats it, and clearing it on a floating one docks it. The script keeps the flag
|
||||
// set on every floating window, its dialogs included, and cleared everywhere else, however the
|
||||
// window got there. Kept below, a window alone on its own output only has the wallpaper under it.
|
||||
function marked(w) {
|
||||
return w.managed && !w.deleted && !w.specialWindow && !w.popupWindow;
|
||||
}
|
||||
function topOf(w) {
|
||||
let top = w;
|
||||
for (let n = 0; top.transientFor && n < 10; ++n) top = top.transientFor;
|
||||
return top;
|
||||
}
|
||||
function mark(w) {
|
||||
if (screens === 0 || !marked(w)) return;
|
||||
const want = isSpare(w.output);
|
||||
if (w.keepBelow === want) return;
|
||||
marking = true;
|
||||
w.keepBelow = want;
|
||||
marking = false;
|
||||
}
|
||||
function keepBelowChanged(w) {
|
||||
if (marking || screens === 0 || !marked(w)) return;
|
||||
const top = topOf(w);
|
||||
if (w.keepBelow !== isSpare(top.output)) requestFloat(top);
|
||||
}
|
||||
|
||||
function requestFloat(w) {
|
||||
if (!w || !w.normalWindow || w.popupWindow) return;
|
||||
report(isSpare(w.output) ? "dock-request" : "float-request", w);
|
||||
@@ -118,8 +147,7 @@ function underPointer() {
|
||||
if (w.deleted || w.minimized || w.hidden || !w.managed) continue;
|
||||
const g = w.frameGeometry;
|
||||
if (p.x < g.x || p.y < g.y || p.x >= g.x + g.width || p.y >= g.y + g.height) continue;
|
||||
let top = w;
|
||||
for (let n = 0; top.transientFor && n < 10; ++n) top = top.transientFor;
|
||||
const top = topOf(w);
|
||||
return top.normalWindow && !top.popupWindow ? top : null;
|
||||
}
|
||||
return null;
|
||||
@@ -130,7 +158,10 @@ function run(c) {
|
||||
switch (c.cmd) {
|
||||
case "config":
|
||||
screens = c.screens;
|
||||
workspace.windowList().forEach(w => report("window", w));
|
||||
workspace.windowList().forEach(w => { report("window", w); mark(w); });
|
||||
break;
|
||||
case "mark": // after a float that didn't happen: keep-below back as it was
|
||||
if (w) mark(w);
|
||||
break;
|
||||
case "place": { // onto an output, at a frame rectangle (logical, global)
|
||||
if (!w) break;
|
||||
|
||||
@@ -355,6 +355,7 @@ class Daemon:
|
||||
return
|
||||
slot = self.free_slot()
|
||||
if slot is None:
|
||||
self.command(cmd="mark", id=wid) # the title bar button set keep-below for nothing
|
||||
self.notify(f"All {len(self.slots)} floating windows are in use. Put one back on the desktop "
|
||||
"to float another.")
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user