Files

219 lines
7.0 KiB
QML

import QtQuick
import qs.Common
import qs.Modules.ControlCenter
import qs.DCommon.Widgets
import qs.Widgets
import "../../../DCommon/Common/FocusNavigation.js" as FocusNavigation
Item {
id: root
property string iconName: ""
property string title: ""
property string subtitle: ""
property string statusText: ""
property color statusColor: Theme.surfaceVariantText
property bool shown: false
property real panelWidth: CcMetrics.dialogWidth
property vector4d cornerRadii: Qt.vector4d(Theme.windowRadius, Theme.windowRadius, Theme.windowRadius, Theme.windowRadius)
property Item backdrop: null
readonly property real presence: panel.opacity
default property alias content: contentSlot.data
property alias showScrollBar: contentFlickable.showScrollBar
signal dismissed
anchors.fill: parent
visible: shown || panel.opacity > 0
z: CcMetrics.overlayZ
function present() {
contentFlickable.contentY = 0;
shown = true;
focusScope.forceActiveFocus();
}
function dismiss() {
shown = false;
}
MouseArea {
anchors.fill: parent
enabled: root.shown
hoverEnabled: true
preventStealing: true
acceptedButtons: Qt.AllButtons
onClicked: root.dismiss()
onWheel: wheel => wheel.accepted = true
}
Rectangle {
anchors.fill: parent
topLeftRadius: root.cornerRadii.x
topRightRadius: root.cornerRadii.y
bottomRightRadius: root.cornerRadii.z
bottomLeftRadius: root.cornerRadii.w
color: Theme.withAlpha(Theme.scrimColor, Theme.scrimAlpha)
opacity: root.shown ? 1 : 0
Behavior on opacity {
enabled: CcMetrics.animationsEnabled
NumberAnimation {
duration: Theme.expressiveDurations.expressiveEffects
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
}
}
}
FocusScope {
id: focusScope
anchors.fill: parent
focus: root.shown
enabled: root.shown
Keys.onEscapePressed: event => {
root.dismiss();
event.accepted = true;
}
}
Rectangle {
id: panel
parent: focusScope
anchors.centerIn: parent
width: Math.min(root.panelWidth, root.width - Theme.spacingL * 2)
height: column.implicitHeight + Theme.spacingL * 2
radius: Theme.windowRadius
color: CcMetrics.dialogColor
border.width: Theme.layerOutlineWidth
border.color: Theme.outlineMedium
opacity: root.shown ? 1 : 0
scale: root.shown ? 1 : CcMetrics.popupEnterScale
onOpacityChanged: {
if (opacity > 0 || root.shown)
return;
root.dismissed();
}
Behavior on opacity {
enabled: CcMetrics.animationsEnabled
NumberAnimation {
duration: Theme.expressiveDurations.expressiveEffects
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
}
}
Behavior on scale {
enabled: CcMetrics.animationsEnabled
NumberAnimation {
duration: Theme.expressiveDurations.expressiveEffects
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
}
}
// In-window sheets get no compositor blur, so blur the covered content behind the panel only.
Loader {
anchors.fill: parent
z: -1
active: root.backdrop !== null && root.visible && CcMetrics.hideCoveredContent
sourceComponent: BackdropBlur {
radius: panel.radius
sourceItem: root.backdrop
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
preventStealing: true
acceptedButtons: Qt.AllButtons
onClicked: mouse => mouse.accepted = true
onWheel: wheel => wheel.accepted = true
}
Column {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
id: header
width: parent.width
spacing: Theme.spacingM
DIcon {
name: root.iconName
size: Theme.iconSizeLarge
color: Theme.primary
visible: name !== ""
anchors.verticalCenter: parent.verticalCenter
}
Column {
width: parent.width - (root.iconName !== "" ? Theme.iconSizeLarge + parent.spacing : 0)
spacing: Theme.spacingXXS
anchors.verticalCenter: parent.verticalCenter
StyledText {
width: parent.width
text: root.title
font.pixelSize: Theme.fontSizeLarge
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
elide: Text.ElideRight
}
StyledText {
width: parent.width
text: root.subtitle
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideRight
visible: text !== ""
}
}
}
StyledText {
id: status
width: parent.width
text: root.statusText
font.pixelSize: Theme.fontSizeSmall
color: root.statusColor
wrapMode: Text.WordWrap
visible: text !== ""
}
DFlickable {
id: contentFlickable
width: parent.width
height: Math.min(contentHeight, Math.max(0, root.height - Theme.spacingL * 4 - header.height - column.spacing - (status.visible ? status.height + column.spacing : 0)))
contentHeight: contentSlot.implicitHeight
clip: true
readonly property Item windowFocusItem: Window.activeFocusItem
onWindowFocusItemChanged: {
const item = windowFocusItem;
if (!root.shown || !item || !FocusNavigation.containsFocus(contentSlot))
return;
const top = item.mapToItem(contentSlot, 0, 0).y;
contentFlickable.revealRange(top, top + item.height);
}
Column {
id: contentSlot
width: parent.width
spacing: CcMetrics.detailContentGap
}
}
}
}
}