Files

205 lines
6.6 KiB
QML

import QtQuick
import qs.Common
import qs.Widgets
Item {
id: win
property var panel: null
property string widgetId: ""
property string title: ""
property string icon: ""
property real widgetWidth: 360
property real widgetHeight: 320
property real minimumY: 0
property bool shown: false
property Component contentComponent: null
readonly property real margin: panel ? panel.edgeMargin : 12
readonly property real panelWidth: panel ? panel.width : 0
readonly property real panelHeight: panel ? panel.height : 0
readonly property string screenKey: panel ? panel.screenKey : ""
// Which monitor the current x/y were resolved for; guards against saving a
// position under a screen it was never arranged on.
property string appliedKey: ""
readonly property real maxX: Math.max(margin, (panel ? panel.width : width) - width - margin)
readonly property real maxY: Math.max(minimumY, (panel ? panel.height : height) - height - margin)
function clampX(value) {
return Math.max(margin, Math.min(maxX, value));
}
function clampY(value) {
return Math.max(minimumY, Math.min(maxY, value));
}
// Each monitor keeps its own absolute arrangement, so moving between a 4K
// and a 1440p screen restores what was arranged there rather than a scaled
// copy of the other one.
function applyStoredPosition() {
if (!panel || panel.width <= 0 || panel.height <= 0)
return;
const stored = panel.widgetPosition(win.widgetId);
win.x = win.clampX(stored.x);
win.y = win.clampY(stored.y);
win.appliedKey = win.screenKey;
}
function raise() {
if (panel)
win.z = panel.nextWindowZ();
}
function storePosition() {
if (!panel || panel.width <= 0 || panel.height <= 0)
return;
// Mid-transition to another monitor: the position on screen does not
// belong to the new screen yet.
if (win.screenKey === "" || win.screenKey !== win.appliedKey)
return;
panel.saveWidgetPosition(win.widgetId, win.x, win.y);
}
width: widgetWidth
height: widgetHeight
visible: shown && opacity > 0
opacity: shown ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
Component.onCompleted: win.applyStoredPosition()
onShownChanged: {
if (shown) {
win.applyStoredPosition();
win.raise();
}
}
// The panel has neither size nor a resolved screen while the delegate is
// created, and the two settle independently - the screen can change without
// a resize (same resolution) and vice versa, so react to both.
onPanelWidthChanged: win.applyStoredPosition()
onPanelHeightChanged: win.applyStoredPosition()
onScreenKeyChanged: win.applyStoredPosition()
Rectangle {
id: frame
anchors.fill: parent
radius: Theme.cornerRadius * 1.25
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
border.color: dragArea.pressed ? Theme.primary : Theme.outlineMedium
border.width: dragArea.pressed ? 2 : Theme.layerOutlineWidth
Rectangle {
id: titleBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 38
radius: frame.radius
color: dragArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
// Square off the bottom corners so the title bar blends into the body.
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: parent.radius
color: parent.color
}
DankIcon {
id: titleIcon
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
name: win.icon
size: Theme.iconSize - 6
color: Theme.primary
}
StyledText {
anchors.left: titleIcon.right
anchors.leftMargin: Theme.spacingS
anchors.right: closeButton.left
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
text: win.title
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
elide: Text.ElideRight
}
DankActionButton {
id: closeButton
anchors.right: parent.right
anchors.rightMargin: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
iconName: "close"
buttonSize: 26
iconSize: 15
iconColor: Theme.surfaceVariantText
onClicked: {
if (win.panel)
win.panel.toggleWidget(win.widgetId);
}
}
MouseArea {
id: dragArea
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: closeButton.left
hoverEnabled: true
cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
drag.target: win
drag.axis: Drag.XAndYAxis
drag.minimumX: win.margin
drag.maximumX: win.maxX
drag.minimumY: win.minimumY
drag.maximumY: win.maxY
drag.threshold: 0
onPressed: win.raise()
onReleased: win.storePosition()
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.AllButtons
propagateComposedEvents: true
z: -1
onPressed: mouse => {
win.raise();
mouse.accepted = false;
}
}
Loader {
anchors.top: titleBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
anchors.bottomMargin: Theme.spacingM
active: win.shown
sourceComponent: win.contentComponent
}
}
}