1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-28 23:42:51 -05:00

Spotlight initial version

This commit is contained in:
bbedward
2025-07-11 18:10:27 -04:00
parent 75cb1ead2f
commit 9e34c6fdfa
5 changed files with 735 additions and 64 deletions

View File

@@ -8,6 +8,7 @@ Singleton {
property int themeIndex: 0 property int themeIndex: 0
property bool themeIsDynamic: false property bool themeIsDynamic: false
property var recentlyUsedApps: []
readonly property string configDir: Qt.resolvedUrl("file://" + Quickshell.env("HOME") + "/.config/DankMaterialDark") readonly property string configDir: Qt.resolvedUrl("file://" + Quickshell.env("HOME") + "/.config/DankMaterialDark")
readonly property string configFile: configDir + "/settings.json" readonly property string configFile: configDir + "/settings.json"
@@ -56,7 +57,8 @@ Singleton {
var settings = JSON.parse(content) var settings = JSON.parse(content)
themeIndex = settings.themeIndex !== undefined ? settings.themeIndex : 0 themeIndex = settings.themeIndex !== undefined ? settings.themeIndex : 0
themeIsDynamic = settings.themeIsDynamic !== undefined ? settings.themeIsDynamic : false themeIsDynamic = settings.themeIsDynamic !== undefined ? settings.themeIsDynamic : false
console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic) recentlyUsedApps = settings.recentlyUsedApps || []
console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "recentApps:", recentlyUsedApps.length)
} else { } else {
console.log("Settings file is empty") console.log("Settings file is empty")
} }
@@ -78,14 +80,15 @@ Singleton {
function saveSettings() { function saveSettings() {
var settings = { var settings = {
themeIndex: themeIndex, themeIndex: themeIndex,
themeIsDynamic: themeIsDynamic themeIsDynamic: themeIsDynamic,
recentlyUsedApps: recentlyUsedApps
} }
var content = JSON.stringify(settings, null, 2) var content = JSON.stringify(settings, null, 2)
writeProcess.command = ["sh", "-c", "echo '" + content + "' > '" + Quickshell.env("HOME") + "/.config/DankMaterialDark/settings.json'"] writeProcess.command = ["sh", "-c", "echo '" + content + "' > '" + Quickshell.env("HOME") + "/.config/DankMaterialDark/settings.json'"]
writeProcess.running = true writeProcess.running = true
console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic) console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "recentApps:", recentlyUsedApps.length)
} }
function applyStoredTheme() { function applyStoredTheme() {
@@ -108,4 +111,30 @@ Singleton {
themeIsDynamic = isDynamic themeIsDynamic = isDynamic
saveSettings() saveSettings()
} }
function addRecentApp(app) {
var existingIndex = -1
for (var i = 0; i < recentlyUsedApps.length; i++) {
if (recentlyUsedApps[i].exec === app.exec) {
existingIndex = i
break
}
}
if (existingIndex >= 0) {
recentlyUsedApps.splice(existingIndex, 1)
}
recentlyUsedApps.unshift(app)
if (recentlyUsedApps.length > 10) {
recentlyUsedApps = recentlyUsedApps.slice(0, 10)
}
saveSettings()
}
function getRecentApps() {
return recentlyUsedApps
}
} }

View File

@@ -213,11 +213,13 @@ PanelWindow {
spacing: activeTheme.spacingL spacing: activeTheme.spacingL
// Title and actions // Title and actions
Row { Item {
width: parent.width width: parent.width
height: 40 height: 40
Text { Text {
id: titleText
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: "Clipboard History" text: "Clipboard History"
font.pixelSize: activeTheme.fontSizeLarge + 4 font.pixelSize: activeTheme.fontSizeLarge + 4
@@ -225,69 +227,67 @@ PanelWindow {
color: activeTheme.surfaceText color: activeTheme.surfaceText
} }
Item { Row {
width: parent.width - 180 - (clearAllButton.visible ? 48 : 0) anchors.right: parent.right
height: 1
}
// Clear all button
Rectangle {
id: clearAllButton
width: 40
height: 32
radius: activeTheme.cornerRadius
color: clearArea.containsMouse ? Qt.rgba(activeTheme.error.r, activeTheme.error.g, activeTheme.error.b, 0.12) : "transparent"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: clipboardModel.count > 0 spacing: activeTheme.spacingS
Text { // Clear all button
anchors.centerIn: parent Rectangle {
text: "delete_sweep" id: clearAllButton
font.family: activeTheme.iconFont width: 40
font.pixelSize: activeTheme.iconSize height: 32
color: clearArea.containsMouse ? activeTheme.error : activeTheme.surfaceText radius: activeTheme.cornerRadius
color: clearArea.containsMouse ? Qt.rgba(activeTheme.error.r, activeTheme.error.g, activeTheme.error.b, 0.12) : "transparent"
visible: clipboardModel.count > 0
Text {
anchors.centerIn: parent
text: "delete_sweep"
font.family: activeTheme.iconFont
font.pixelSize: activeTheme.iconSize
color: clearArea.containsMouse ? activeTheme.error : activeTheme.surfaceText
}
MouseArea {
id: clearArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: clearAll()
}
Behavior on color {
ColorAnimation { duration: activeTheme.shortDuration }
}
} }
MouseArea { // Close button
id: clearArea Rectangle {
anchors.fill: parent width: 40
hoverEnabled: true height: 32
cursorShape: Qt.PointingHandCursor radius: activeTheme.cornerRadius
onClicked: clearAll() color: closeArea.containsMouse ? Qt.rgba(activeTheme.error.r, activeTheme.error.g, activeTheme.error.b, 0.12) : "transparent"
}
Text {
Behavior on color { anchors.centerIn: parent
ColorAnimation { duration: activeTheme.shortDuration } text: "close"
} font.family: activeTheme.iconFont
} font.pixelSize: activeTheme.iconSize
color: closeArea.containsMouse ? activeTheme.error : activeTheme.surfaceText
// Close button }
Rectangle {
width: 40 MouseArea {
height: 32 id: closeArea
radius: activeTheme.cornerRadius anchors.fill: parent
color: closeArea.containsMouse ? Qt.rgba(activeTheme.error.r, activeTheme.error.g, activeTheme.error.b, 0.12) : "transparent" hoverEnabled: true
anchors.verticalCenter: parent.verticalCenter cursorShape: Qt.PointingHandCursor
anchors.rightMargin: 4 onClicked: clipboardHistory.hide()
}
Text {
anchors.centerIn: parent Behavior on color {
text: "close" ColorAnimation { duration: activeTheme.shortDuration }
font.family: activeTheme.iconFont }
font.pixelSize: activeTheme.iconSize
color: closeArea.containsMouse ? activeTheme.error : activeTheme.surfaceText
}
MouseArea {
id: closeArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: clipboardHistory.hide()
}
Behavior on color {
ColorAnimation { duration: activeTheme.shortDuration }
} }
} }
} }
@@ -614,6 +614,7 @@ PanelWindow {
if (exitCode === 0) { if (exitCode === 0) {
clipboardHistory.clipboardEntries = [] clipboardHistory.clipboardEntries = []
clipboardModel.clear() clipboardModel.clear()
updateFilteredModel()
} }
} }
} }
@@ -623,4 +624,26 @@ PanelWindow {
hide() hide()
} }
} }
IpcHandler {
target: "clipboard"
function open() {
console.log("ClipboardHistory: IPC open() called")
clipboardHistory.show()
return "CLIPBOARD_OPEN_SUCCESS"
}
function close() {
console.log("ClipboardHistory: IPC close() called")
clipboardHistory.hide()
return "CLIPBOARD_CLOSE_SUCCESS"
}
function toggle() {
console.log("ClipboardHistory: IPC toggle() called")
clipboardHistory.toggle()
return "CLIPBOARD_TOGGLE_SUCCESS"
}
}
} }

View File

@@ -0,0 +1,614 @@
import QtQuick
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Io
import "../Common"
PanelWindow {
id: spotlightLauncher
property bool spotlightOpen: false
property var currentApp: ({})
property var allApps: []
property var recentApps: []
property var filteredApps: []
property int selectedIndex: 0
property int maxResults: 12
property var categories: ["All"]
property string selectedCategory: "All"
property var appCategories: ({
"AudioVideo": "Media",
"Audio": "Media",
"Video": "Media",
"Development": "Development",
"TextEditor": "Development",
"Education": "Education",
"Game": "Games",
"Graphics": "Graphics",
"Network": "Internet",
"Office": "Office",
"Science": "Science",
"Settings": "Settings",
"System": "System",
"Utility": "Utilities"
})
anchors {
top: true
left: true
right: true
bottom: true
}
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: spotlightOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
WlrLayershell.namespace: "quickshell-spotlight"
visible: spotlightOpen
onVisibleChanged: {
console.log("SpotlightLauncher visibility changed to:", visible)
}
color: "transparent"
function show() {
console.log("SpotlightLauncher: show() called")
spotlightOpen = true
console.log("SpotlightLauncher: spotlightOpen set to", spotlightOpen)
loadRecentApps()
updateFilteredApps()
Qt.callLater(function() {
searchField.forceActiveFocus()
searchField.selectAll()
})
}
function hide() {
spotlightOpen = false
searchField.text = ""
selectedIndex = 0
selectedCategory = "All"
updateFilteredApps()
}
function toggle() {
if (spotlightOpen) {
hide()
} else {
show()
}
}
function loadRecentApps() {
recentApps = Prefs.getRecentApps()
}
function updateFilteredApps() {
filteredApps = []
selectedIndex = 0
var apps = allApps
// Filter by category first
if (selectedCategory !== "All") {
apps = apps.filter(app => {
return app.categories.some(cat => appCategories[cat] === selectedCategory)
})
}
if (searchField.text.length === 0) {
// Show recent apps first, then all apps, limited to maxResults
var combined = []
// Add recent apps first
recentApps.forEach(recentApp => {
var found = apps.find(app => app.exec === recentApp.exec)
if (found) {
combined.push(found)
}
})
// Add remaining apps not in recent, sorted alphabetically
var remaining = apps.filter(app => {
return !recentApps.some(recentApp => recentApp.exec === app.exec)
}).sort((a, b) => a.name.localeCompare(b.name))
combined = combined.concat(remaining)
filteredApps = combined.slice(0, maxResults)
} else {
var query = searchField.text.toLowerCase()
var matches = []
for (var i = 0; i < apps.length; i++) {
var app = apps[i]
var name = app.name.toLowerCase()
var comment = (app.comment || "").toLowerCase()
if (name.includes(query) || comment.includes(query)) {
var score = 0
if (name.startsWith(query)) score += 100
if (name.includes(query)) score += 50
if (comment.includes(query)) score += 25
matches.push({
name: app.name,
exec: app.exec,
icon: app.icon,
comment: app.comment,
categories: app.categories,
score: score
})
}
}
matches.sort(function(a, b) { return b.score - a.score })
filteredApps = matches.slice(0, maxResults)
}
filteredModel.clear()
for (var i = 0; i < filteredApps.length; i++) {
filteredModel.append(filteredApps[i])
}
}
function launchApp(app) {
Prefs.addRecentApp(app)
appLauncher.start(app.exec)
hide()
}
function selectNext() {
if (filteredApps.length > 0) {
selectedIndex = (selectedIndex + 1) % filteredApps.length
}
}
function selectPrevious() {
if (filteredApps.length > 0) {
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filteredApps.length - 1
}
}
function launchSelected() {
if (filteredApps.length > 0 && selectedIndex >= 0 && selectedIndex < filteredApps.length) {
launchApp(filteredApps[selectedIndex])
}
}
ListModel { id: filteredModel }
Process {
id: desktopScanner
command: ["sh", "-c", `
for dir in "/usr/share/applications/" "/usr/local/share/applications/" "$HOME/.local/share/applications/" "/run/current-system/sw/share/applications/"; do
if [ -d "$dir" ]; then
find "$dir" -name "*.desktop" 2>/dev/null | while read file; do
echo "===FILE:$file"
sed -n '/^\\[Desktop Entry\\]/,/^\\[.*\\]/{/^\\[Desktop Entry\\]/d; /^\\[.*\\]/q; /^Name=/p; /^Exec=/p; /^Icon=/p; /^Hidden=/p; /^NoDisplay=/p; /^Categories=/p; /^Comment=/p}' "$file" 2>/dev/null || true
done
fi
done
`]
stdout: SplitParser {
splitMarker: "\n"
onRead: (line) => {
if (line.startsWith("===FILE:")) {
if (currentApp.name && currentApp.exec && !currentApp.hidden && !currentApp.noDisplay) {
allApps.push({
name: currentApp.name,
exec: currentApp.exec,
icon: currentApp.icon || "application-x-executable",
comment: currentApp.comment || "",
categories: currentApp.categories || []
})
}
currentApp = { name: "", exec: "", icon: "", comment: "", categories: [], hidden: false, noDisplay: false }
} else if (line.startsWith("Name=")) {
currentApp.name = line.substring(5)
} else if (line.startsWith("Exec=")) {
currentApp.exec = line.substring(5)
} else if (line.startsWith("Icon=")) {
currentApp.icon = line.substring(5)
} else if (line.startsWith("Comment=")) {
currentApp.comment = line.substring(8)
} else if (line.startsWith("Categories=")) {
currentApp.categories = line.substring(11).split(";").filter(cat => cat.length > 0)
} else if (line === "Hidden=true") {
currentApp.hidden = true
} else if (line === "NoDisplay=true") {
currentApp.noDisplay = true
}
}
}
onExited: {
if (currentApp.name && currentApp.exec && !currentApp.hidden && !currentApp.noDisplay) {
allApps.push({
name: currentApp.name,
exec: currentApp.exec,
icon: currentApp.icon || "application-x-executable",
comment: currentApp.comment || "",
categories: currentApp.categories || []
})
}
// Extract unique categories
var uniqueCategories = new Set(["All"])
allApps.forEach(app => {
app.categories.forEach(cat => {
if (appCategories[cat]) {
uniqueCategories.add(appCategories[cat])
}
})
})
categories = Array.from(uniqueCategories)
console.log("Spotlight: Loaded", allApps.length, "applications with", categories.length, "categories")
if (spotlightOpen) {
updateFilteredApps()
}
}
}
Process {
id: appLauncher
function start(exec) {
var cleanExec = exec.replace(/%[fFuU]/g, "").trim()
console.log("Spotlight: Launching app:", cleanExec)
command = ["setsid", "sh", "-c", cleanExec]
running = true
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.log("Spotlight: Failed to launch application, exit code:", exitCode)
}
}
}
Rectangle {
anchors.fill: parent
color: Qt.rgba(0, 0, 0, 0.4)
opacity: spotlightOpen ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
MouseArea {
anchors.fill: parent
enabled: spotlightOpen
onClicked: hide()
}
}
Rectangle {
id: mainContainer
width: 600
height: Math.min(600, categoryFlow.height + (categoryFlow.visible ? Theme.spacingL : 0) + searchContainer.height + resultsList.height + Theme.spacingXL * 2 + Theme.spacingL)
anchors.centerIn: parent
color: Theme.surfaceContainer
radius: Theme.cornerRadiusXLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 1
transform: Scale {
origin.x: mainContainer.width / 2
origin.y: mainContainer.height / 2
xScale: spotlightOpen ? 1.0 : 0.9
yScale: spotlightOpen ? 1.0 : 0.9
Behavior on xScale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Easing.OutBack
easing.overshoot: 1.1
}
}
Behavior on yScale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Easing.OutBack
easing.overshoot: 1.1
}
}
}
opacity: spotlightOpen ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
layer.enabled: true
layer.effect: DropShadow {
radius: 32
samples: 64
color: Qt.rgba(0, 0, 0, 0.3)
horizontalOffset: 0
verticalOffset: 8
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingXL
spacing: Theme.spacingL
// Category selector
Flow {
id: categoryFlow
width: parent.width
height: categories.length > 1 ? implicitHeight : 0
visible: categories.length > 1
spacing: Theme.spacingM
Repeater {
model: categories
Rectangle {
height: 32
width: Math.min(categoryText.implicitWidth + Theme.spacingL * 2, parent.width - Theme.spacingM)
radius: Theme.cornerRadius
color: selectedCategory === modelData ? Theme.primary : "transparent"
border.color: selectedCategory === modelData ? "transparent" : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 1
Text {
id: categoryText
anchors.centerIn: parent
text: modelData
color: selectedCategory === modelData ? Theme.onPrimary : Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
font.weight: selectedCategory === modelData ? Font.Medium : Font.Normal
elide: Text.ElideRight
width: Math.min(implicitWidth, parent.width - Theme.spacingS * 2)
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
selectedCategory = modelData
updateFilteredApps()
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
Rectangle {
id: searchContainer
width: parent.width
height: 56
radius: Theme.cornerRadiusLarge
color: Theme.surfaceVariant
border.width: searchField.activeFocus ? 2 : 1
border.color: searchField.activeFocus ? Theme.primary :
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
Behavior on border.color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Row {
anchors.fill: parent
anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL
spacing: Theme.spacingM
Text {
anchors.verticalCenter: parent.verticalCenter
text: "search"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: searchField.activeFocus ? Theme.primary : Theme.surfaceVariantText
font.weight: Theme.iconFontWeight
}
TextInput {
id: searchField
anchors.verticalCenter: parent.verticalCenter
width: parent.width - parent.spacing - Theme.iconSize - 32
height: parent.height - Theme.spacingS
color: Theme.surfaceText
font.pixelSize: Theme.fontSizeLarge
verticalAlignment: TextInput.AlignVCenter
focus: spotlightOpen
selectByMouse: true
Text {
anchors.verticalCenter: parent.verticalCenter
text: searchField.text.length === 0 ? (recentApps.length > 0 ? "Search applications or select from recent..." : "Search applications...") : ""
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeLarge
visible: searchField.text.length === 0 && !searchField.activeFocus
}
onTextChanged: {
updateFilteredApps()
}
Keys.onPressed: function(event) {
if (event.key === Qt.Key_Escape) {
hide()
event.accepted = true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
launchSelected()
event.accepted = true
} else if (event.key === Qt.Key_Down) {
selectNext()
event.accepted = true
} else if (event.key === Qt.Key_Up) {
selectPrevious()
event.accepted = true
}
}
}
}
}
Column {
id: resultsList
width: parent.width
height: filteredApps.length > 0 ? Math.min(filteredApps.length * 60, 320) : 0
visible: filteredApps.length > 0
Repeater {
model: filteredModel
Rectangle {
width: resultsList.width
height: 60
radius: Theme.cornerRadius
color: index === selectedIndex ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
appMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) :
"transparent"
border.color: index === selectedIndex ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : "transparent"
border.width: index === selectedIndex ? 1 : 0
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingL
Rectangle {
width: 40
height: 40
radius: Theme.cornerRadius
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
border.width: 1
anchors.verticalCenter: parent.verticalCenter
IconImage {
id: appIcon
anchors.fill: parent
anchors.margins: 4
source: model.icon ? Quickshell.iconPath(model.icon, "") : ""
smooth: true
asynchronous: true
onStatusChanged: {
if (status === Image.Error || status === Image.Null) {
fallbackText.visible = true
} else {
fallbackText.visible = false
}
}
}
Text {
id: fallbackText
anchors.centerIn: parent
text: model.name ? model.name.charAt(0).toUpperCase() : "A"
font.pixelSize: 18
color: Theme.primary
font.weight: Font.Bold
visible: false
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 40 - Theme.spacingL
spacing: 2
Text {
width: parent.width
text: model.name
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
}
Text {
width: parent.width
text: model.comment || "Application"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
elide: Text.ElideRight
visible: model.comment && model.comment.length > 0
}
}
}
MouseArea {
id: appMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: {
selectedIndex = index
}
onClicked: {
launchApp(model)
}
}
}
}
}
}
}
IpcHandler {
target: "spotlight"
function open() {
console.log("SpotlightLauncher: IPC open() called")
spotlightLauncher.show()
return "SPOTLIGHT_OPEN_SUCCESS"
}
function close() {
console.log("SpotlightLauncher: IPC close() called")
spotlightLauncher.hide()
return "SPOTLIGHT_CLOSE_SUCCESS"
}
function toggle() {
console.log("SpotlightLauncher: IPC toggle() called")
spotlightLauncher.toggle()
return "SPOTLIGHT_TOGGLE_SUCCESS"
}
}
Component.onCompleted: {
console.log("SpotlightLauncher: Component.onCompleted called - component loaded successfully!")
desktopScanner.running = true
}
}

View File

@@ -23,4 +23,5 @@ PowerMenuPopup 1.0 PowerMenuPopup.qml
PowerConfirmDialog 1.0 PowerConfirmDialog.qml PowerConfirmDialog 1.0 PowerConfirmDialog.qml
ThemePicker 1.0 ThemePicker.qml ThemePicker 1.0 ThemePicker.qml
CpuMonitorWidget 1.0 CpuMonitorWidget.qml CpuMonitorWidget 1.0 CpuMonitorWidget.qml
RamMonitorWidget 1.0 RamMonitorWidget.qml RamMonitorWidget 1.0 RamMonitorWidget.qml
SpotlightLauncher 1.0 SpotlightLauncher.qml

View File

@@ -305,6 +305,10 @@ ShellRoot {
theme: Theme theme: Theme
} }
SpotlightLauncher {
id: spotlightLauncher
}
ClipboardHistory { ClipboardHistory {
id: clipboardHistoryPopup id: clipboardHistoryPopup
theme: Theme theme: Theme