/*SPDX-FileCopyrightText: 2011 Martin GrΓ€Γlin <mgraesslin@kde.org>SPDX-FileCopyrightText: 2012 Gregor Taetzner <gregor@freenet.de>SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org>SPDX-FileCopyrightText: 2013 David Edmundson <davidedmundson@kde.org>SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>SPDX-FileCopyrightText: 2021 Mikel Johnson <mikel5764@gmail.com>SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>SPDX-License-Identifier: GPL-2.0-or-later*/pragma ComponentBehavior: Boundimport QtQuickimport QtQuick.Layoutsimport org.kde.plasma.plasmoidimport org.kde.plasma.core as PlasmaCoreimport org.kde.ksvg as KSvgimport org.kde.plasma.components as PC3import org.kde.plasma.private.kicker as Kickerimport org.kde.kirigami as Kirigamiimport "code/tools.js" as ToolsPlasmoidItem {id: kickoffwidth: Kirigami.Units.iconSizes.hugeheight: Kirigami.Units.iconSizes.hugeswitchWidth: fullRepresentationItem ? fullRepresentationItem.Layout.minimumWidth : Kirigami.Units.iconSizes.huge * 10switchHeight: fullRepresentationItem ? fullRepresentationItem.Layout.minimumHeight : Kirigami.Units.iconSizes.huge * 10// The properties are defined here instead of the singleton because each// instance of Kickoff requires different instances of these propertiesreadonly property bool inPanel: [PlasmaCore.Types.TopEdge,PlasmaCore.Types.RightEdge,PlasmaCore.Types.BottomEdge,PlasmaCore.Types.LeftEdge,].includes(Plasmoid.location)readonly property bool vertical: Plasmoid.formFactor === PlasmaCore.Types.Vertical// Used to prevent the width from changing frequently when the scrollbar appears or disappearsreadonly property bool mayHaveGridWithScrollBar: Plasmoid.configuration.applicationsDisplay === 0|| (Plasmoid.configuration.favoritesDisplay === 0 && kickoff.rootModel.favoritesModel.count > minimumGridRowCount * minimumGridRowCount)//BEGIN Modelsreadonly property Kicker.RootModel rootModel: Kicker.RootModel {autoPopulate: false// TODO: appletInterface property now can be ported to "applet" and have the real Applet* assigned directlyappletInterface: kickoffappNameFormat: Plasmoid.configuration.appNameFormatflat: true // have categories, but no subcategoriessorted: Plasmoid.configuration.alphaSortshowSeparators: !sorted && Plasmoid.configuration.applicationsDisplay != 0showRootSeparator: trueshowTopLevelItems: trueshowAllApps: trueshowAllAppsCategorized: falseshowRecentApps: falseshowRecentDocs: falseshowPowerSession: falseshowFavoritesPlaceholder: truehighlightNewlyInstalledApps: Plasmoid.configuration.highlightNewlyInstalledAppsComponent.onCompleted: {favoritesModel.initForClient("org.kde.plasma.kickoff.favorites.instance-" + Plasmoid.id)if (!Plasmoid.configuration.favoritesPortedToKAstats) {if (favoritesModel.count < 1) {favoritesModel.portOldFavorites(Plasmoid.configuration.favorites);}Plasmoid.configuration.favoritesPortedToKAstats = true;}}}readonly property Kicker.RunnerModel runnerModel: Kicker.RunnerModel {query: kickoff.searchField ? kickoff.searchField.text : ""onRequestUpdateQuery: query => {if (kickoff.searchField) {kickoff.searchField.text = query;}}appletInterface: kickoffmergeResults: truefavoritesModel: kickoff.rootModel.favoritesModel}readonly property Kicker.ComputerModel computerModel: Kicker.ComputerModel {appletInterface: kickofffavoritesModel: kickoff.rootModel.favoritesModelsystemApplications: Plasmoid.configuration.systemApplicationsComponent.onCompleted: {//systemApplications = Plasmoid.configuration.systemApplications;}}readonly property alias recentUsageModel: recentUsageModelKicker.RecentUsageModel {id: recentUsageModelfavoritesModel: kickoff.rootModel.favoritesModel}readonly property alias frequentUsageModel: frequentUsageModelKicker.RecentUsageModel {id: frequentUsageModelfavoritesModel: kickoff.rootModel.favoritesModelordering: 1 // Popular / Frequently Used}//END//BEGIN UI elements// Set in FullRepresentation.qmlproperty Item header: null// Set in Header.qml// QTBUG Using PC3.TextField as type makes assignment fail// "Cannot assign QObject* to TextField_QMLTYPE_8*"property Item searchField: null// Set in FullRepresentation.qml, ApplicationPage.qml, PlacesPage.qmlproperty Item sideBar: null // is null when searchingproperty Item contentArea: null // is searchView when searching// Set in NormalPage.qmlproperty Item footer: null// True when central pane (and header) LayoutMirroring diverges from global// LayoutMirroring, in order to achieve the desired sidebar positionreadonly property bool paneSwap: Plasmoid.configuration.paneSwapreadonly property bool sideBarOnRight: (Application.layoutDirection == Qt.RightToLeft) != paneSwap// References to items according to their focus chain orderreadonly property Item firstHeaderItem: header ? (paneSwap ? header.pinButton : header.avatar) : nullreadonly property Item lastHeaderItem: header ? (paneSwap ? header.avatar : header.pinButton) : nullreadonly property Item firstCentralPane: paneSwap ? contentArea : sideBarreadonly property Item lastCentralPane: paneSwap ? sideBar : contentAreareadonly property Item dragSource: Item {id: dragSource // BUG 449426property Item sourceItemDrag.dragType: Drag.AutomaticDrag.supportedActions: Qt.CopyAction | Qt.LinkAction | Qt.MoveAction}//END//BEGIN Metricsreadonly property KSvg.FrameSvgItem backgroundMetrics: KSvg.FrameSvgItem {// Inset defaults to a negative value when not set by margin hintsreadonly property real leftPadding: Math.round(margins.left - Math.max(inset.left, 0))readonly property real rightPadding: Math.round(margins.right - Math.max(inset.right, 0))readonly property real topPadding: Math.round(margins.top - Math.max(inset.top, 0))readonly property real bottomPadding: Math.round(margins.bottom - Math.max(inset.bottom, 0))readonly property real spacing: Math.round(leftPadding)visible: falseimagePath: Plasmoid.formFactor === PlasmaCore.Types.Planar ? "widgets/background" : "dialogs/background"}// This is here rather than in the singleton with the other metrics items// because the list delegate's height depends on a configuration setting// and the singleton can't access thosereadonly property real listDelegateHeight: listDelegate.heightKickoffListDelegate {id: listDelegatevisible: falseenabled: falsemodel: nullindex: -1text: "asdf"url: ""decoration: "start-here-kde"description: "asdf"action: nullindicator: null}// Used to show smaller Kickoff on small screensreadonly property int minimumGridRowCount: Math.min(Screen.desktopAvailableWidth, Screen.desktopAvailableHeight) * Screen.devicePixelRatio < KickoffSingleton.gridCellSize * 4 + (fullRepresentationItem ? fullRepresentationItem.normalPage.preferredSideBarWidth : KickoffSingleton.gridCellSize * 2) ? 2 : 4//ENDPlasmoid.icon: Plasmoid.configuration.iconpreferredRepresentation: compactRepresentationfullRepresentation: FullRepresentation { focus: true }// Only exists because the default CompactRepresentation doesn't:// - open on drag// - allow defining a custom drop handler// - expose the ability to show text below or beside the icon// TODO remove once it gains those featurescompactRepresentation: MouseArea {id: compactRoot// Taken from DigitalClock to ensure uniform sizing when next to each otherreadonly property bool tooSmall: Plasmoid.formFactor === PlasmaCore.Types.Horizontal && Math.round(2 * (compactRoot.height / 5)) <= Kirigami.Theme.smallFont.pixelSizereadonly property bool shouldHaveIcon: Plasmoid.formFactor === PlasmaCore.Types.Vertical || Plasmoid.icon !== ""readonly property bool shouldHaveLabel: Plasmoid.formFactor !== PlasmaCore.Types.Vertical && Plasmoid.configuration.menuLabel !== ""readonly property int iconSize: Kirigami.Units.iconSizes.largereadonly property var sizing: {const displayedIcon = imageFallback.visible ? imageFallback : (buttonIcon.valid ? buttonIcon : buttonIconFallback);let impWidth = 0;if (shouldHaveIcon) {impWidth += displayedIcon.width;}if (shouldHaveLabel) {impWidth += labelTextField.contentWidth + labelTextField.Layout.leftMargin + labelTextField.Layout.rightMargin;}const impHeight = displayedIcon.height > 0 ? displayedIcon.height : iconSize// at least square, but can be wider/tallerif (kickoff.inPanel) {if (kickoff.vertical) {return {preferredWidth: iconSize,preferredHeight: impHeight};} else { // horizontalreturn {preferredWidth: impWidth,preferredHeight: iconSize};}} else {return {preferredWidth: impWidth,preferredHeight: Kirigami.Units.iconSizes.small,};}}implicitWidth: iconSizeimplicitHeight: iconSizeLayout.preferredWidth: sizing.preferredWidthLayout.preferredHeight: sizing.preferredHeightLayout.minimumWidth: Layout.preferredWidthLayout.minimumHeight: Layout.preferredHeighthoverEnabled: trueproperty bool wasExpandedAccessible.name: Plasmoid.titleAccessible.role: Accessible.ButtononPressed: wasExpanded = kickoff.expandedonClicked: kickoff.expanded = !wasExpandedDropArea {id: compactDragAreaanchors.fill: parentonEntered: drag => {if (drag.hasUrls) {expandOnDragTimer.start()}}onExited: expandOnDragTimer.stop()}Timer {id: expandOnDragTimer// this is an interaction and not an animation, so we want it as a constantinterval: 250onTriggered: kickoff.expanded = true}RowLayout {id: iconLabelRowanchors.fill: parentspacing: 0Kirigami.Icon {id: buttonIconLayout.fillWidth: kickoff.verticalLayout.fillHeight: !kickoff.verticalLayout.preferredWidth: kickoff.vertical ? -1 : height / (implicitHeight / implicitWidth)Layout.preferredHeight: !kickoff.vertical ? -1 : width * (implicitHeight / implicitWidth)Layout.maximumHeight: Kirigami.Units.iconSizes.hugeLayout.maximumWidth: Kirigami.Units.iconSizes.hugeLayout.alignment: Qt.AlignVCenter | Qt.AlignHCentersource: Tools.iconOrDefault(Plasmoid.formFactor, Plasmoid.icon)active: compactRoot.containsMouse || compactDragArea.containsDragroundToIconSize: implicitHeight === implicitWidthvisible: valid && !imageFallback.visible}Kirigami.Icon {id: buttonIconFallback// fallback is assumed to be squareLayout.fillWidth: kickoff.verticalLayout.fillHeight: !kickoff.verticalLayout.preferredWidth: kickoff.vertical ? -1 : heightLayout.preferredHeight: !kickoff.vertical ? -1 : widthLayout.alignment: Qt.AlignVCenter | Qt.AlignHCentersource: buttonIcon.valid ? null : Tools.defaultIconNameactive: compactRoot.containsMouse || compactDragArea.containsDragvisible: !buttonIcon.valid && Plasmoid.icon !== "" && !imageFallback.visible}Image {id: imageFallbackreadonly property bool nonSquareImage: sourceSize.width != sourceSize.heightvisible: nonSquareImage && status == Image.Readysource: Plasmoid.icon.startsWith("/") ? "file:" + Plasmoid.icon.split("/").map(encodeURIComponent).join("/") : ""Layout.fillWidth: kickoff.verticalLayout.fillHeight: !kickoff.verticalLayout.preferredWidth: kickoff.vertical ? -1 : height / (implicitHeight / implicitWidth)Layout.preferredHeight: !kickoff.vertical ? -1 : width * (implicitHeight / implicitWidth)Layout.maximumHeight: kickoff.vertical ? -1 : Kirigami.Units.iconSizes.hugeLayout.maximumWidth: kickoff.vertical ? Kirigami.Units.iconSizes.huge : -1Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenterfillMode: Image.PreserveAspectFitmipmap: true}PC3.Label {id: labelTextFieldLayout.fillHeight: trueLayout.leftMargin: Kirigami.Units.smallSpacingLayout.rightMargin: Kirigami.Units.smallSpacingtext: Plasmoid.configuration.menuLabeltextFormat: Text.StyledTexthorizontalAlignment: Text.AlignLeftverticalAlignment: Text.AlignVCenterwrapMode: Text.NoWrapfontSizeMode: Text.VerticalFitfont.pixelSize: compactRoot.tooSmall ? Kirigami.Theme.defaultFont.pixelSize : Kirigami.Units.iconSizes.roundedIconSize(Kirigami.Units.gridUnit * 2)minimumPointSize: Kirigami.Theme.smallFont.pointSizevisible: compactRoot.shouldHaveLabel}}}Kicker.ProcessRunner {id: processRunner}Plasmoid.contextualActions: [PlasmaCore.Action {text: i18nc("@action:inmenu launches kmenuedit", "Edit Applicationsβ¦")icon.name: "kmenuedit"visible: Plasmoid.immutability !== PlasmaCore.Types.SystemImmutableonTriggered: processRunner.runMenuEditor()}]Component.onCompleted: {Plasmoid.activationTogglesExpanded = true}} // root