diff --git a/shell/contents/applet/AppletError.qml b/shell/contents/applet/AppletError.qml new file mode 100644 index 00000000..cd391065 --- /dev/null +++ b/shell/contents/applet/AppletError.qml @@ -0,0 +1,266 @@ +/* + SPDX-FileCopyrightText: 2013 Marco Martin + + 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 + } + } + } +} + diff --git a/shell/contents/applet/CompactApplet.qml b/shell/contents/applet/CompactApplet.qml new file mode 100644 index 00000000..58e858b3 --- /dev/null +++ b/shell/contents/applet/CompactApplet.qml @@ -0,0 +1,339 @@ +/* + SPDX-FileCopyrightText: 2013 Marco Martin + + 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 + } + } +} diff --git a/shell/contents/applet/DefaultCompactRepresentation.qml b/shell/contents/applet/DefaultCompactRepresentation.qml new file mode 100644 index 00000000..488378bf --- /dev/null +++ b/shell/contents/applet/DefaultCompactRepresentation.qml @@ -0,0 +1,76 @@ +/* + SPDX-FileCopyrightText: 2013 Marco Martin + + 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 + } +} diff --git a/shell/contents/views/Panel.qml b/shell/contents/views/Panel.qml index cbef0400..7dcba88c 100644 --- a/shell/contents/views/Panel.qml +++ b/shell/contents/views/Panel.qml @@ -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 } - }