diff --git a/containments/taskpanel/package/contents/ui/main.qml b/containments/taskpanel/package/contents/ui/main.qml index e35b4673..3933f8f9 100644 --- a/containments/taskpanel/package/contents/ui/main.qml +++ b/containments/taskpanel/package/contents/ui/main.qml @@ -19,6 +19,7 @@ import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin ContainmentItem { id: root + Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground // filled in by the shell (Panel.qml) with the plasma-workspace PanelView property var panel: null @@ -26,7 +27,13 @@ ContainmentItem { setWindowProperties() } - Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground + // filled in by the shell (Panel.qml) + property var tabBar: null + onTabBarChanged: { + if (tabBar) { + tabBar.visible = false; + } + } readonly property bool inLandscape: Screen.width > Screen.height; @@ -68,6 +75,7 @@ ContainmentItem { function setWindowProperties() { if (root.panel) { + root.panel.floating = false; root.panel.maximize(); // maximize first, then we can apply offsets (otherwise they are overridden) root.panel.offset = intendedWindowOffset; root.panel.thickness = navigationPanelHeight; diff --git a/shell/contents/views/Desktop.qml b/shell/contents/views/Desktop.qml index fcd16077..a389f2db 100644 --- a/shell/contents/views/Desktop.qml +++ b/shell/contents/views/Desktop.qml @@ -53,6 +53,7 @@ Rectangle { readonly property color backgroundColor: Kirigami.Theme.backgroundColor readonly property color textColor: Kirigami.Theme.textColor + property color colorFromPlugin: "transparent" Kirigami.Theme.inherit: false Kirigami.Theme.backgroundColor: backgroundColor @@ -65,17 +66,25 @@ Rectangle { target: desktop property: "accentColor" value: { + if (!Qt.colorEqual(imageColors.colorFromPlugin, "transparent")) { + return imageColors.colorFromPlugin; + } if (imageColors.palette.length === 0) { - return "#00000000"; + return "transparent"; } return imageColors.dominant; } + when: desktop.usedInAccentColor // Without this, accentColor may still be updated after usedInAccentColor becomes false } property Connections repaintConnection: Connections { target: root.containment.wallpaper - function onRepaintNeeded() { - imageColors.update(); + function onRepaintNeeded(color) { + imageColors.colorFromPlugin = color; + + if (Qt.colorEqual(color, "transparent")) { + imageColors.update(); + } } } } diff --git a/shell/contents/views/Panel.qml b/shell/contents/views/Panel.qml index 7b80f1bd..d44abb91 100644 --- a/shell/contents/views/Panel.qml +++ b/shell/contents/views/Panel.qml @@ -4,28 +4,307 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -import QtQuick -import QtQuick.Layouts +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 -Rectangle { +import org.kde.plasma.plasmoid 2.0 + +Item { id: root - visible: false //adjust borders is run during setup. We want to avoid painting till completed + // NOTE: Plasma Mobile specific: + Connections { + target: root + + function onContainmentChanged() { + // HACK: add PanelView into the containment so that it can be used + if (containment.panel !== undefined) { + containment.panel = panel; + } + if (containment.tabBar !== undefined) { + containment.tabBar = tabBar; + } + } + } + + // 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 + property Item containment - color: !containment || containment.plasmoid.backgroundHints == PlasmaCore.Types.NoBackground ? "transparent" : Kirigami.Theme.textColor + 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 + visible: false + prefix: ['floating', ''] + 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) + + readonly property int minPanelHeight: translucentItem.minimumDrawingHeight + readonly property int minPanelWidth: translucentItem.minimumDrawingWidth + + TaskManager.VirtualDesktopInfo { + id: virtualDesktopInfo + } + + TaskManager.ActivityInfo { + id: activityInfo + } + + property bool touchingWindow: visibleWindowsModel.count > 0 + + TaskManager.TasksModel { + id: visibleWindowsModel + filterByVirtualDesktop: true + filterByActivity: true + filterByScreen: true + 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 + 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)) + } + } + + Connections { + target: containment + 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; + } + } + } + + // 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 + // 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 + 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) + + // 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 + 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 + readonly property bool screenCovered: !KWindowSystem.showingDesktop && touchingWindow && panel.visibilityMode == Panel.Global.NormalPanel + property var stateTriggers: [floating, screenCovered, isOpaque, isAdaptive, isTransparent, KX11Extras.compositingActive] + onStateTriggersChanged: { + let opaqueApplets = false + let floatingApplets = false + if ((!floating || screenCovered) && (isOpaque || (screenCovered && isAdaptive))) { + panelOpacity = 1 + opaqueApplets = true + floatingness = 0 + } else if ((!floating || screenCovered) && (isTransparent || (!screenCovered && isAdaptive))) { + panelOpacity = 0 + floatingness = 0 + } else if ((floating && !screenCovered) && (isTransparent || isAdaptive)) { + panelOpacity = 0 + floatingness = 1 + floatingApplets = true + } else if (floating && !screenCovered && isOpaque) { + panelOpacity = 1 + opaqueApplets = true + floatingness = 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: { - containment.parent = root; - containment.visible = true; - containment.anchors.fill = root; - - // HACK: add PanelView into the containment so that it can be used - if (containment.panel !== undefined) { - containment.panel = panel; + 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 + delayed: true + value: { + if (!containment) { + return; + } + if (verticalPanel) { + return containment.Layout.preferredHeight + } else { + return containment.Layout.preferredWidth + } + } + restoreMode: Binding.RestoreBinding } Binding { @@ -42,7 +321,54 @@ Rectangle { restoreMode: Binding.RestoreBinding } - Component.onCompleted: { - visible = true + KSvg.FrameSvgItem { + id: tabBar + x: root.verticalPanel || !panel.activeFocusItem + ? 0 + : 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 + + width: panel.activeFocusItem + ? (root.verticalPanel ? root.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) + : 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.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 + } + }