containments/panel: make AppletContainer registered type

Panel has AppletContainer as an unregistered type, which means the type
system can't reason about it.

Instead, we register it as its own type in a separate qml file, and
specify/assert the type where necessary for properties and variables.
This commit is contained in:
Christoph Wolk 2025-12-31 16:18:10 +01:00
parent 2a28d01312
commit 863d22887a
4 changed files with 65 additions and 39 deletions

View file

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
* SPDX-FileCopyrightText: 2022 Niccolò Venerandi <niccolo@venerandi.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.plasmoid
Loader {
required property Item applet
required property int index
property Item temporarySpacer
property Item dragging
property bool isAppletContainer: true
property bool isMarginSeparator: ((applet.Plasmoid?.constraintHints & Plasmoid.MarginAreasSeparator) == Plasmoid.MarginAreasSeparator)
property int appletIndex: index // To make sure it's always readable even inside other models
property bool inThickArea: false
visible: applet.Plasmoid?.status !== PlasmaCore.Types.HiddenStatus || (!Plasmoid.immutable && Plasmoid.userConfiguring) || Containment.corona.editMode;
//when the applet moves caused by its resize, don't animate.
//this is completely heuristic, but looks way less "jumpy"
property bool movingForResize: false
property bool wantsToFillWidth: applet?.Layout.fillWidth
property bool wantsToFillHeight: applet?.Layout.fillHeight
property int availWidth
property int availHeight
}

View file

@ -1,5 +1,6 @@
plasma_add_applet(org.kde.panel
QML_SOURCES
AppletContainer.qml
ConfigOverlay.qml
main.qml
LayoutManager.js

View file

@ -34,7 +34,7 @@ MouseArea {
required property bool reverse
required property int rootWidth
property Item currentApplet
property AppletContainer currentApplet
property int draggedItemIndex
property real startDragOffset: 0.0
property bool dragAndDropping: false
@ -43,7 +43,7 @@ MouseArea {
Drag.active: false
Drag.supportedActions: Qt.MoveAction
Drag.mimeData: {
"text/x-plasmoidinstanceid": Plasmoid.containment.id+':'+configurationArea.currentApplet?.applet.plasmoid.id
"text/x-plasmoidinstanceid": Plasmoid.containment.id+':'+configurationArea.currentApplet?.applet.Plasmoid.id
}
Drag.onDragFinished: dropEvent => {
if (dropEvent != Qt.MoveAction) {
@ -66,7 +66,7 @@ MouseArea {
mouse.x > width + padding || mouse.y > height + padding)) {
configurationArea.currentApplet.grabToImage(result => {
configurationArea.Drag.imageSource = result.url
appletsModel.remove(placeHolder.parent.index)
appletsModel.remove((placeHolder.parent as AppletContainer).index)
currentApplet.visible = false
configurationArea.Drag.active = true
})
@ -121,7 +121,7 @@ MouseArea {
// Need to set currentApplet here too, to make touch selection + drag
// with with a touchscreen, because there are no entered events in that
// case
let item = currentLayout.childAt(mouse.x, mouse.y);
let item = currentLayout.childAt(mouse.x, mouse.y) as AppletContainer;
// BUG 454095: Don't allow dragging lastSpacer as it's not a real applet
if (!item || item == lastSpacer || item == addWidgetsButton) {
configurationArea.currentApplet = null
@ -158,7 +158,7 @@ MouseArea {
if (!currentApplet) {
return;
}
appletsModel.set(placeHolder.parent.index, {applet: currentApplet.applet})
appletsModel.set((placeHolder.parent as AppletContainer).index, {applet: currentApplet.applet})
let newCurrentApplet = currentApplet.applet.parent
newCurrentApplet.animateFrom(currentApplet.x, currentApplet.y)
newCurrentApplet.dragging = null
@ -266,7 +266,7 @@ MouseArea {
onVisualParentChanged: {
if (visualParent) {
tooltip.requestActivate()
const thisPlasmoid = configurationArea.currentApplet.applet.plasmoid;
const thisPlasmoid = configurationArea.currentApplet.applet.Plasmoid;
thisPlasmoid.contextualActionsAboutToShow();
alternativesButton.visible = thisPlasmoid.internalAction("alternatives")?.enabled ?? false;
configureButton.visible = thisPlasmoid.internalAction("configure")?.enabled ?? false;
@ -312,11 +312,11 @@ MouseArea {
// we want destructive actions to be far from the initial
// cursor position, so show this on the top unless it's on
// a top panel
visible: configurationArea.currentApplet?.applet.plasmoid.internalAction("remove")?.enabled ?? false
visible: configurationArea.currentApplet?.applet.Plasmoid.internalAction("remove")?.enabled ?? false
icon.name: "edit-delete-remove-symbolic"
text: i18n("Remove")
onClicked: {
configurationArea.currentApplet.applet.plasmoid.internalAction("remove").trigger();
configurationArea.currentApplet.applet.Plasmoid.internalAction("remove").trigger();
configurationArea.currentApplet = null;
}
}
@ -325,9 +325,9 @@ MouseArea {
Layout.fillWidth: true
icon.name: "configure"
text: i18n("Configure…")
visible: configurationArea.currentApplet?.applet.plasmoid.hasConfigurationInterface ?? false
visible: configurationArea.currentApplet?.applet.Plasmoid.hasConfigurationInterface ?? false
onClicked: {
configurationArea.currentApplet.applet.plasmoid.internalAction("configure").trigger();
configurationArea.currentApplet.applet.Plasmoid.internalAction("configure").trigger();
configurationArea.currentApplet = null;
}
}
@ -337,7 +337,7 @@ MouseArea {
icon.name: "widget-alternatives"
text: i18n("Show Alternatives…")
onClicked: {
configurationArea.currentApplet.applet.plasmoid.internalAction("alternatives").trigger();
configurationArea.currentApplet.applet.Plasmoid.internalAction("alternatives").trigger();
// We keep this popup open for a brief interval of time instead
// of re-setting the currentApplet now so that the alternatives
// popup has time to gain focus. The hideTimer will close this
@ -365,22 +365,22 @@ MouseArea {
editable: true
Layout.fillWidth: true
focus: !Kirigami.InputMethod.willShowOnActive
visible: configurationArea.currentApplet?.applet.plasmoid.pluginName === "org.kde.plasma.panelspacer"
&& !configurationArea.currentApplet.applet.plasmoid.configuration.expanding
visible: configurationArea.currentApplet?.applet.Plasmoid.pluginName === "org.kde.plasma.panelspacer"
&& !configurationArea.currentApplet.applet.Plasmoid.configuration.expanding
from: 0
stepSize: 10
to: configurationArea.rootWidth
value: configurationArea.currentApplet?.applet.plasmoid.configuration.length ?? 0
value: configurationArea.currentApplet?.applet.Plasmoid.configuration.length ?? 0
onValueModified: {
configurationArea.currentApplet.applet.plasmoid.configuration.length = value
configurationArea.currentApplet.applet.Plasmoid.configuration.length = value
}
}
PlasmaComponents3.Switch {
text: i18nc("@option:check Whether to allow a spacer widget to fill available space", "Flexible size")
visible: configurationArea.currentApplet?.applet.plasmoid.pluginName === "org.kde.plasma.panelspacer"
checked: configurationArea.currentApplet?.applet.plasmoid.configuration.expanding ?? false
onToggled: configurationArea.currentApplet.applet.plasmoid.configuration.expanding = checked
visible: configurationArea.currentApplet?.applet.Plasmoid?.pluginName === "org.kde.plasma.panelspacer"
checked: configurationArea.currentApplet?.applet.Plasmoid.configuration.expanding ?? false
onToggled: configurationArea.currentApplet.applet.Plasmoid.configuration.expanding = checked
Layout.fillWidth: true
Layout.bottomMargin: Kirigami.Units.smallSpacing
}

View file

@ -201,14 +201,15 @@ ContainmentItem {
* without dragEnter, and in this case parent.index is undefined, so also
* check if dndSpacer is in appletsModel.
*/
if (typeof(dndSpacer.parent.index) === "number" && dndSpacer.parent.index > -1) {
appletsModel.remove(dndSpacer.parent.index);
const spacerContainer = dndSpacer.parent as AppletContainer
if (typeof(spacerContainer?.index) === "number" && spacerContainer.index > -1) {
appletsModel.remove(spacerContainer.index);
root.fixedWidth = root.fixedHeight = 0;
}
}
onDrop: event => {
appletsModel.remove(dndSpacer.parent.index);
appletsModel.remove((dndSpacer.parent as AppletContainer).index);
root.processMimeData(event.mimeData, event.x, event.y);
event.accept(event.proposedAction);
root.fixedWidth = root.fixedHeight = 0;
@ -216,31 +217,20 @@ ContainmentItem {
//BEGIN components
Component {
id: appletContainerComponent
// This loader conditionally manages the BusyIndicator, it's not
// loading the applet. The applet becomes a regular child item.
Loader {
AppletContainer {
id: container
required property Item applet
required property int index
property Item dragging
property bool isAppletContainer: true
property bool isMarginSeparator: ((applet.plasmoid?.constraintHints & Plasmoid.MarginAreasSeparator) == Plasmoid.MarginAreasSeparator)
property int appletIndex: index // To make sure it's always readable even inside other models
property bool inThickArea: false
visible: applet.plasmoid?.status !== PlasmaCore.Types.HiddenStatus || (!Plasmoid.immutable && Plasmoid.userConfiguring) || Containment.corona.editMode;
//when the applet moves caused by its resize, don't animate.
//this is completely heuristic, but looks way less "jumpy"
property bool movingForResize: false
function getMargins(side, returnAllMargins = false, overrideFillArea = null, overrideThickArea = null): real {
if (!applet || !applet.plasmoid) {
if (!applet || !applet.Plasmoid) {
return 0;
}
//Margins are either the size of the margins in the SVG, unless that prevents the panel from being at least half a smallMedium icon + smallSpace) tall at which point we set the margin to whatever allows it to be that...or if it still won't fit, 1.
let fillArea = overrideFillArea === null ? applet && (applet.plasmoid.constraintHints & Plasmoid.CanFillArea) : overrideFillArea
let fillArea = overrideFillArea === null ? applet && (applet.Plasmoid.constraintHints & Plasmoid.CanFillArea) : overrideFillArea
let inThickArea = overrideThickArea === null ? container.inThickArea : overrideThickArea
var layout = {
top: root.isHorizontal, bottom: root.isHorizontal,
@ -259,8 +249,7 @@ ContainmentItem {
// https://bugs.kde.org/show_bug.cgi?id=473420
Layout.fillWidth: true
Layout.fillHeight: true
property bool wantsToFillWidth: applet?.Layout.fillWidth
property bool wantsToFillHeight: applet?.Layout.fillHeight
onWantsToFillWidthChanged: root.checkLastSpacer()
onWantsToFillHeightChanged: root.checkLastSpacer()
@ -312,7 +301,7 @@ ContainmentItem {
}
elementId: fill ? 'fill' : (root.isHorizontal ? side + (container.inThickArea ? 'left' : 'right') : (container.inThickArea ? 'top' : 'bottom') + side)
svg: dropArea.marginHighlightSvg
anchors {top: parent.top; left: parent.left; right: parent.right; bottom: parent.bottom}
anchors {top: parent?.top; left: parent?.left; right: parent?.right; bottom: parent?.bottom}
}
Repeater {
model: ['top', 'bottom', 'right', 'left']