aerothemeplasma/plasma/shells/org.kde.plasma.desktop/contents/views/Panel.qml
2024-08-09 03:20:25 +02:00

362 lines
16 KiB
QML

/*
SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.1
import QtQml 2.15
import org.kde.plasma.core as PlasmaCore
import org.kde.ksvg 1.0 as KSvg
import org.kde.taskmanager 0.1 as TaskManager
import org.kde.kwindowsystem 1.0
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.shell.panel 0.1 as Panel
import org.kde.plasma.plasmoid 2.0
Item {
id: root
property Item containment
property bool floatingPrefix: floatingPanelSvg.usedPrefix === "floating"
readonly property bool verticalPanel: containment?.plasmoid?.formFactor === PlasmaCore.Types.Vertical
readonly property real spacingAtMinSize: Math.round(Math.max(1, (verticalPanel ? root.width : root.height) - Kirigami.Units.iconSizes.smallMedium)/2)
KSvg.FrameSvgItem {
id: thickPanelSvg
visible: false
prefix: 'thick'
imagePath: "widgets/panel-background"
}
KSvg.FrameSvgItem {
id: floatingPanelSvg
prefix: ['floating', '']
imagePath: "widgets/panel-background"
}
// NOTE: Many of the properties in this file are accessed directly in C++ PanelView!
// If you change these, make sure to also correct the related code in panelview.cpp.
readonly property bool topEdge: containment?.plasmoid?.location === PlasmaCore.Types.TopEdge
readonly property bool leftEdge: containment?.plasmoid?.location === PlasmaCore.Types.LeftEdge
readonly property bool rightEdge: containment?.plasmoid?.location === PlasmaCore.Types.RightEdge
readonly property bool bottomEdge: containment?.plasmoid?.location === PlasmaCore.Types.BottomEdge
readonly property int topPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.top + Kirigami.Units.smallSpacing, spacingAtMinSize));
readonly property int bottomPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.bottom + Kirigami.Units.smallSpacing, spacingAtMinSize));
readonly property int leftPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.left + Kirigami.Units.smallSpacing, spacingAtMinSize));
readonly property int rightPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.right + Kirigami.Units.smallSpacing, spacingAtMinSize));
readonly property int fixedBottomFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.bottom : 8)
readonly property int fixedLeftFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.left : 8)
readonly property int fixedRightFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.right : 8)
readonly property int fixedTopFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.top : 8)
readonly property int bottomFloatingPadding: Math.round(fixedBottomFloatingPadding * floatingness)
readonly property int leftFloatingPadding: Math.round(fixedLeftFloatingPadding * floatingness)
readonly property int rightFloatingPadding: Math.round(fixedRightFloatingPadding * floatingness)
readonly property int topFloatingPadding: Math.round(fixedTopFloatingPadding * floatingness)
readonly property int minPanelHeight: translucentItem.minimumDrawingHeight
readonly property int minPanelWidth: translucentItem.minimumDrawingWidth
TaskManager.VirtualDesktopInfo {
id: virtualDesktopInfo
}
TaskManager.ActivityInfo {
id: activityInfo
}
// We need to have a little gap between the raw visibleWindowsModel count
// and actually determining if a window is touching.
// This is because certain dialog windows start off with a position of (screenwidth/2, screenheight/2)
// and they register as "touching" in the split-second before KWin can place them correctly.
// This avoids the panel flashing if it is auto-hide etc and such a window is shown.
// Examples of such windows: properties of a file on desktop, or portal "open with" dialog
property bool touchingWindow: false
property bool touchingWindowDirect: visibleWindowsModel.count > 0
property bool showingDesktop: KWindowSystem.showingDesktop
Timer {
id: touchingWindowDebounceTimer
interval: 10 // ms, I find that this value is enough while not causing unresponsiveness while dragging windows close
onTriggered: root.touchingWindow = !KWindowSystem.showingDesktop && root.touchingWindowDirect
}
onTouchingWindowDirectChanged: touchingWindowDebounceTimer.start()
onShowingDesktopChanged: touchingWindowDebounceTimer.start()
TaskManager.TasksModel {
id: visibleWindowsModel
filterByVirtualDesktop: true
filterByActivity: true
filterByScreen: false
filterByRegion: TaskManager.RegionFilterMode.Intersect
filterHidden: true
filterMinimized: true
screenGeometry: panel.screenGeometry
virtualDesktop: virtualDesktopInfo.currentDesktop
activity: activityInfo.currentActivity
groupMode: TaskManager.TasksModel.GroupDisabled
Binding on regionGeometry {
delayed: true
value: panel.width, panel.height, panel.x, panel.y, panel.dogdeGeometryByDistance(panel.visibilityMode === Panel.Global.DodgeWindows ? -1 : 1) // +1 is for overlap detection, -1 is for snapping to panel
}
}
Connections {
target: root.containment?.plasmoid ?? null
function onActivated() {
if (root.containment.plasmoid.status === PlasmaCore.Types.AcceptingInputStatus) {
root.containment.plasmoid.status = PlasmaCore.Types.PassiveStatus;
} else {
root.containment.plasmoid.status = PlasmaCore.Types.AcceptingInputStatus;
}
}
}
// Floatingness is a value in [0, 1] that's multiplied to the floating margin; 0: not floating, 1: floating, between 0 and 1: animation between the two states
readonly property int floatingnessAnimationDuration: Kirigami.Units.longDuration
property double floatingnessTarget: 0.0 // The animation is handled in panelview.cpp for efficiency
property double floatingness: 0.0
// PanelOpacity is a value in [0, 1] that's used as the opacity of the opaque elements over the transparent ones; values between 0 and 1 are used for animations
property double panelOpacity
Behavior on panelOpacity {
NumberAnimation {
duration: Kirigami.Units.longDuration
easing.type: Easing.OutCubic
}
}
// This value is read from panelview.cpp and disables shadow for floating panels, as they'd be detached from the panel
property bool hasShadows: floatingness < 0.5
property var panelMask: floatingness === 0 ? (panelOpacity === 1 ? opaqueItem.mask : translucentItem.mask) : (panelOpacity === 1 ? floatingOpaqueItem.mask : floatingTranslucentItem.mask)
// The point is read from panelview.cpp and is used as an offset for the mask
readonly property point floatingTranslucentItemOffset: Qt.point(floatingTranslucentItem.x, floatingTranslucentItem.y)
KSvg.FrameSvgItem {
id: translucentItem
visible: floatingness === 0 && panelOpacity !== 1
enabledBorders: panel.enabledBorders
anchors.fill: floatingTranslucentItem
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "widgets/panel-background"
}
KSvg.FrameSvgItem {
id: floatingTranslucentItem
visible: floatingness !== 0 && panelOpacity !== 1
x: root.leftEdge ? fixedLeftFloatingPadding + fixedRightFloatingPadding * (1 - floatingness) : leftFloatingPadding
y: root.topEdge ? fixedTopFloatingPadding + fixedBottomFloatingPadding * (1 - floatingness) : topFloatingPadding
width: verticalPanel ? panel.thickness : parent.width - leftFloatingPadding - rightFloatingPadding
height: verticalPanel ? parent.height - topFloatingPadding - bottomFloatingPadding : panel.thickness
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "widgets/panel-background"
}
KSvg.FrameSvgItem {
id: floatingOpaqueItem
visible: floatingness !== 0 && panelOpacity !== 0
opacity: panelOpacity
anchors.fill: floatingTranslucentItem
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "solid/widgets/panel-background"
}
KSvg.FrameSvgItem {
id: opaqueItem
visible: panelOpacity !== 0 && floatingness === 0
opacity: panelOpacity
enabledBorders: panel.enabledBorders
anchors.fill: floatingTranslucentItem
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "solid/widgets/panel-background"
}
KSvg.FrameSvgItem {
id: floatingShadow
visible: !hasShadows
z: -100
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "solid/widgets/panel-background"
prefix: "shadow"
anchors {
fill: floatingTranslucentItem
topMargin: -floatingShadow.margins.top
leftMargin: -floatingShadow.margins.left
rightMargin: -floatingShadow.margins.right
bottomMargin: -floatingShadow.margins.bottom
}
}
Keys.onEscapePressed: {
root.parent.focus = false
}
property bool isOpaque: panel.opacityMode === Panel.Global.Opaque
property bool isTransparent: panel.opacityMode === Panel.Global.Translucent
property bool isAdaptive: panel.opacityMode === Panel.Global.Adaptive
property bool floating: panel.floating
property bool hasCompositing: KWindowSystem.isPlatformX11 ? KX11Extras.compositingActive : true
readonly property bool screenCovered: touchingWindow && panel.visibilityMode == Panel.Global.NormalPanel
property var stateTriggers: [floating, screenCovered, isOpaque, isAdaptive, isTransparent, hasCompositing, containment]
onStateTriggersChanged: {
let opaqueApplets = false
let floatingApplets = false
if ((!floating || screenCovered) && (isOpaque || (screenCovered && isAdaptive))) {
panelOpacity = 1
opaqueApplets = true
floatingnessTarget = 0
} else if ((!floating || screenCovered) && (isTransparent || (!screenCovered && isAdaptive))) {
panelOpacity = 0
floatingnessTarget = 0
} else if ((floating && !screenCovered) && (isTransparent || isAdaptive)) {
panelOpacity = 0
floatingnessTarget = 1
floatingApplets = true
} else if (floating && !screenCovered && isOpaque) {
panelOpacity = 1
opaqueApplets = true
floatingnessTarget = 1
floatingApplets = true
}
if (!KWindowSystem.isPlatformWayland && !KX11Extras.compositingActive) {
opaqueApplets = false
panelOpacity = 0
}
// Not using panelOpacity to check as it has a NumberAnimation, and it will thus
// be still read as the initial value here, before the animation starts.
if (containment) {
if (opaqueApplets) {
containment.plasmoid.containmentDisplayHints |= PlasmaCore.Types.ContainmentPrefersOpaqueBackground
} else {
containment.plasmoid.containmentDisplayHints &= ~PlasmaCore.Types.ContainmentPrefersOpaqueBackground
}
if (floatingApplets) {
containment.plasmoid.containmentDisplayHints |= PlasmaCore.Types.ContainmentPrefersFloatingApplets
} else {
containment.plasmoid.containmentDisplayHints &= ~PlasmaCore.Types.ContainmentPrefersFloatingApplets
}
}
}
function adjustPrefix() {
if (!containment) {
return "";
}
var pre;
switch (containment.plasmoid.location) {
case PlasmaCore.Types.LeftEdge:
pre = "west";
break;
case PlasmaCore.Types.TopEdge:
pre = "north";
break;
case PlasmaCore.Types.RightEdge:
pre = "east";
break;
case PlasmaCore.Types.BottomEdge:
pre = "south";
break;
default:
pre = "";
break;
}
translucentItem.prefix = opaqueItem.prefix = floatingTranslucentItem.prefix = floatingOpaqueItem.prefix = [pre, ""];
}
onContainmentChanged: {
if (!containment) {
return;
}
containment.parent = containmentParent;
containment.visible = true;
containment.anchors.fill = containmentParent;
containment.plasmoid.locationChanged.connect(adjustPrefix);
adjustPrefix();
}
Binding {
target: panel
property: "length"
when: containment
value: {
if (!containment) {
return;
}
if (verticalPanel) {
return containment.Layout.preferredHeight
} else {
return containment.Layout.preferredWidth
}
}
restoreMode: Binding.RestoreBinding
}
Binding {
target: panel
property: "backgroundHints"
when: containment
value: {
if (!containment) {
return;
}
return containment.plasmoid.backgroundHints;
}
restoreMode: Binding.RestoreBinding
}
KSvg.FrameSvgItem {
Accessible.name: i18n("Panel Focus Indicator")
x: root.verticalPanel || !panel.activeFocusItem
? translucentItem.x
: Math.max(panel.activeFocusItem.Kirigami.ScenePosition.x, panel.activeFocusItem.Kirigami.ScenePosition.x)
y: root.verticalPanel && panel.activeFocusItem
? Math.max(panel.activeFocusItem.Kirigami.ScenePosition.y, panel.activeFocusItem.Kirigami.ScenePosition.y)
: translucentItem.y
width: panel.activeFocusItem
? (root.verticalPanel ? translucentItem.width : Math.min(panel.activeFocusItem.width, panel.activeFocusItem.width))
: 0
height: panel.activeFocusItem
? (root.verticalPanel ? Math.min(panel.activeFocusItem.height, panel.activeFocusItem.height) : translucentItem.height)
: 0
visible: panel.active && panel.activeFocusItem
imagePath: "widgets/tabbar"
prefix: {
if (!root.containment) {
return "";
}
var prefix = ""
switch (root.containment.plasmoid.location) {
case PlasmaCore.Types.LeftEdge:
prefix = "west-active-tab";
break;
case PlasmaCore.Types.TopEdge:
prefix = "north-active-tab";
break;
case PlasmaCore.Types.RightEdge:
prefix = "east-active-tab";
break;
default:
prefix = "south-active-tab";
}
if (!hasElementPrefix(prefix)) {
prefix = "active-tab";
}
return prefix;
}
}
Item {
id: containmentParent
anchors.fill: isOpaque ? floatingOpaqueItem : floatingTranslucentItem
anchors.leftMargin: root.verticalPanel ? 0 : -Kirigami.Units.smallSpacing
width: root.verticalPanel ? panel.thickness : root.width - fixedLeftFloatingPadding - fixedRightFloatingPadding
height: root.verticalPanel ? root.height - fixedBottomFloatingPadding - fixedTopFloatingPadding : panel.thickness
}
}