mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-20 18:24:02 -04:00
238 lines
8.4 KiB
QML
238 lines
8.4 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import qs.Common
|
|
import qs.Services
|
|
|
|
PanelWindow {
|
|
id: root
|
|
|
|
required property string barId
|
|
|
|
readonly property bool free: true
|
|
readonly property bool dot: body.dotMode
|
|
readonly property var barConfig: SettingsData.getBarConfig(root.barId)
|
|
readonly property var islandController: body.islandController
|
|
readonly property int launcherResultCount: body.launcherResultCount
|
|
readonly property int hostOriginX: body.hostOriginX
|
|
readonly property int hostOriginY: body.hostOriginY
|
|
readonly property real dotSize: body.dotSize
|
|
readonly property real compactWidth: body.islandController.compactTarget.width
|
|
readonly property real compactHeight: body.islandController.compactTarget.height
|
|
readonly property real idleScale: Math.max(0.2, Math.min(1, body.setting("islandFreeIdleScale")))
|
|
readonly property real idleOpacity: Math.max(0.05, Math.min(1, body.setting("islandFreeIdleOpacity")))
|
|
readonly property int idleDelay: root.dot ? body.setting("islandFreeIdleDelay") : 0
|
|
readonly property real screenWidth: root.screen?.width ?? 1920
|
|
readonly property real screenHeight: root.screen?.height ?? 1080
|
|
readonly property string positionKey: IslandHostRegistry.key(root.screen?.name, root.barId)
|
|
readonly property var storedAnchor: SessionData.islandFreePositions[root.positionKey] ?? null
|
|
readonly property real storedAnchorX: root.clampAnchorX((typeof root.storedAnchor?.x === "number" ? root.storedAnchor.x : 0.5) * root.screenWidth)
|
|
readonly property real storedAnchorY: root.clampAnchorY((typeof root.storedAnchor?.y === "number" ? root.storedAnchor.y : 0.5) * root.screenHeight)
|
|
readonly property int surfaceX: Math.floor(body.currentVisualX - root.hitPadding - body.shadowPadding)
|
|
readonly property int surfaceY: Math.floor(body.currentVisualY - root.hitPadding - body.shadowPadding)
|
|
|
|
readonly property bool expanded: body.expanded
|
|
readonly property bool pointerInside: body.islandController.pointerInside
|
|
readonly property string activeActivity: body.islandController.activeActivity
|
|
// An idle-shrunk nub keeps its full-size hit area; the drag area shares it or ring clicks would be lost.
|
|
readonly property real hitPadding: root.idle ? Math.max(0, (root.dotSize - root.dotSize * root.idleScale) / 2) : 0
|
|
// Blur regions are not antialiased and a small circle shows the jagged rim, so blur waits for the morph.
|
|
readonly property bool blurWanted: !root.dot || Math.min(body.currentVisualWidth, body.currentVisualHeight) > root.dotSize * 1.5
|
|
|
|
property real anchorX: 0
|
|
property real anchorY: 0
|
|
property bool dragging: false
|
|
property bool idle: false
|
|
|
|
function anchorLimit(size, extent) {
|
|
const half = size / 2;
|
|
const low = body.freeMargin + half;
|
|
const high = extent - body.freeMargin - half;
|
|
return high <= low ? [extent / 2, extent / 2] : [low, high];
|
|
}
|
|
|
|
function clampAnchorX(value) {
|
|
const range = root.anchorLimit(root.compactWidth, root.screenWidth);
|
|
return Math.max(range[0], Math.min(value, range[1]));
|
|
}
|
|
|
|
function clampAnchorY(value) {
|
|
const range = root.anchorLimit(root.compactHeight, root.screenHeight);
|
|
return Math.max(range[0], Math.min(value, range[1]));
|
|
}
|
|
|
|
function storeAnchor() {
|
|
SessionData.setIslandFreePosition(root.positionKey, root.anchorX / root.screenWidth, root.anchorY / root.screenHeight);
|
|
}
|
|
|
|
function moveTo(x, y) {
|
|
root.anchorX = root.clampAnchorX(x);
|
|
root.anchorY = root.clampAnchorY(y);
|
|
root.storeAnchor();
|
|
root.wake();
|
|
}
|
|
|
|
function wake() {
|
|
root.idle = false;
|
|
if (root.idleDelay > 0)
|
|
idleTimer.restart();
|
|
else
|
|
idleTimer.stop();
|
|
}
|
|
|
|
function requestKeyboardFocus() {
|
|
body.requestKeyboardFocus();
|
|
}
|
|
|
|
function containsGlobalPoint(gx, gy, padding) {
|
|
return body.containsGlobalPoint(gx, gy, padding);
|
|
}
|
|
|
|
onStoredAnchorXChanged: {
|
|
if (!root.dragging)
|
|
root.anchorX = root.storedAnchorX;
|
|
}
|
|
onStoredAnchorYChanged: {
|
|
if (!root.dragging)
|
|
root.anchorY = root.storedAnchorY;
|
|
}
|
|
onPointerInsideChanged: root.wake()
|
|
onExpandedChanged: root.wake()
|
|
onActiveActivityChanged: root.wake()
|
|
|
|
color: "transparent"
|
|
exclusiveZone: -1
|
|
WlrLayershell.namespace: "dms:dankisland"
|
|
WlrLayershell.layer: LayerShell.fromEnv("DMS_DANKISLAND_LAYER", body.usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top)
|
|
WlrLayershell.keyboardFocus: islandChrome.keyboardFocusPolicy
|
|
BackgroundEffect.blurRegion: root.blurWanted && BlurService.enabled && BlurService.available && body.surfaceOpacity > 0 && body.surfaceOpacity < 1 ? surfaceBlurRegion : null
|
|
|
|
Region {
|
|
id: surfaceBlurRegion
|
|
|
|
readonly property real left: body.x + body.currentVisualX
|
|
readonly property real top: body.y + body.currentVisualY
|
|
|
|
x: Math.ceil(left) + 1
|
|
y: Math.ceil(top) + 1
|
|
width: Math.max(0, Math.floor(left + body.currentVisualWidth) - 1 - x)
|
|
height: Math.max(0, Math.floor(top + body.currentVisualHeight) - 1 - y)
|
|
radius: body.currentSurfaceRadius > 0 ? Math.ceil(body.currentSurfaceRadius) + 1 : 0
|
|
}
|
|
|
|
anchors {
|
|
top: true
|
|
left: true
|
|
}
|
|
WlrLayershell.margins {
|
|
left: root.surfaceX
|
|
top: root.surfaceY
|
|
}
|
|
implicitWidth: Math.ceil(body.currentVisualX + body.currentVisualWidth + root.hitPadding + body.shadowPadding) - root.surfaceX
|
|
implicitHeight: Math.ceil(body.currentVisualY + body.currentVisualHeight + root.hitPadding + body.shadowPadding) - root.surfaceY
|
|
|
|
Component.onCompleted: {
|
|
root.wake();
|
|
KeyboardFocus.registerBarWindow(root);
|
|
}
|
|
Component.onDestruction: KeyboardFocus.unregisterBarWindow(root)
|
|
|
|
IslandHostChrome {
|
|
id: islandChrome
|
|
|
|
window: root
|
|
extraWindows: [dismissWindow]
|
|
host: body
|
|
}
|
|
|
|
Timer {
|
|
id: idleTimer
|
|
|
|
interval: Math.max(1000, root.idleDelay)
|
|
onTriggered: {
|
|
if (root.expanded || root.pointerInside || root.dragging || body.islandController.transientActive)
|
|
return;
|
|
root.idle = true;
|
|
}
|
|
}
|
|
|
|
mask: Region {
|
|
item: body.inputSuspended ? null : hitTarget
|
|
}
|
|
Item {
|
|
id: hitTarget
|
|
anchors.fill: parent
|
|
anchors.margins: body.shadowPadding
|
|
}
|
|
|
|
// Keep the animated buffer tight; the unmoving input-only window catches outside clicks.
|
|
PanelWindow {
|
|
id: dismissWindow
|
|
screen: root.screen
|
|
visible: root.expanded && !body.inputSuspended
|
|
color: "transparent"
|
|
exclusiveZone: -1
|
|
WlrLayershell.namespace: "dms:dankisland-dismiss"
|
|
WlrLayershell.layer: root.WlrLayershell.layer
|
|
anchors {
|
|
top: true
|
|
bottom: true
|
|
left: true
|
|
right: true
|
|
}
|
|
mask: Region {
|
|
width: dismissWindow.width
|
|
height: dismissWindow.height
|
|
Region {
|
|
x: root.surfaceX + hitTarget.x
|
|
y: root.surfaceY + hitTarget.y
|
|
width: hitTarget.width
|
|
height: hitTarget.height
|
|
intersection: Intersection.Subtract
|
|
}
|
|
}
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
|
onClicked: body.islandController.requestCollapse()
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
enabled: !root.expanded && !body.inputSuspended
|
|
onClicked: {
|
|
SettingsData.recordBarInteraction(root.screen, root.barId);
|
|
body.islandController.requestToggle(true);
|
|
}
|
|
}
|
|
|
|
IslandBarHost {
|
|
id: body
|
|
|
|
x: -root.surfaceX
|
|
y: -root.surfaceY
|
|
width: root.screenWidth
|
|
height: root.screenHeight
|
|
originOffsetX: x
|
|
originOffsetY: y
|
|
barConfig: root.barConfig
|
|
screen: root.screen
|
|
hostWindow: root
|
|
barId: root.barId
|
|
anchorX: root.anchorX
|
|
anchorY: root.anchorY
|
|
anchorSnaps: root.dragging
|
|
freeScale: root.idle ? root.idleScale : 1
|
|
freeOpacity: root.idle ? root.idleOpacity : 1
|
|
}
|
|
|
|
IslandFreeDragArea {
|
|
hostWindow: root
|
|
surface: body.surface
|
|
enabled: !root.expanded && !body.inputSuspended
|
|
}
|
|
}
|