import Quickshell import QtQuick import qs.config import qs.components import qs.services // Left click focuses the window, or minimises it if it is already focused — // except under attwm, which has no minimised state. Middle click closes it. PanelButton { id: root // The window this button stands for: either an attwm window object from its // IPC state, or a Toplevel handle from wlr-foreign-toplevel-management. // Both carry the same facts under different names, so `attwm` says which is // in hand rather than each read having to guess. required property var win required property bool attwm readonly property string appId: (root.attwm ? root.win.app_id : root.win.appId) || "" readonly property bool activated: root.attwm ? root.win.focused : root.win.activated // Not on screen right now: minimised elsewhere, and under attwm parked on a // tag nobody is viewing, which is that model's equivalent. readonly property bool hidden: root.attwm ? !root.win.visible : root.win.minimized // Desktop entries carry a proper themed icon; appId alone often works as an // icon name too, which is the fallback AppIcon ends up using. readonly property var entry: DesktopEntries.heuristicLookup(root.appId) readonly property string title: root.win.title || root.appId || "Window" active: root.activated minContentWidth: Config.windowButtonMinWidth acceptedButtons: Qt.LeftButton | Qt.MiddleButton opacity: root.hidden ? 0.55 : 1 Behavior on opacity { NumberAnimation { duration: Theme.animDuration easing.type: Theme.animEasing } } onClicked: event => { if (event.button === Qt.MiddleButton) { if (root.attwm) Attwm.closeWindow(root.win.id); else root.win.close(); return; } if (root.attwm) { // attwm has nothing to minimise into — a window is on a tag or it // is not — so a click always means focus, and attwm brings the // window's tags into view if they are not already. Attwm.focusWindow(root.win.id); return; } if (root.win.activated) { root.win.minimized = true; } else { root.win.minimized = false; root.win.activate(); } } // Both children are given the same height so the Row needs no anchors, // which positioners disallow. Row { spacing: Theme.spacing + 2 AppIcon { icon: (root.entry && root.entry.icon) || root.appId fallbackLabel: root.appId || root.title size: Theme.iconSize } Text { id: label // Clamp to the natural width so the button never depends on its own // width, which would loop through PanelButton's implicit sizing. width: Math.min(implicitWidth, Config.windowButtonMaxWidth) height: Theme.iconSize verticalAlignment: Text.AlignVCenter elide: Text.ElideRight text: root.title color: root.activated ? Theme.text : Theme.textDim font.family: Theme.fontFamily font.pixelSize: Theme.fontSize } } // Active-window underline, the usual taskbar affordance. overlay: Rectangle { anchors { left: parent.left right: parent.right bottom: parent.bottom leftMargin: Theme.radius rightMargin: Theme.radius } height: 2 radius: 1 color: Theme.accent visible: root.activated } }