Watch
1
0
Fork
You've already forked souveraine
0
souveraine/surfaces/quickshell/modules/ii/dock/DockApps.qml
Fimeg 8f42fc953d publish: the public projection begins here
This is a projection, not a development branch. The tree above was constructed
from the internal source named below under a manifest that decides which paths
may leave, then scanned as a whole tree rather than as a series of patches, and
only then published.

Public history starts here because the history before it was not admissible,
and neither was the tree. What used to stand in this repository included a
rescue copy of another machine, a directory of phone handoffs, deployment
wired to one house, and a submodule pointing at a forge no stranger can reach.
None of that was ever the product. It stays in the private forge, which is
allowed to hold the whole working organism, and this is what was deliberately
sent out instead.

Three mechanisms produced this tree, in decreasing order of trust. A top-level
path the manifest does not name never arrives at all, which is the one that
catches directories nobody has thought of yet. Named internal files inside
admitted roots are dropped. A short, reviewed table replaces deployment
defaults that a public build must not carry -- an endpoint aimed at one LAN, a
VPN profile belonging to one phone, packaging built from one checkout path.

Everything after this commit is an ordinary publication with the same three
trailers, so a force push stops being routine and starts meaning that
something deliberate happened. The trailers bind the projection to its source
without pretending the public SHA is the private one: same lineage, different
tree, and the record says so.

Source-Sha: 8f27b1e76a8fef560a336aba18e6990713ff1047
Policy-Sha: 6b261d2f3e6e1fb19874846ba4bb1dfe15565d25b8618c1c1afba0419c101d27
Tree-Digest: 18ec3563c5e5ef9a414993a9f6734b251ff9ed3cd56eebdd6cac01e45c6e3067
2026-09-04 15:55:48 -04:00

270 lines
12 KiB
QML

pragma ComponentBehavior: Bound
import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
Item {
id: root
property real maxWindowPreviewHeight: 200
property real maxWindowPreviewWidth: 300
property real windowControlsHeight: 30
property real buttonPadding: 5
property Item lastHoveredButton: null
property bool buttonHovered: false
property bool requestDockShow: previewPopup.show
// Drag-to-combine target, shared across all dock buttons/stacks. A
// DropArea's 500ms dwell sets these; the dragged button reads them on
// release to decide the combine. dragTargetStackId non-empty means the
// drop target is an existing stack (add into it); empty means a plain
// app (make a new stack).
property string dragTargetAppId: ""
property string dragTargetStackId: ""
// Cap from Dock.qml: the widest the app list may grow before the dock
// would outgrow the screen. Past it the ListView scrolls horizontally.
property real maxWidth: -1
// Drag-to-reorder state, shared across all DockAppButton delegates.
// dragSourceIndex: model index of the button currently being dragged.
// dragInsertIndex: model index BEFORE which the insertion gap shows.
// -1 means no valid reorder target. Set by DropArea.onEntered when
// the drag hovers a pinned app; cleared on release / cancel / dwell.
property int dragSourceIndex: -1
property int dragInsertIndex: -1
Layout.fillHeight: true
// No top-only margin: it shoved the whole icon list DOWN inside the bar
// with nothing balancing it — the actual cause of icons drooping below
// the dock. fillHeight + the dockRow centering handle vertical placement.
implicitWidth: maxWidth >= 0 ? Math.min(listView.contentWidth, maxWidth) : listView.contentWidth
Behavior on implicitWidth {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
function popupCenterXForButton(button) {
if (!button || !root.QsWindow)
return 0;
return root.QsWindow.mapFromItem(button, button.width / 2, 0).x;
}
StyledListView {
id: listView
spacing: 2
orientation: ListView.Horizontal
anchors.fill: parent
clip: true
// Only flickable when the list actually overflows, so touch drags on
// the buttons (drag-to-combine) aren't stolen while everything fits.
interactive: contentWidth > width
boundsBehavior: Flickable.StopAtBounds
model: ScriptModel {
objectProp: "appId"
values: TaskbarApps.apps
}
delegate: Loader {
required property var modelData
required property int index
// Fan-out stack entries render as DockStack; everything else
// (pinned apps, running windows, separators) as DockAppButton.
sourceComponent: modelData.isStack ? stackComp : appComp
Component {
id: appComp
DockAppButton {
appToplevel: modelData
appListRoot: root
modelIndex: parent.index
topInset: Appearance.sizes.hyprlandGapsOut + root.buttonPadding
bottomInset: Appearance.sizes.hyprlandGapsOut + root.buttonPadding
}
}
Component {
id: stackComp
DockStack {
appToplevel: modelData
appListRoot: root
topInset: Appearance.sizes.hyprlandGapsOut + root.buttonPadding
bottomInset: Appearance.sizes.hyprlandGapsOut + root.buttonPadding
}
}
}
}
PopupWindow {
id: previewPopup
property var appTopLevel: root.lastHoveredButton?.appToplevel
property bool shouldShow: (popupMouseArea.containsMouse || root.buttonHovered) && appTopLevel && appTopLevel.toplevels && appTopLevel.toplevels.length > 0
property bool show: false
property real cachedCenterX: 0
Connections {
target: root
function onLastHoveredButtonChanged() {
if (root.lastHoveredButton && root.QsWindow)
previewPopup.cachedCenterX = root.popupCenterXForButton(root.lastHoveredButton);
}
function onButtonHoveredChanged() {
if (root.buttonHovered && root.lastHoveredButton && root.QsWindow)
previewPopup.cachedCenterX = root.popupCenterXForButton(root.lastHoveredButton);
updateTimer.restart();
}
}
onShouldShowChanged: {
updateTimer.restart();
}
Timer {
id: updateTimer
interval: 100
onTriggered: {
previewPopup.show = previewPopup.shouldShow;
}
}
anchor {
window: root.QsWindow.window
adjustment: PopupAdjustment.None
gravity: Edges.Top | Edges.Right
edges: Edges.Top | Edges.Left
}
visible: popupBackground.opacity > 0
color: "transparent"
implicitWidth: root.QsWindow.window?.width ?? 1
implicitHeight: popupMouseArea.implicitHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
MouseArea {
id: popupMouseArea
anchors.bottom: parent.bottom
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
hoverEnabled: true
x: previewPopup.cachedCenterX - width / 2
StyledRectangularShadow {
target: popupBackground
opacity: previewPopup.show ? 1 : 0
visible: opacity > 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
}
Rectangle {
id: popupBackground
property real padding: 5
opacity: previewPopup.show ? 1 : 0
visible: opacity > 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
clip: true
color: Appearance.m3colors.m3surfaceContainer
radius: Appearance.rounding.normal
anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.sizes.elevationMargin
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: previewRowLayout.implicitHeight + padding * 2
implicitWidth: previewRowLayout.implicitWidth + padding * 2
Behavior on implicitWidth {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Behavior on implicitHeight {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
RowLayout {
id: previewRowLayout
anchors.centerIn: parent
Repeater {
model: ScriptModel {
values: previewPopup.appTopLevel?.toplevels ?? []
}
RippleButton {
id: windowButton
Layout.fillHeight: true
required property var modelData
padding: 0
middleClickAction: () => {
windowButton.modelData?.close();
}
onClicked: {
windowButton.modelData?.activate();
}
contentItem: ColumnLayout {
implicitWidth: screencopyView.implicitWidth
implicitHeight: screencopyView.implicitHeight
ButtonGroup {
contentWidth: parent.width - anchors.margins * 2
StyledText {
Layout.margins: 5
Layout.fillWidth: true
font.pixelSize: Appearance.font.pixelSize.small
text: windowButton.modelData?.title
elide: Text.ElideRight
color: Appearance.m3colors.m3onSurface
}
GroupButton {
id: closeButton
colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
baseWidth: root.windowControlsHeight
baseHeight: root.windowControlsHeight
buttonRadius: Appearance.rounding.full
contentItem: MaterialSymbol {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: "close"
iconSize: Appearance.font.pixelSize.normal
color: Appearance.m3colors.m3onSurface
}
onClicked: {
windowButton.modelData?.close();
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: screencopyView.height
implicitWidth: screencopyView.width
ScreencopyView {
id: screencopyView
anchors.centerIn: parent
captureSource: windowButton.modelData
live: true
paintCursor: true
constraintSize: Qt.size(root.maxWindowPreviewWidth, root.maxWindowPreviewHeight)
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: screencopyView.width
height: screencopyView.height
radius: Appearance.rounding.small
}
}
}
}
}
}
}
}
}
}
}
}