shell: Sync implementations with Plasma Desktop

This fixes some of the oddities with applets that we experience that are designed for Desktop. We don't really have a need for different behaviour from Desktop, so it's probably best to follow it for the least amount of bugs.

Eventually we perhaps should derive our shell package from desktop?
This commit is contained in:
Devin Lin 2024-07-15 18:48:44 -04:00
parent 7f011e92ed
commit ce3ecd9e4b
4 changed files with 754 additions and 77 deletions

View file

@ -0,0 +1,266 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
// NOTE: Below is taken straight out of Plasma Desktop so that we can
// support desktop applets properly, try to keep it in sync:
// plasma-desktop/desktoppackage/contents/applet/AppletError.qml
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components 3.0 as PC3
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.plasmoid 2.0
PlasmoidItem {
id: root
enum LayoutType {
HorizontalPanel,
VerticalPanel,
Desktop,
DesktopCompact
}
property var errorInformation
readonly property real minimumPreferredWidth: Kirigami.Units.gridUnit * 12
readonly property real minimumPreferredHeight: Kirigami.Units.gridUnit * 12
// To properly show the error message in panel
readonly property int layoutForm: {
if (fullRepresentationItem.width >= root.minimumPreferredWidth) {
if (fullRepresentationItem.height >= root.minimumPreferredHeight) {
return AppletError.Desktop;
} else if (fullRepresentationItem.height >= Kirigami.Units.iconSizes.huge + root.fullRepresentationItem.buttonLayout.implicitHeight) {
return AppletError.DesktopCompact;
}
}
return Plasmoid.formFactor === PlasmaCore.Types.Vertical ? AppletError.VerticalPanel : AppletError.HorizontalPanel;
}
preloadFullRepresentation: true
fullRepresentation: GridLayout {
id: fullRep
property alias buttonLayout: buttonLayout
Layout.minimumWidth: {
switch (root.layoutForm) {
case AppletError.Desktop:
case AppletError.DesktopCompact:
// [Icon] [Text]
// [Button]
// [Information]
return Math.max(root.minimumPreferredWidth, buttonLayout.implicitWidth);
case AppletError.VerticalPanel:
// [Icon]
// [Copy]
// [Open]
return Math.max(headerIcon.implicitWidth, buttonLayout.implicitWidth);
case AppletError.HorizontalPanel:
// [Icon] [Copy] [Open]
return headingLayout.implicitWidth + rowSpacing + buttonLayout.implicitWidth;
}
}
Layout.minimumHeight: {
switch (root.layoutForm) {
case AppletError.Desktop:
return headingLayout.implicitHeight + fullRep.columnSpacing + buttonLayout.implicitHeight + fullRep.columnSpacing + fullContentView.implicitHeight;
case AppletError.DesktopCompact:
return Math.max(headingLayout.implicitHeight, buttonLayout.implicitHeight);
case AppletError.VerticalPanel:
return headingLayout.implicitHeight + fullRep.columnSpacing + buttonLayout.implicitHeight;
case AppletError.HorizontalPanel:
return Math.max(headingLayout.implicitHeight, buttonLayout.implicitHeight);
}
}
// Same as systray popups
Layout.preferredWidth: Kirigami.Units.gridUnit * 24
Layout.preferredHeight: Kirigami.Units.gridUnit * 24
Layout.maximumWidth: Kirigami.Units.gridUnit * 34
Layout.maximumHeight: Kirigami.Units.gridUnit * 34
rowSpacing: textArea.topPadding
columnSpacing: rowSpacing
flow: {
switch (root.layoutForm) {
case AppletError.HorizontalPanel:
return GridLayout.LeftToRight;
default:
return GridLayout.TopToBottom;
}
}
RowLayout {
id: headingLayout
Layout.margins: root.layoutForm !== AppletError.Desktop ? 0 : Kirigami.Units.gridUnit
Layout.maximumWidth: fullRep.width
spacing: 0
Layout.fillWidth: true
Kirigami.Icon {
id: headerIcon
implicitWidth: Math.min(Kirigami.Units.iconSizes.huge, fullRep.width, fullRep.height)
implicitHeight: implicitWidth
activeFocusOnTab: true
source: "dialog-error"
Accessible.description: heading.text
PlasmaCore.ToolTipArea {
anchors.fill: parent
enabled: !heading.visible || heading.truncated
mainText: heading.text
textFormat: Text.PlainText
}
}
Kirigami.Heading {
id: heading
visible: root.layoutForm !== AppletError.VerticalPanel
// Descent is equal to the amount of space above and below capital letters.
// Add descent to the sides to make the spacing around Latin text look more even.
leftPadding: headingFontMetrics.descent
rightPadding: headingFontMetrics.descent
text: root.errorInformation ? root.errorInformation.compactError : "No error information."
textFormat: Text.PlainText
level: 2
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
Layout.fillWidth: true
Layout.maximumHeight: headerIcon.implicitHeight
FontMetrics {
id: headingFontMetrics
font: heading.font
}
}
}
GridLayout {
id: buttonLayout
Layout.alignment: Qt.AlignCenter
rowSpacing: fullRep.rowSpacing
columnSpacing: parent.columnSpacing
flow: {
switch (root.layoutForm) {
case AppletError.HorizontalPanel:
case AppletError.VerticalPanel:
return fullRep.flow;
default:
return GridLayout.LeftToRight;
}
}
PC3.Button {
id: copyButton
display: root.layoutForm === AppletError.HorizontalPanel || root.layoutForm === AppletError.VerticalPanel ? PC3.AbstractButton.IconOnly : PC3.AbstractButton.TextBesideIcon
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Copy to Clipboard")
icon.name: "edit-copy"
onClicked: {
textArea.selectAll()
textArea.copy()
textArea.deselect()
}
PlasmaCore.ToolTipArea {
anchors.fill: parent
enabled: parent.display === PC3.AbstractButton.IconOnly
mainText: parent.text
textFormat: Text.PlainText
}
}
Loader {
id: compactContentLoader
active: root.layoutForm !== AppletError.Desktop
visible: active
sourceComponent: PC3.Button {
display: copyButton.display
icon.name: "window-new"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "View Error Details…")
checked: dialog.visible
onClicked: dialog.visible = !dialog.visible
PlasmaCore.ToolTipArea {
anchors.fill: parent
enabled: parent.display === PC3.AbstractButton.IconOnly
mainText: parent.text
textFormat: Text.PlainText
}
QQC2.ApplicationWindow {
id: dialog
flags: Qt.Dialog | Qt.WindowStaysOnTopHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint
minimumWidth: dialogFontMetrics.height * 12
+ dialogTextArea.leftPadding + dialogTextArea.rightPadding
minimumHeight: dialogFontMetrics.height * 12
+ dialogTextArea.topPadding + dialogTextArea.bottomPadding
width: Kirigami.Units.gridUnit * 24
height: Kirigami.Units.gridUnit * 24
color: palette.base
QQC2.ScrollView {
id: dialogScrollView
anchors.fill: parent
QQC2.TextArea {
id: dialogTextArea
// HACK: silence binding loop warnings.
// contentWidth seems to be causing the binding loop,
// but contentWidth is read-only and we have no control
// over how it is calculated.
implicitWidth: 0
wrapMode: TextEdit.Wrap
text: textArea.text
font.family: "monospace"
readOnly: true
selectByMouse: true
background: null
FontMetrics {
id: dialogFontMetrics
font: dialogTextArea.font
}
}
background: null
}
}
}
}
}
PC3.ScrollView {
id: fullContentView
// Not handled by a Loader because we need
// TextEdit::copy() to copy to clipboard.
visible: !compactContentLoader.active
Layout.fillHeight: true
Layout.fillWidth: true
PC3.TextArea {
id: textArea
// HACK: silence binding loop warnings.
// contentWidth seems to be causing the binding loop,
// but contentWidth is read-only and we have no control
// over how it is calculated.
implicitWidth: 0
wrapMode: TextEdit.Wrap
text: root.errorInformation && root.errorInformation.errors ?
root.errorInformation.errors.join("\n\n")
// This is just to suppress warnings. Users should never see this.
: "No error information."
font.family: "monospace"
readOnly: true
selectByMouse: true
}
}
}
}

View file

@ -0,0 +1,339 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
// NOTE: Below is taken straight out of Plasma Desktop so that we can
// support desktop applets properly, try to keep it in sync:
// plasma-desktop/desktoppackage/contents/applet/CompactApplet.qml
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import org.kde.plasma.core as PlasmaCore
import org.kde.ksvg 1.0 as KSvg
import org.kde.plasma.plasmoid 2.0
import org.kde.kquickcontrolsaddons 2.0
import org.kde.kirigami 2.20 as Kirigami
PlasmaCore.ToolTipArea {
id: root
objectName: "org.kde.desktop-CompactApplet"
anchors.fill: parent
mainText: plasmoidItem ? plasmoidItem.toolTipMainText : ""
subText: plasmoidItem ? plasmoidItem.toolTipSubText : ""
location: Plasmoid.location
active: plasmoidItem ? !plasmoidItem.expanded : false
textFormat: plasmoidItem ? plasmoidItem.toolTipTextFormat : 0
mainItem: plasmoidItem && plasmoidItem.toolTipItem ? plasmoidItem.toolTipItem : null
readonly property bool vertical: location === PlasmaCore.Types.RightEdge || location === PlasmaCore.Types.LeftEdge
property Item fullRepresentation
property Item compactRepresentation
property Item expandedFeedback: expandedItem
property PlasmoidItem plasmoidItem
onCompactRepresentationChanged: {
if (compactRepresentation) {
compactRepresentation.anchors.fill = null;
compactRepresentation.parent = compactRepresentationParent;
compactRepresentation.anchors.fill = compactRepresentationParent;
compactRepresentation.visible = true;
}
root.visible = true;
}
onFullRepresentationChanged: {
if (fullRepresentation) {
fullRepresentation.anchors.fill = null;
fullRepresentation.parent = appletParent;
fullRepresentation.anchors.fill = appletParent;
}
}
FocusScope {
id: compactRepresentationParent
anchors.fill: parent
activeFocusOnTab: true
onActiveFocusChanged: {
// When the scope gets the active focus, try to focus its first descendant,
// if there is on which has activeFocusOnTab
if (!activeFocus) {
return;
}
let nextItem = nextItemInFocusChain();
let candidate = nextItem;
while (candidate.parent) {
if (candidate === compactRepresentationParent) {
nextItem.forceActiveFocus();
return;
}
candidate = candidate.parent;
}
}
objectName: "expandApplet"
Accessible.name: root.mainText
Accessible.description: i18nd("plasma_shell_org.kde.plasma.desktop", "Open %1", root.subText)
Accessible.role: Accessible.Button
Accessible.onPressAction: Plasmoid.activated()
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Space:
case Qt.Key_Enter:
case Qt.Key_Return:
case Qt.Key_Select:
Plasmoid.activated();
break;
}
}
}
KSvg.FrameSvgItem {
id: expandedItem
z: -100
property var containerMargins: {
let item = root;
while (item.parent) {
item = item.parent;
if (item.isAppletContainer) {
return item.getMargins;
}
}
return undefined;
}
anchors {
fill: parent
property bool returnAllMargins: true
// The above makes sure margin is returned even for side margins, that
// would be otherwise turned off.
bottomMargin: !vertical && containerMargins ? -containerMargins('bottom', returnAllMargins) : 0;
topMargin: !vertical && containerMargins ? -containerMargins('top', returnAllMargins) : 0;
leftMargin: vertical && containerMargins ? -containerMargins('left', returnAllMargins) : 0;
rightMargin: vertical && containerMargins ? -containerMargins('right', returnAllMargins) : 0;
}
imagePath: "widgets/tabbar"
visible: opacity > 0
prefix: {
let prefix;
switch (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;
}
opacity: plasmoidItem && plasmoidItem.expanded ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: Kirigami.Units.shortDuration
easing.type: Easing.InOutQuad
}
}
}
Timer {
id: expandedSync
interval: 100
onTriggered: plasmoidItem.expanded = dialog.visible;
}
Connections {
target: Plasmoid.internalAction("configure")
function onTriggered() {
if (root.plasmoidItem.hideOnWindowDeactivate) {
plasmoidItem.expanded = false
}
}
}
Connections {
target: root.Plasmoid
function onContextualActionsAboutToShow() { root.hideImmediately() }
}
PlasmaCore.AppletPopup {
id: dialog
objectName: "popupWindow"
popupDirection: switch (Plasmoid.location) {
case PlasmaCore.Types.TopEdge:
return Qt.BottomEdge
case PlasmaCore.Types.LeftEdge:
return Qt.RightEdge
case PlasmaCore.Types.RightEdge:
return Qt.LeftEdge
default:
return Qt.TopEdge
}
margin: (Plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentPrefersFloatingApplets) ? Kirigami.Units.largeSpacing : 0
floating: Plasmoid.location == PlasmaCore.Types.Floating
removeBorderStrategy: Plasmoid.location === PlasmaCore.Types.Floating
? PlasmaCore.AppletPopup.AtScreenEdges
: PlasmaCore.AppletPopup.AtScreenEdges | PlasmaCore.AppletPopup.AtPanelEdges
hideOnWindowDeactivate: root.plasmoidItem && root.plasmoidItem.hideOnWindowDeactivate
visible: root.plasmoidItem && root.plasmoidItem.expanded && fullRepresentation
visualParent: root.compactRepresentation
backgroundHints: (Plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentPrefersOpaqueBackground) ? PlasmaCore.AppletPopup.SolidBackground : PlasmaCore.AppletPopup.StandardBackground
appletInterface: root.plasmoidItem
property var oldStatus: PlasmaCore.Types.UnknownStatus
onVisibleChanged: {
if (!visible) {
expandedSync.restart();
Plasmoid.status = oldStatus;
} else {
oldStatus = Plasmoid.status;
Plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus;
// This call currently fails and complains at runtime:
// QWindow::setWindowState: QWindow::setWindowState does not accept Qt::WindowActive
dialog.requestActivate();
}
}
//It's a MouseEventListener to get all the events, so the eventfilter will be able to catch them
mainItem: MouseEventListener {
id: appletParent
focus: true
Keys.onEscapePressed: {
root.plasmoidItem.expanded = false;
}
Layout.minimumWidth: fullRepresentation ? fullRepresentation.Layout.minimumWidth : 0
Layout.minimumHeight: fullRepresentation ? fullRepresentation.Layout.minimumHeight : 0
Layout.maximumWidth: fullRepresentation ? fullRepresentation.Layout.maximumWidth : Infinity
Layout.maximumHeight: fullRepresentation ? fullRepresentation.Layout.maximumHeight : Infinity
implicitWidth: {
if (root.fullRepresentation !== null) {
/****/ if (root.fullRepresentation.Layout.preferredWidth > 0) {
return root.fullRepresentation.Layout.preferredWidth;
} else if (root.fullRepresentation.implicitWidth > 0) {
return root.fullRepresentation.implicitWidth;
}
}
return Kirigami.Units.iconSizes.sizeForLabels * 35;
}
implicitHeight: {
if (root.fullRepresentation !== null) {
/****/ if (fullRepresentation.Layout.preferredHeight > 0) {
return fullRepresentation.Layout.preferredHeight;
} else if (fullRepresentation.implicitHeight > 0) {
return fullRepresentation.implicitHeight;
}
}
return Kirigami.Units.iconSizes.sizeForLabels * 25;
}
onActiveFocusChanged: {
if (activeFocus && fullRepresentation) {
fullRepresentation.forceActiveFocus()
}
}
// Draws a line between the applet dialog and the panel
KSvg.SvgItem {
id: separator
// Only draw for popups of panel applets, not desktop applets
visible: [PlasmaCore.Types.TopEdge, PlasmaCore.Types.LeftEdge, PlasmaCore.Types.RightEdge, PlasmaCore.Types.BottomEdge]
.includes(Plasmoid.location) && !dialog.margin
anchors {
topMargin: -dialog.topPadding
leftMargin: -dialog.leftPadding
rightMargin: -dialog.rightPadding
bottomMargin: -dialog.bottomPadding
}
z: 999 /* Draw the line on top of the applet */
elementId: (Plasmoid.location === PlasmaCore.Types.TopEdge || Plasmoid.location === PlasmaCore.Types.BottomEdge) ? "horizontal-line" : "vertical-line"
imagePath: "widgets/line"
states: [
State {
when: Plasmoid.location === PlasmaCore.Types.TopEdge
AnchorChanges {
target: separator
anchors {
top: separator.parent.top
left: separator.parent.left
right: separator.parent.right
}
}
PropertyChanges {
target: separator
height: 1
}
},
State {
when: Plasmoid.location === PlasmaCore.Types.LeftEdge
AnchorChanges {
target: separator
anchors {
left: separator.parent.left
top: separator.parent.top
bottom: separator.parent.bottom
}
}
PropertyChanges {
target: separator
width: 1
}
},
State {
when: Plasmoid.location === PlasmaCore.Types.RightEdge
AnchorChanges {
target: separator
anchors {
top: separator.parent.top
right: separator.parent.right
bottom: separator.parent.bottom
}
}
PropertyChanges {
target: separator
width: 1
}
},
State {
when: Plasmoid.location === PlasmaCore.Types.BottomEdge
AnchorChanges {
target: separator
anchors {
left: separator.parent.left
right: separator.parent.right
bottom: separator.parent.bottom
}
}
PropertyChanges {
target: separator
height: 1
}
}
]
}
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
}
}
}

View file

@ -0,0 +1,76 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
// NOTE: Below is taken straight out of Plasma Desktop so that we can
// support desktop applets properly, try to keep it in sync:
// plasma-desktop/desktoppackage/contents/applet/DefaultCompactRepresentation.qml
import QtQuick 2.15
import QtQuick.Layouts 1.15
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.plasmoid 2.0
import org.kde.kirigami 2.20 as Kirigami
Kirigami.Icon {
property PlasmoidItem plasmoidItem
readonly property bool inPanel: [PlasmaCore.Types.TopEdge, PlasmaCore.Types.RightEdge, PlasmaCore.Types.BottomEdge, PlasmaCore.Types.LeftEdge]
.includes(Plasmoid.location)
Layout.minimumWidth: {
switch (Plasmoid.formFactor) {
case PlasmaCore.Types.Vertical:
return 0;
case PlasmaCore.Types.Horizontal:
return height;
default:
return Kirigami.Units.gridUnit * 3;
}
}
Layout.minimumHeight: {
switch (Plasmoid.formFactor) {
case PlasmaCore.Types.Vertical:
return width;
case PlasmaCore.Types.Horizontal:
return 0;
default:
return Kirigami.Units.gridUnit * 3;
}
}
source: Plasmoid.icon || "plasma"
active: mouseArea.containsMouse
activeFocusOnTab: true
Keys.onPressed: event => {
switch (event.key) {
case Qt.Key_Space:
case Qt.Key_Enter:
case Qt.Key_Return:
case Qt.Key_Select:
Plasmoid.activated();
event.accepted = true; // BUG 481393: Prevent system tray from receiving the event
break;
}
}
Accessible.name: Plasmoid.title
Accessible.description: plasmoidItem.toolTipSubText ?? ""
Accessible.role: Accessible.Button
MouseArea {
id: mouseArea
property bool wasExpanded: false
anchors.fill: parent
hoverEnabled: true
onPressed: wasExpanded = plasmoidItem.expanded
onClicked: plasmoidItem.expanded = !wasExpanded
}
}

View file

@ -24,8 +24,8 @@ Item {
// NOTE: Plasma Mobile specific:
Connections {
target: root
function onContainmentChanged() {
function onContainmentChanged() {
// HACK: add PanelView into the containment so that it can be used
if (containment.panel !== undefined) {
containment.panel = panel;
@ -36,7 +36,7 @@ Item {
}
}
// NOTE: Below is taken straight out of Plasma Desktop so that we can
// NOTE: Below is taken straight out of Plasma Desktop so that we can
// support desktop panels properly, try to keep it in sync:
// plasma-desktop/desktoppackage/contents/views/Panel.qml
@ -59,29 +59,41 @@ Item {
imagePath: "widgets/panel-background"
}
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)
// 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 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 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 minPanelHeight: translucentItem.minimumDrawingHeight
readonly property int minPanelWidth: translucentItem.minimumDrawingWidth
// This value is read from panelview.cpp which needs it to decide which border should be enabled
property real topShadowMargin: -floatingTranslucentItem.y
property real leftShadowMargin: -floatingTranslucentItem.x
property real rightShadowMargin: -(width - floatingTranslucentItem.width - floatingTranslucentItem.x)
property real bottomShadowMargin: -(height - floatingTranslucentItem.height - floatingTranslucentItem.y)
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)
TaskManager.VirtualDesktopInfo {
id: virtualDesktopInfo
}
@ -90,13 +102,28 @@ Item {
id: activityInfo
}
property bool touchingWindow: visibleWindowsModel.count > 0
// 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: true
filterByScreen: false
filterByRegion: TaskManager.RegionFilterMode.Intersect
filterHidden: true
filterMinimized: true
@ -109,38 +136,28 @@ Item {
Binding on regionGeometry {
delayed: true
property real verticalMargin: (fixedTopFloatingPadding + fixedBottomFloatingPadding) * (1 - floatingness)
property real horizontalMargin: (fixedLeftFloatingPadding + fixedRightFloatingPadding) * (1 - floatingness)
// This makes the panel de-float when a window is 6px from it or less.
// 6px is chosen to avoid any potential issue with kwin snapping behavior,
// and it looks like the panel hides away from the active window.
value: floatingness, panel.width, panel.height, panel.x, panel.y, panel.geometryByDistance(6 + (verticalPanel ? horizontalMargin : verticalMargin))
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: containment
target: root.containment?.plasmoid ?? null
function onActivated() {
// BUG 472909: status changes to PassiveStatus or ActiveStatus after applet shortcut is pressed for the second time
if (containment.status === PlasmaCore.Types.PassiveStatus /*After pressing panel shortcut*/ || containment.status === PlasmaCore.Types.ActiveStatus) {
containment.status = PlasmaCore.Types.AcceptingInputStatus;
// BUG 472909: if applet shortcut is pressed, panel also gets activated, but status will change to RequiresAttentionStatus after applet has focus
} else /* Panel has focus, or applet has focus */ {
containment.status = PlasmaCore.Types.PassiveStatus;
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
property double floatingness
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 floatingness {
NumberAnimation {
duration: Kirigami.Units.longDuration
easing.type: Easing.OutCubic
}
}
Behavior on panelOpacity {
NumberAnimation {
duration: Kirigami.Units.longDuration
@ -148,14 +165,6 @@ Item {
}
}
// 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)
// These two values are read from panelview.cpp and are used as an offset for the mask
property int maskOffsetX: floatingTranslucentItem.x
property int maskOffsetY: floatingTranslucentItem.y
KSvg.FrameSvgItem {
id: translucentItem
visible: floatingness === 0 && panelOpacity !== 1
@ -166,8 +175,8 @@ Item {
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
x: root.rightEdge ? fixedLeftFloatingPadding + fixedRightFloatingPadding * (1 - floatingness) : leftFloatingPadding
y: root.bottomEdge ? fixedTopFloatingPadding + fixedBottomFloatingPadding * (1 - floatingness) : topFloatingPadding
width: verticalPanel ? panel.thickness : parent.width - leftFloatingPadding - rightFloatingPadding
height: verticalPanel ? parent.height - topFloatingPadding - bottomFloatingPadding : panel.thickness
@ -188,20 +197,6 @@ Item {
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
@ -211,29 +206,30 @@ Item {
property bool isTransparent: panel.opacityMode === Panel.Global.Translucent
property bool isAdaptive: panel.opacityMode === Panel.Global.Adaptive
property bool floating: panel.floating
readonly property bool screenCovered: !KWindowSystem.showingDesktop && touchingWindow && panel.visibilityMode == Panel.Global.NormalPanel
property var stateTriggers: [floating, screenCovered, isOpaque, isAdaptive, isTransparent]
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
floatingness = 0
floatingnessTarget = 0
} else if ((!floating || screenCovered) && (isTransparent || (!screenCovered && isAdaptive))) {
panelOpacity = 0
floatingness = 0
floatingnessTarget = 0
} else if ((floating && !screenCovered) && (isTransparent || isAdaptive)) {
panelOpacity = 0
floatingness = 1
floatingnessTarget = 1
floatingApplets = true
} else if (floating && !screenCovered && isOpaque) {
panelOpacity = 1
opaqueApplets = true
floatingness = 1
floatingnessTarget = 1
floatingApplets = true
}
if (!KWindowSystem.isPlatformWayland) {
if (!KWindowSystem.isPlatformWayland && !KX11Extras.compositingActive) {
opaqueApplets = false
panelOpacity = 0
}
@ -293,7 +289,6 @@ Item {
target: panel
property: "length"
when: containment
delayed: true
value: {
if (!containment) {
return;
@ -322,19 +317,21 @@ Item {
}
KSvg.FrameSvgItem {
id: tabBar
Accessible.name: i18n("Panel Focus Indicator")
x: root.verticalPanel || !panel.activeFocusItem
? 0
? 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)
: 0
: translucentItem.y
width: panel.activeFocusItem
? (root.verticalPanel ? root.width : Math.min(panel.activeFocusItem.width, panel.activeFocusItem.width))
? (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) : root.height)
? (root.verticalPanel ? Math.min(panel.activeFocusItem.height, panel.activeFocusItem.height) : translucentItem.height)
: 0
visible: panel.active && panel.activeFocusItem
@ -367,8 +364,7 @@ Item {
Item {
id: containmentParent
anchors.centerIn: isOpaque ? floatingOpaqueItem : floatingTranslucentItem
width: root.verticalPanel ? panel.thickness : root.width - root.floatingness * (fixedLeftFloatingPadding + fixedRightFloatingPadding)
height: root.verticalPanel ? root.height - root.floatingness * (fixedBottomFloatingPadding - fixedTopFloatingPadding) : panel.thickness
width: root.verticalPanel ? panel.thickness : root.width - fixedLeftFloatingPadding - fixedRightFloatingPadding
height: root.verticalPanel ? root.height - fixedBottomFloatingPadding - fixedTopFloatingPadding : panel.thickness
}
}