From 874a03b6bb02487c8b0399b373c1e09c311708fd Mon Sep 17 00:00:00 2001 From: Calamytryx <91600814+Calamytryx@users.noreply.github.com> Date: Mon, 6 Oct 2025 21:51:07 +0800 Subject: [PATCH] Refactor configuration and UI components for improved structure and functionality; add launcher icon configuration and enhance grid layouts. --- contents/config/config.qml | 17 + contents/config/main.xml | 4 +- contents/ui/CategoryTab.qml | 30 +- contents/ui/ConfigGeneral.qml | 55 +++ contents/ui/InventorySlot.qml | 35 +- contents/ui/StaticFavoriteGrid.qml | 69 +++ contents/ui/StaticGrid.qml | 69 +++ contents/ui/main.qml | 765 ++++++++++++++++++++++------- 8 files changed, 816 insertions(+), 228 deletions(-) create mode 100644 contents/config/config.qml create mode 100644 contents/ui/ConfigGeneral.qml create mode 100644 contents/ui/StaticFavoriteGrid.qml create mode 100644 contents/ui/StaticGrid.qml diff --git a/contents/config/config.qml b/contents/config/config.qml new file mode 100644 index 0000000..5f6eaa4 --- /dev/null +++ b/contents/config/config.qml @@ -0,0 +1,17 @@ +/* + SPDX-FileCopyrightText: 2014 Eike Hein + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +import QtQuick 2.15 + +import org.kde.plasma.configuration 2.0 + +ConfigModel { + ConfigCategory { + name: i18n("General") + icon: "preferences-desktop-plasma" + source: "ConfigGeneral.qml" + } +} diff --git a/contents/config/main.xml b/contents/config/main.xml index 2ebe2ce..b444da0 100644 --- a/contents/config/main.xml +++ b/contents/config/main.xml @@ -6,8 +6,8 @@ - - + + applications-all diff --git a/contents/ui/CategoryTab.qml b/contents/ui/CategoryTab.qml index 23d7952..bbf515e 100644 --- a/contents/ui/CategoryTab.qml +++ b/contents/ui/CategoryTab.qml @@ -12,24 +12,13 @@ Rectangle { signal clicked() - color: isActive ? "#8B8B8B" : "#5B5B5B" - border.color: "#373737" - border.width: Kirigami.Units.devicePixelRatio * 2 + color: isActive ? "#C6C6C6" : "#8B8B8B" + z: isActive ? 9999999 : 0 Behavior on color { ColorAnimation { duration: 100 } } - // Inner highlight - Rectangle { - anchors.fill: parent - anchors.margins: Kirigami.Units.devicePixelRatio - color: "transparent" - border.color: isActive ? "#FFFFFF" : "#7B7B7B" - border.width: Kirigami.Units.devicePixelRatio - opacity: 0.5 - } - // Icon Kirigami.Icon { anchors.centerIn: parent @@ -37,7 +26,7 @@ Rectangle { height: Kirigami.Units.iconSizes.medium source: categoryIcon smooth: true - color: "#FFFFFF" + color: "#000" } // Tooltip @@ -78,19 +67,6 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - - onEntered: { - hovered = true - if (!isActive) { - tab.color = Qt.lighter(tab.color, 1.2) - } - } - onExited: { - hovered = false - if (!isActive) { - tab.color = "#5B5B5B" - } - } onClicked: tab.clicked() } } diff --git a/contents/ui/ConfigGeneral.qml b/contents/ui/ConfigGeneral.qml new file mode 100644 index 0000000..85014f0 --- /dev/null +++ b/contents/ui/ConfigGeneral.qml @@ -0,0 +1,55 @@ +/* + SPDX-FileCopyrightText: 2014 Eike Hein + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import org.kde.kirigami 2.20 as Kirigami +import org.kde.plasma.plasmoid 2.0 +import org.kde.kcmutils as KCM +import org.kde.iconthemes as KIconThemes + +KCM.SimpleKCM { + id: configGeneral + + property string cfg_launcherIcon: Plasmoid.configuration.launcherIcon || "applications-all" + + Kirigami.FormLayout { + anchors.left: parent.left + anchors.right: parent.right + + Button { + id: iconButton + + Kirigami.FormData.label: i18n("Launcher Icon:") + + implicitWidth: Kirigami.Units.iconSizes.large + Kirigami.Units.smallSpacing * 2 + implicitHeight: Kirigami.Units.iconSizes.large + Kirigami.Units.smallSpacing * 2 + + onPressed: iconDialog.open() + + Kirigami.Icon { + anchors.centerIn: parent + width: Kirigami.Units.iconSizes.large + height: width + source: configGeneral.cfg_launcherIcon || "applications-all" + } + + KIconThemes.IconDialog { + id: iconDialog + onIconNameChanged: configGeneral.cfg_launcherIcon = iconName || "applications-all" + } + } + + Label { + text: i18n("Choose an icon for the compact launcher representation.") + font.italic: true + wrapMode: Text.WordWrap + } + + } +} diff --git a/contents/ui/InventorySlot.qml b/contents/ui/InventorySlot.qml index fd1e308..33ebd45 100644 --- a/contents/ui/InventorySlot.qml +++ b/contents/ui/InventorySlot.qml @@ -11,6 +11,7 @@ Item { property string appExec: "" property bool hovered: false property bool isFavorite: false + property bool isFavoriteRow: false signal clicked() signal rightClicked() @@ -22,39 +23,19 @@ Item { Rectangle { id: slotBg anchors.fill: parent - color: hovered ? "#A0A0A0" : "#8B8B8B" - border.color: "#C6C6C6" - border.width: 3 - antialiasing: false // Disable antialiasing for crisp edges + color: "transparent" Behavior on color { ColorAnimation { duration: 100 } } - // Inner rectangle with only top and left borders + // Inner rectangle Rectangle { id: innerBorders anchors.fill: parent anchors.margins: 1 // Small margin to avoid overlap with parent border - color: "transparent" + color: "#8B8B8B" - // Top border - Rectangle { - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: 2 - color: "#2a2a2a" - } - - // Left border - Rectangle { - anchors.top: parent.top - anchors.left: parent.left - anchors.bottom: parent.bottom - width: 2 - color: "#2a2a2a" - } } // App icon Kirigami.Icon { @@ -65,9 +46,9 @@ Item { smooth: true } - // Favorite star indicator + // Favorite star indicator (only show if not in favorite row) Text { - visible: isFavorite + visible: isFavorite && !isFavoriteRow anchors.right: parent.right anchors.top: parent.top anchors.margins: Kirigami.Units.smallSpacing / 2 @@ -81,7 +62,7 @@ Item { id: tooltip visible: hovered && appName !== "" anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.top + anchors.bottom: parent.bottom anchors.bottomMargin: Kirigami.Units.smallSpacing width: tooltipText.width + Kirigami.Units.largeSpacing height: tooltipText.height + Kirigami.Units.smallSpacing @@ -89,7 +70,7 @@ Item { border.color: "#2A2A2A" border.width: Kirigami.Units.devicePixelRatio radius: Kirigami.Units.smallSpacing / 2 - z: 1000 + z: 999999 Rectangle { anchors.fill: parent diff --git a/contents/ui/StaticFavoriteGrid.qml b/contents/ui/StaticFavoriteGrid.qml new file mode 100644 index 0000000..869c8f5 --- /dev/null +++ b/contents/ui/StaticFavoriteGrid.qml @@ -0,0 +1,69 @@ +import QtQuick +import QtQuick.Layouts +import org.kde.kirigami as Kirigami + +Grid { + id: staticFavoriteGrid + property int cellWidth: Kirigami.Units.gridUnit * 3.5 + property int cellHeight: Kirigami.Units.gridUnit * 3.5 + + readonly property int numCols: 10 // Force 10 columns + readonly property int numRows: 1 // Force 1 row + readonly property int totalCells: numCols * numRows + + columns: numCols + z: 9999999 + + Repeater { + model: staticFavoriteGrid.totalCells + + Item { + width: staticFavoriteGrid.cellWidth + height: staticFavoriteGrid.cellHeight + + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: "#C6C6C6" + border.width: 3 + antialiasing: false + + Rectangle { + anchors.fill: parent + anchors.margins: 1 + color: "transparent" + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#2a2a2a" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + } + } + } + } +} diff --git a/contents/ui/StaticGrid.qml b/contents/ui/StaticGrid.qml new file mode 100644 index 0000000..7f41140 --- /dev/null +++ b/contents/ui/StaticGrid.qml @@ -0,0 +1,69 @@ +import QtQuick +import QtQuick.Layouts +import org.kde.kirigami as Kirigami + +Grid { + id: staticGrid + property int cellWidth: Kirigami.Units.gridUnit * 3.5 + property int cellHeight: Kirigami.Units.gridUnit * 3.5 + + readonly property int numCols: 10 // Force 10 columns + readonly property int numRows: 5 // Force 5 rows + readonly property int totalCells: numCols * numRows + + columns: numCols + z: 9999999 + + Repeater { + model: staticGrid.totalCells + + Item { + width: staticGrid.cellWidth + height: staticGrid.cellHeight + + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: "transparent" + border.width: 3 + antialiasing: false + + Rectangle { + anchors.fill: parent + anchors.margins: 1 + color: "transparent" + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#2a2a2a" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + } + } + } + } +} diff --git a/contents/ui/main.qml b/contents/ui/main.qml index 61fee17..515d813 100644 --- a/contents/ui/main.qml +++ b/contents/ui/main.qml @@ -8,15 +8,581 @@ import org.kde.plasma.plasma5support as Plasma5Support import org.kde.ksvg as KSvg import org.kde.kirigami as Kirigami +import org.kde.plasma.private.kicker 0.1 as Kicker +import org.kde.plasma.plasma5support 2.0 as P5Support + PlasmoidItem { id: root width: Kirigami.Units.gridUnit * 40 height: Kirigami.Units.gridUnit * 35 - Plasmoid.backgroundHints: PlasmaCore.Types.DefaultBackground + Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground - preferredRepresentation: fullRepresentation + preferredRepresentation: compactRepresentation + + compactRepresentation: MouseArea { + width: Kirigami.Units.gridUnit * 1.5 + height: Kirigami.Units.gridUnit * 1.5 + + Kirigami.Icon { + anchors.centerIn: parent + width: plasmoid.configuration.launcherIconSize || Kirigami.Units.iconSizes.large + height: plasmoid.configuration.launcherIconSize || Kirigami.Units.iconSizes.large + source: plasmoid.configuration.launcherIcon || "~/.local/share/plasma/plasmoids/mc_inventory/grass_block.png" + } + + onClicked: root.expanded = !root.expanded + } + + fullRepresentation: Item { + x: plasmoid.screenGeometry.x + (plasmoid.screenGeometry.width - width) / 2 + y: plasmoid.screenGeometry.y + (plasmoid.screenGeometry.height - height) / 2 + + + Layout.preferredWidth: (backgroundRect.width) + Layout.preferredHeight: (topCategoriesRow.height + bottomCategoriesRow.height + backgroundRect.height) // Dynamic height based on content + Layout.minimumWidth: Kirigami.Units.gridUnit * 30 + Layout.minimumHeight: Kirigami.Units.gridUnit * 19 // Adjusted minimum accordingly + + // Top categories outside the main container + RowLayout { + id: topCategoriesRow + anchors.top: parent.top + anchors.bottomMargin: -2 // Negative margin to overlap the border + anchors.left: parent.left + + height: Kirigami.Units.gridUnit * 3 + spacing: Kirigami.Units.smallSpacing + z: 999999999 + + // Top categories + Repeater { + model: topCategories + + CategoryTab { + Layout.preferredWidth: Kirigami.Units.gridUnit * 3 + Layout.preferredHeight: Kirigami.Units.gridUnit * 3 + categoryId: modelData.id + categoryName: modelData.name + categoryIcon: modelData.icon + isActive: currentCategory === modelData.id + onClicked: currentCategory = modelData.id + + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: "transparent" + border.width: 3 + antialiasing: false + + Rectangle { + anchors.fill: parent + color: "transparent" + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#f7f7f7" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#c6c6c6" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + } + } + } + } + } + + // Minecraft-style background (now smaller, containing only search, inventory, close) + Rectangle { + id: backgroundRect + anchors.top: topCategoriesRow.bottom + anchors.bottom: bottomCategoriesRow.top + anchors.left: parent.left + anchors.right: parent.right + color: "#C6C6C6" + + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: "transparent" + border.width: 3 + antialiasing: false + + Rectangle { + anchors.fill: parent + color: "transparent" + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#f7f7f7" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#2a2a2a" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + } + } + + + ColumnLayout { + anchors.fill: parent + anchors.margins: Kirigami.Units.smallSpacing * 2 + spacing: Kirigami.Units.smallSpacing + + // Search row inside the container + RowLayout { + Layout.fillWidth: true + spacing: Kirigami.Units.smallSpacing + + Item { Layout.fillWidth: true } + + // Search button/field + Rectangle { + Layout.preferredWidth: Kirigami.Units.gridUnit * 9 + Layout.preferredHeight: Kirigami.Units.gridUnit * 3 + color: "#8B8B8B" + border.color: "#373737" + border.width: Kirigami.Units.devicePixelRatio * 2 + + TextField { + id: searchField + anchors.fill: parent + anchors.margins: Kirigami.Units.smallSpacing + placeholderText: "🔍 Search" + background: Rectangle { + color: "#000000" + opacity: 0.4 + } + color: "#FFFFFF" + font.pixelSize: searchField.height * 0.4 + horizontalAlignment: Text.AlignHLeft + onTextChanged: searchText = text + } + } + } + + // Inventory grid with custom scrollbar + Rectangle { + id: inventoryContainer + Layout.fillWidth: true + Layout.fillHeight: true + color: "#c6c6c6" + + readonly property int scrollbarWidth: Kirigami.Units.gridUnit * 3.5 * 0.75 + readonly property int separatorWidth: Kirigami.Units.gridUnit * 3.5 * 0.25 + readonly property int gridAreaWidth: width - scrollbarWidth - separatorWidth + readonly property int favoriteBarHeight: Kirigami.Units.gridUnit * 3.5 + readonly property int mainGridHeight: Kirigami.Units.gridUnit * 3.5 * 5 + + StaticGrid { + id: staticGridDisplay + width: inventoryContainer.gridAreaWidth + height: inventoryContainer.mainGridHeight + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: Kirigami.Units.smallSpacing + } + + GridView { + id: gridView + width: inventoryContainer.gridAreaWidth + height: inventoryContainer.mainGridHeight + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: Kirigami.Units.smallSpacing + clip: true + + model: getFilteredApps() + + cellWidth: Kirigami.Units.gridUnit * 3.5 + cellHeight: Kirigami.Units.gridUnit * 3.5 + + // Force 10 columns + property int columns: 10 + + interactive: false // Disable user interaction on the grid itself + + // Make any change to contentY instantaneous + Behavior on contentY { NumberAnimation { duration: 0 } } + + delegate: InventorySlot { + appName: modelData.name + appIcon: modelData.icon + appExec: modelData.desktop + isFavorite: root.isFavorite(modelData.desktop) + isFavoriteRow: false + onClicked: launchApp(appExec) + onRightClicked: root.toggleFavorite(appExec) + } + } + + MouseArea { + width: gridView.width + height: gridView.height + anchors.left: gridView.left + anchors.top: gridView.top + acceptedButtons: Qt.NoButton // Only interested in wheel events + onWheel: { + let newY = gridView.contentY + if (wheel.angleDelta.y < 0) { // Scroll down + newY += gridView.cellHeight + } else if (wheel.angleDelta.y > 0) { // Scroll up + newY -= gridView.cellHeight + } + // Clamp the position to prevent overscrolling + gridView.contentY = Math.max(0, Math.min(newY, gridView.contentHeight - gridView.height)) + } + } + + // Static favorite grid background + StaticFavoriteGrid { + width: inventoryContainer.gridAreaWidth + height: inventoryContainer.favoriteBarHeight + anchors.left: gridView.left + anchors.top: gridView.bottom + anchors.topMargin: ((gridView.height / 5) / 4 ) + } + + // Favorite app bar + Grid { + id: favoriteBar + width: inventoryContainer.gridAreaWidth + height: inventoryContainer.favoriteBarHeight + anchors.left: gridView.left + anchors.top: gridView.bottom + anchors.topMargin: ((gridView.height / 5) / 4 ) + columns: 10 + + Repeater { + model: 10 + + InventorySlot { + property var favoriteApp: index < favoriteApps.length ? getFavoriteAppData(favoriteApps[index]) : null + + appName: favoriteApp ? favoriteApp.name : "" + appIcon: favoriteApp ? favoriteApp.icon : "" + appExec: favoriteApp ? favoriteApp.desktop : "" + isFavorite: true + isFavoriteRow: true + onClicked: { + if (appExec) launchApp(appExec) + } + onRightClicked: { + if (appExec) root.toggleFavorite(appExec) + } + } + } + } + + // Separator wall + Rectangle { + id: separatorWall + width: ((gridView.height / 5) / 4 ) + height: parent.height + anchors.right: scrollbarArea.left + anchors.left: gridView.right + color: "#C6C6C6" + } + + // Custom Scrollbar + Rectangle { + id: scrollbarArea + width: inventoryContainer.scrollbarWidth + height: parent.height - 8 + y: 4 + anchors.right: parent.right + color: "#8b8b8b" + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#2a2a2a" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + + Rectangle { + id: scrollbarHandle + width: parent.width * 0.9 + height: Kirigami.Units.gridUnit * 3.5 + color: "#5B5B5B" + anchors.horizontalCenter: parent.horizontalCenter + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#f7f7f7" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#2a2a2a" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + + // Calculate Y position based on GridView's scroll + y: { + let scrollableHeight = gridView.contentHeight - gridView.height + let handleScrollableHeight = scrollbarArea.height - height + if (scrollableHeight <= 0) return 0 + return (gridView.contentY / scrollableHeight) * handleScrollableHeight + } + + MouseArea { + anchors.fill: parent + drag.target: parent + drag.axis: Drag.YAxis + drag.minimumY: 0 + drag.maximumY: scrollbarArea.height - parent.height + + onPositionChanged: { + let scrollableHeight = gridView.contentHeight - gridView.height + let handleScrollableHeight = scrollbarArea.height - parent.height + if (handleScrollableHeight <= 0) return + + // Calculate the scroll ratio from the handle's drag position + let scrollRatio = parent.y / handleScrollableHeight + + // Calculate the ideal, continuous content position + let continuousContentY = scrollRatio * scrollableHeight + + // Snap the content position to the nearest cell row + let snappedContentY = Math.round(continuousContentY / gridView.cellHeight) * gridView.cellHeight + + // Apply the snapped position to the GridView + gridView.contentY = snappedContentY + } + } + } + } + } + } + } + + // Bottom categories outside the main container + RowLayout { + id: bottomCategoriesRow + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: Kirigami.Units.gridUnit * 3 + spacing: Kirigami.Units.smallSpacing + + // Bottom categories + Repeater { + model: bottomCategories + + CategoryTab { + Layout.preferredWidth: Kirigami.Units.gridUnit * 3 + Layout.preferredHeight: Kirigami.Units.gridUnit * 3 + categoryId: modelData.id + categoryName: modelData.name + categoryIcon: modelData.icon + isActive: currentCategory === modelData.id + onClicked: currentCategory = modelData.id + + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: "transparent" + border.width: 3 + antialiasing: false + + Rectangle { + anchors.fill: parent + color: "transparent" + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#c6c6c6" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#2a2a2a" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + } + } + + } + } + + Item { Layout.fillWidth: true } + + // Close button + Rectangle { + Layout.preferredWidth: Kirigami.Units.gridUnit * 3 + Layout.preferredHeight: Kirigami.Units.gridUnit * 3 + color: "#8B8B8B" + border.color: "#373737" + border.width: Kirigami.Units.devicePixelRatio * 2 + + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: "transparent" + border.width: 3 + antialiasing: false + + Rectangle { + anchors.fill: parent + color: "transparent" + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "transparent" + } + + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: 2 + color: "#2a2a2a" + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 + color: "#f7f7f7" + } + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 2 + color: "#f7f7f7" + } + } + } + + Text { + anchors.centerIn: parent + text: "✕" + color: "#FFFFFF" + font.pixelSize: Kirigami.Units.gridUnit + font.bold: true + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: root.expanded = false + onEntered: parent.color = "#A0A0A0" + onExited: parent.color = "#8B8B8B" + } + } + } + } // Data source for applications Plasma5Support.DataSource { @@ -76,6 +642,22 @@ PlasmoidItem { return favoriteApps.indexOf(exec) !== -1 } + function getFavoriteAppData(desktopFile) { + if (!desktopFile) return null + + for (let source in appsSource.data) { + let appData = appsSource.data[source] + if ((appData.storageId || source) === desktopFile) { + return { + name: appData.name || appData.genericName || source, + icon: appData.iconName || "application-x-executable", + desktop: desktopFile + } + } + } + return null + } + function getFilteredApps() { let apps = [] let seenDesktopFiles = {} @@ -135,6 +717,15 @@ PlasmoidItem { } } } + // Always ensure we have exactly 50 items (5 rows × 10 columns) + while (apps.length < 50) { + apps.push({ + name: "", + icon: "", + desktop: "" + }) + } + return apps } @@ -150,174 +741,4 @@ PlasmoidItem { disconnectSource(sourceName) } } - - fullRepresentation: Item { - Layout.preferredWidth: Kirigami.Units.gridUnit * 40 - Layout.preferredHeight: Kirigami.Units.gridUnit * 35 - Layout.minimumWidth: Kirigami.Units.gridUnit * 30 - Layout.minimumHeight: Kirigami.Units.gridUnit * 25 - - // Minecraft-style background - Rectangle { - anchors.fill: parent - color: "#C6C6C6" - border.color: "#373737" - border.width: Kirigami.Units.devicePixelRatio * 2 - - // Inner shadow effect - Rectangle { - anchors.fill: parent - anchors.margins: Kirigami.Units.devicePixelRatio * 2 - color: "transparent" - border.color: "#FFFFFF" - border.width: Kirigami.Units.devicePixelRatio - opacity: 0.3 - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: Kirigami.Units.smallSpacing * 2 - spacing: Kirigami.Units.smallSpacing - - // Top row: categories + search - RowLayout { - Layout.fillWidth: true - spacing: Kirigami.Units.smallSpacing - - // Top categories - Repeater { - model: topCategories - - CategoryTab { - Layout.preferredWidth: Kirigami.Units.gridUnit * 3 - Layout.preferredHeight: Kirigami.Units.gridUnit * 3 - categoryId: modelData.id - categoryName: modelData.name - categoryIcon: modelData.icon - isActive: currentCategory === modelData.id - onClicked: currentCategory = modelData.id - } - } - - Item { Layout.fillWidth: true } - - // Search button/field - Rectangle { - Layout.preferredWidth: Kirigami.Units.gridUnit * 9 - Layout.preferredHeight: Kirigami.Units.gridUnit * 3 - color: "#8B8B8B" - border.color: "#373737" - border.width: Kirigami.Units.devicePixelRatio * 2 - - TextField { - id: searchField - anchors.fill: parent - anchors.margins: Kirigami.Units.smallSpacing - placeholderText: "🔍 Search" - background: Rectangle { - color: "#000000" - opacity: 0.4 - } - color: "#FFFFFF" - font.pixelSize: Kirigami.Theme.defaultFont.pixelSize - horizontalAlignment: Text.AlignHLeft - onTextChanged: searchText = text - } - } - } - - // Inventory grid - Rectangle { - Layout.fillWidth: true - Layout.fillHeight: true - color: "#8B8B8B" - border.color: "#373737" - border.width: 2 - - ScrollView { - id: scrollView - anchors.fill: parent - anchors.margins: Kirigami.Units.smallSpacing - clip: true - - Flow { - width: scrollView.availableWidth - spacing: 0 - - Repeater { - model: getFilteredApps() - - InventorySlot { - appName: modelData.name - appIcon: modelData.icon - appExec: modelData.desktop - isFavorite: root.isFavorite(modelData.desktop) - onClicked: launchApp(appExec) - onRightClicked: root.toggleFavorite(appExec) - } - } - } - } - } - - // Bottom row: other categories + close - RowLayout { - Layout.fillWidth: true - spacing: Kirigami.Units.smallSpacing - - // Bottom categories - Repeater { - model: bottomCategories - - CategoryTab { - Layout.preferredWidth: Kirigami.Units.gridUnit * 3 - Layout.preferredHeight: Kirigami.Units.gridUnit * 3 - categoryId: modelData.id - categoryName: modelData.name - categoryIcon: modelData.icon - isActive: currentCategory === modelData.id - onClicked: currentCategory = modelData.id - } - } - - Item { Layout.fillWidth: true } - - // Close button - Rectangle { - Layout.preferredWidth: Kirigami.Units.gridUnit * 3 - Layout.preferredHeight: Kirigami.Units.gridUnit * 3 - color: "#8B8B8B" - border.color: "#373737" - border.width: Kirigami.Units.devicePixelRatio * 2 - - Rectangle { - anchors.fill: parent - anchors.margins: Kirigami.Units.devicePixelRatio - color: "transparent" - border.color: "#7B7B7B" - border.width: Kirigami.Units.devicePixelRatio - opacity: 0.5 - } - - Text { - anchors.centerIn: parent - text: "✕" - color: "#FFFFFF" - font.pixelSize: Kirigami.Units.gridUnit - font.bold: true - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: root.expanded = false - onEntered: parent.color = "#A0A0A0" - onExited: parent.color = "#8B8B8B" - } - } - } - } - } - } } \ No newline at end of file