diff --git a/Modals/Spotlight/SpotlightResults.qml b/Modals/Spotlight/SpotlightResults.qml index 449e2731..a0404e86 100644 --- a/Modals/Spotlight/SpotlightResults.qml +++ b/Modals/Spotlight/SpotlightResults.qml @@ -7,6 +7,10 @@ import qs.Widgets Rectangle { id: resultsContainer + // DEVELOPER NOTE: This component renders the Spotlight launcher (accessed via Mod+Space). + // Changes to launcher behavior, especially item rendering, filtering, or model structure, + // likely require corresponding updates in Modules/AppDrawer/AppLauncher.qml and vice versa. + property var appLauncher: null property var contextMenu: null @@ -90,19 +94,32 @@ Rectangle { width: resultsList.iconSize height: resultsList.iconSize anchors.verticalCenter: parent.verticalCenter + visible: model.icon !== undefined && model.icon !== "" + + property string iconValue: model.icon || "" + property bool isMaterial: iconValue.indexOf("material:") === 0 + property string materialName: isMaterial ? iconValue.substring(9) : "" + + DankIcon { + anchors.centerIn: parent + name: parent.materialName + size: resultsList.iconSize + color: Theme.surfaceText + visible: parent.isMaterial + } IconImage { id: listIconImg anchors.fill: parent - source: Quickshell.iconPath(model.icon, true) + source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true) asynchronous: true - visible: status === Image.Ready + visible: !parent.isMaterial && status === Image.Ready } Rectangle { anchors.fill: parent - visible: !listIconImg.visible + visible: !parent.isMaterial && !listIconImg.visible color: Theme.surfaceLight radius: Theme.cornerRadius border.width: 1 @@ -120,7 +137,7 @@ Rectangle { Column { anchors.verticalCenter: parent.verticalCenter - width: parent.width - resultsList.iconSize - Theme.spacingL + width: (model.icon !== undefined && model.icon !== "") ? (parent.width - resultsList.iconSize - Theme.spacingL) : parent.width spacing: Theme.spacingXS StyledText { @@ -255,20 +272,33 @@ Rectangle { width: iconSize height: iconSize anchors.horizontalCenter: parent.horizontalCenter + visible: model.icon !== undefined && model.icon !== "" + + property string iconValue: model.icon || "" + property bool isMaterial: iconValue.indexOf("material:") === 0 + property string materialName: isMaterial ? iconValue.substring(9) : "" + + DankIcon { + anchors.centerIn: parent + name: parent.materialName + size: parent.iconSize + color: Theme.surfaceText + visible: parent.isMaterial + } IconImage { id: gridIconImg anchors.fill: parent - source: Quickshell.iconPath(model.icon, true) + source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true) smooth: true asynchronous: true - visible: status === Image.Ready + visible: !parent.isMaterial && status === Image.Ready } Rectangle { anchors.fill: parent - visible: !gridIconImg.visible + visible: !parent.isMaterial && !gridIconImg.visible color: Theme.surfaceLight radius: Theme.cornerRadius border.width: 1 diff --git a/Modules/AppDrawer/AppDrawerPopout.qml b/Modules/AppDrawer/AppDrawerPopout.qml index ecbf3266..a1d36b96 100644 --- a/Modules/AppDrawer/AppDrawerPopout.qml +++ b/Modules/AppDrawer/AppDrawerPopout.qml @@ -404,16 +404,29 @@ DankPopout { width: appList.iconSize height: appList.iconSize anchors.verticalCenter: parent.verticalCenter + visible: model.icon !== undefined && model.icon !== "" + + property string iconValue: model.icon || "" + property bool isMaterial: iconValue.indexOf("material:") === 0 + property string materialName: isMaterial ? iconValue.substring(9) : "" + + DankIcon { + anchors.centerIn: parent + name: parent.materialName + size: appList.iconSize - Theme.spacingM + color: Theme.surfaceText + visible: parent.isMaterial + } IconImage { id: listIconImg anchors.fill: parent anchors.margins: Theme.spacingXS - source: Quickshell.iconPath(model.icon, true) + source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true) smooth: true asynchronous: true - visible: status === Image.Ready + visible: !parent.isMaterial && status === Image.Ready } Rectangle { @@ -421,7 +434,7 @@ DankPopout { anchors.leftMargin: Theme.spacingS anchors.rightMargin: Theme.spacingS anchors.bottomMargin: Theme.spacingM - visible: !listIconImg.visible + visible: !parent.isMaterial && listIconImg.status !== Image.Ready color: Theme.surfaceLight radius: Theme.cornerRadius border.width: 0 @@ -435,11 +448,12 @@ DankPopout { font.weight: Font.Bold } } + } Column { anchors.verticalCenter: parent.verticalCenter - width: parent.width - appList.iconSize - Theme.spacingL + width: (model.icon !== undefined && model.icon !== "") ? (parent.width - appList.iconSize - Theme.spacingL) : parent.width spacing: Theme.spacingXS StyledText { @@ -513,6 +527,7 @@ DankPopout { property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns property int baseCellHeight: baseCellWidth + 20 property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns + property int remainingSpace: width - (actualColumns * cellWidth) signal keyboardNavigationReset @@ -578,6 +593,19 @@ DankPopout { width: iconSize height: iconSize anchors.horizontalCenter: parent.horizontalCenter + visible: model.icon !== undefined && model.icon !== "" + + property string iconValue: model.icon || "" + property bool isMaterial: iconValue.indexOf("material:") === 0 + property string materialName: isMaterial ? iconValue.substring(9) : "" + + DankIcon { + anchors.centerIn: parent + name: parent.materialName + size: parent.iconSize - Theme.spacingL + color: Theme.surfaceText + visible: parent.isMaterial + } IconImage { id: gridIconImg @@ -586,10 +614,10 @@ DankPopout { anchors.leftMargin: Theme.spacingS anchors.rightMargin: Theme.spacingS anchors.bottomMargin: Theme.spacingS - source: Quickshell.iconPath(model.icon, true) + source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true) smooth: true asynchronous: true - visible: status === Image.Ready + visible: !parent.isMaterial && status === Image.Ready } Rectangle { @@ -597,7 +625,7 @@ DankPopout { anchors.leftMargin: Theme.spacingS anchors.rightMargin: Theme.spacingS anchors.bottomMargin: Theme.spacingS - visible: !gridIconImg.visible + visible: !parent.isMaterial && gridIconImg.status !== Image.Ready color: Theme.surfaceLight radius: Theme.cornerRadius border.width: 0 diff --git a/Modules/AppDrawer/AppLauncher.qml b/Modules/AppDrawer/AppLauncher.qml index b5c5f0a2..0cd13b55 100644 --- a/Modules/AppDrawer/AppLauncher.qml +++ b/Modules/AppDrawer/AppLauncher.qml @@ -8,6 +8,10 @@ import qs.Widgets Item { id: root + // DEVELOPER NOTE: This component manages the AppDrawer launcher (accessed via DankBar icon). + // Changes to launcher behavior, especially item rendering, filtering, or model structure, + // likely require corresponding updates in Modals/Spotlight/SpotlightResults.qml and vice versa. + property string searchQuery: "" property string selectedCategory: I18n.tr("All") property string viewMode: "list" // "list" or "grid" @@ -163,7 +167,7 @@ Item { filteredModel.append({ "name": app.name || "", "exec": app.execString || app.exec || app.action || "", - "icon": app.icon || "application-x-executable", + "icon": app.icon !== undefined ? app.icon : (isPluginItem ? "" : "application-x-executable"), "comment": app.comment || "", "categories": app.categories || [], "isPlugin": isPluginItem, diff --git a/PLUGINS/LauncherExample/LauncherExampleLauncher.qml b/PLUGINS/LauncherExample/LauncherExampleLauncher.qml index 67964d2c..39ba58d0 100644 --- a/PLUGINS/LauncherExample/LauncherExampleLauncher.qml +++ b/PLUGINS/LauncherExample/LauncherExampleLauncher.qml @@ -26,35 +26,35 @@ Item { const baseItems = [ { name: "Test Item 1", - icon: "lightbulb", + icon: "material:lightbulb", comment: "This is a test item that shows a toast notification", action: "toast:Test Item 1 executed!", categories: ["LauncherExample"] }, { - name: "Test Item 2", - icon: "star", + name: "Test Item 2", + icon: "material:star", comment: "Another test item with different action", action: "toast:Test Item 2 clicked!", categories: ["LauncherExample"] }, { name: "Test Item 3", - icon: "favorite", + icon: "material:favorite", comment: "Third test item for demonstration", action: "toast:Test Item 3 activated!", categories: ["LauncherExample"] }, { name: "Example Copy Action", - icon: "content_copy", + icon: "material:content_copy", comment: "Demonstrates copying text to clipboard", action: "copy:This text was copied by the launcher plugin!", categories: ["LauncherExample"] }, { name: "Example Script Action", - icon: "terminal", + icon: "material:terminal", comment: "Demonstrates running a simple command", action: "script:echo 'Hello from launcher plugin!'", categories: ["LauncherExample"] diff --git a/PLUGINS/LauncherExample/README.md b/PLUGINS/LauncherExample/README.md index ff744d45..d752d0ad 100644 --- a/PLUGINS/LauncherExample/README.md +++ b/PLUGINS/LauncherExample/README.md @@ -88,13 +88,41 @@ function executeItem(item): void ```javascript { name: "Item Name", // Display name - icon: "icon_name", // Material icon + icon: "icon_name", // Icon (optional, see Icon Types below) comment: "Description", // Subtitle text action: "type:data", // Action to execute categories: ["PluginName"] // Category array } ``` +**Icon Types**: + +The `icon` field supports three formats: + +1. **Material Design Icons** - Use `material:` prefix: + ```javascript + icon: "material:lightbulb" // Material Symbols Rounded font + ``` + Examples: `material:star`, `material:favorite`, `material:settings` + +2. **Desktop Theme Icons** - Use icon name directly: + ```javascript + icon: "firefox" // Uses system icon theme + ``` + Examples: `firefox`, `chrome`, `folder`, `text-editor` + +3. **No Icon** - Omit the `icon` field entirely: + ```javascript + { + name: "😀 Grinning Face", + // No icon field + comment: "Copy emoji", + action: "copy:😀", + categories: ["MyPlugin"] + } + ``` + Perfect for emoji pickers or text-only items where the icon area should be hidden + **Action Format**: `type:data` where: - `type` - Action handler (toast, copy, script, etc.) - `data` - Action-specific data diff --git a/PLUGINS/README.md b/PLUGINS/README.md index c3f9e575..99ae3d6d 100644 --- a/PLUGINS/README.md +++ b/PLUGINS/README.md @@ -1221,11 +1221,51 @@ Item { Each item returned by `getItems()` must include: - `name` (string): Display name shown in launcher -- `icon` (string): Material Design icon name +- `icon` (string, optional): Icon specification (see Icon Types below) - `comment` (string): Description/subtitle text - `action` (string): Action identifier in `type:data` format - `categories` (array): Array containing your plugin name +### Icon Types + +The `icon` field supports three formats: + +**1. Material Design Icons** - Use the `material:` prefix: +```javascript +{ + name: "My Item", + icon: "material:lightbulb", // Material Symbols Rounded font + comment: "Uses Material Design icon", + action: "toast:Hello!", + categories: ["MyPlugin"] +} +``` +Available icons: Any icon from Material Symbols font (e.g., `lightbulb`, `star`, `favorite`, `settings`, `terminal`, `translate`, `sentiment_satisfied`) + +**2. Desktop Theme Icons** - Use icon name directly: +```javascript +{ + name: "Firefox", + icon: "firefox", // Uses system icon theme + comment: "Launches Firefox browser", + action: "exec:firefox", + categories: ["MyPlugin"] +} +``` +Uses the user's installed icon theme. Common examples: `firefox`, `chrome`, `folder`, `text-editor` + +**3. No Icon** - Omit the `icon` field entirely: +```javascript +{ + name: "😀 Grinning Face", + // No icon field - emoji/unicode in name displays without icon area + comment: "Copy emoji to clipboard", + action: "copy:😀", + categories: ["MyPlugin"] +} +``` +When `icon` is omitted, the launcher hides the icon area and displays only the text, giving full width to the item name. Perfect for emoji pickers or text-only items. + ### Trigger System Triggers control when your plugin's items appear in the launcher: