shell: Sync panel code with desktop

This allows us to use desktop panels in the future for convergence.
This commit is contained in:
Devin Lin 2023-11-13 22:11:50 -08:00
parent 99bdec0669
commit a4989d4313
3 changed files with 361 additions and 18 deletions

View file

@ -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;

View file

@ -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();
}
}
}
}

View file

@ -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
}
}