Refactor configuration and UI components for improved structure and functionality; add launcher icon configuration and enhance grid layouts.

This commit is contained in:
Calamytryx
2025-10-06 21:51:07 +08:00
parent 5d9f2f5953
commit 874a03b6bb
8 changed files with 816 additions and 228 deletions

View File

@@ -0,0 +1,17 @@
/*
SPDX-FileCopyrightText: 2014 Eike Hein <hein@kde.org>
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"
}
}

View File

@@ -6,8 +6,8 @@
<kcfgfile name=""/>
<group name="General">
<entry name="favoriteApps" type="String">
<default></default>
<entry name="launcherIcon" type="String">
<default>applications-all</default>
</entry>
</group>
</kcfg>

View File

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

View File

@@ -0,0 +1,55 @@
/*
SPDX-FileCopyrightText: 2014 Eike Hein <hein@kde.org>
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
}
}
}

View File

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

View File

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

View File

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

View File

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