mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-26 14:32:52 -05:00
switch hto monorepo structure
This commit is contained in:
140
quickshell/PLUGINS/LauncherExample/LauncherExampleLauncher.qml
Normal file
140
quickshell/PLUGINS/LauncherExample/LauncherExampleLauncher.qml
Normal file
@@ -0,0 +1,140 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// Plugin properties
|
||||
property var pluginService: null
|
||||
property string trigger: "#"
|
||||
|
||||
// Plugin interface signals
|
||||
signal itemsChanged()
|
||||
|
||||
Component.onCompleted: {
|
||||
console.info("LauncherExample: Plugin loaded")
|
||||
|
||||
// Load custom trigger from settings
|
||||
if (pluginService) {
|
||||
trigger = pluginService.loadPluginData("launcherExample", "trigger", "#")
|
||||
}
|
||||
}
|
||||
|
||||
// Required function: Get items for launcher
|
||||
function getItems(query) {
|
||||
const baseItems = [
|
||||
{
|
||||
name: "Test Item 1",
|
||||
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: "material:star",
|
||||
comment: "Another test item with different action",
|
||||
action: "toast:Test Item 2 clicked!",
|
||||
categories: ["LauncherExample"]
|
||||
},
|
||||
{
|
||||
name: "Test Item 3",
|
||||
icon: "material:favorite",
|
||||
comment: "Third test item for demonstration",
|
||||
action: "toast:Test Item 3 activated!",
|
||||
categories: ["LauncherExample"]
|
||||
},
|
||||
{
|
||||
name: "Unicode Icon Example",
|
||||
icon: "unicode:🚀",
|
||||
comment: "Demonstrates unicode/emoji icon support",
|
||||
action: "toast:Unicode icons work great!",
|
||||
categories: ["LauncherExample"]
|
||||
},
|
||||
{
|
||||
name: "Example Copy Action",
|
||||
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: "material:terminal",
|
||||
comment: "Demonstrates running a simple command",
|
||||
action: "script:echo 'Hello from launcher plugin!'",
|
||||
categories: ["LauncherExample"]
|
||||
}
|
||||
]
|
||||
|
||||
if (!query || query.length === 0) {
|
||||
return baseItems
|
||||
}
|
||||
|
||||
// Filter items based on query
|
||||
const lowerQuery = query.toLowerCase()
|
||||
return baseItems.filter(item => {
|
||||
return item.name.toLowerCase().includes(lowerQuery) ||
|
||||
item.comment.toLowerCase().includes(lowerQuery)
|
||||
})
|
||||
}
|
||||
|
||||
// Required function: Execute item action
|
||||
function executeItem(item) {
|
||||
if (!item || !item.action) {
|
||||
console.warn("LauncherExample: Invalid item or action")
|
||||
return
|
||||
}
|
||||
|
||||
console.log("LauncherExample: Executing item:", item.name, "with action:", item.action)
|
||||
|
||||
const actionParts = item.action.split(":")
|
||||
const actionType = actionParts[0]
|
||||
const actionData = actionParts.slice(1).join(":")
|
||||
|
||||
switch (actionType) {
|
||||
case "toast":
|
||||
showToast(actionData)
|
||||
break
|
||||
case "copy":
|
||||
copyToClipboard(actionData)
|
||||
break
|
||||
case "script":
|
||||
runScript(actionData)
|
||||
break
|
||||
default:
|
||||
console.warn("LauncherExample: Unknown action type:", actionType)
|
||||
showToast("Unknown action: " + actionType)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for different action types
|
||||
function showToast(message) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.showInfo("LauncherExample", message)
|
||||
} else {
|
||||
console.log("LauncherExample Toast:", message)
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
Quickshell.execDetached(["sh", "-c", "echo -n '" + text + "' | wl-copy"])
|
||||
showToast("Copied to clipboard: " + text)
|
||||
}
|
||||
|
||||
function runScript(command) {
|
||||
console.log("LauncherExample: Would run script:", command)
|
||||
showToast("Script executed: " + command)
|
||||
|
||||
// In a real plugin, you might create a Process component here
|
||||
// For demo purposes, we just show what would happen
|
||||
}
|
||||
|
||||
// Watch for trigger changes
|
||||
onTriggerChanged: {
|
||||
if (pluginService) {
|
||||
pluginService.savePluginData("launcherExample", "trigger", trigger)
|
||||
}
|
||||
}
|
||||
}
|
||||
244
quickshell/PLUGINS/LauncherExample/LauncherExampleSettings.qml
Normal file
244
quickshell/PLUGINS/LauncherExample/LauncherExampleSettings.qml
Normal file
@@ -0,0 +1,244 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Widgets
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
|
||||
property var pluginService: null
|
||||
|
||||
implicitHeight: settingsColumn.implicitHeight
|
||||
height: implicitHeight
|
||||
|
||||
Column {
|
||||
id: settingsColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 16
|
||||
|
||||
Text {
|
||||
text: "Launcher Example Plugin Settings"
|
||||
font.pixelSize: 18
|
||||
font.weight: Font.Bold
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "This plugin demonstrates the launcher plugin system with example items and actions."
|
||||
font.pixelSize: 14
|
||||
color: "#CCFFFFFF"
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width - 32
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width - 32
|
||||
height: 1
|
||||
color: "#30FFFFFF"
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 12
|
||||
width: parent.width - 32
|
||||
|
||||
Text {
|
||||
text: "Trigger Configuration"
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: noTriggerToggle.checked ? "Items will always show in the launcher (no trigger needed)." : "Set the trigger text to activate this plugin. Type the trigger in the launcher to filter to this plugin's items."
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 12
|
||||
|
||||
CheckBox {
|
||||
id: noTriggerToggle
|
||||
text: "No trigger (always show)"
|
||||
checked: loadSettings("noTrigger", false)
|
||||
|
||||
contentItem: Text {
|
||||
text: noTriggerToggle.text
|
||||
font.pixelSize: 14
|
||||
color: "#FFFFFF"
|
||||
leftPadding: noTriggerToggle.indicator.width + 8
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 20
|
||||
implicitHeight: 20
|
||||
radius: 4
|
||||
border.color: noTriggerToggle.checked ? "#4CAF50" : "#60FFFFFF"
|
||||
border.width: 2
|
||||
color: noTriggerToggle.checked ? "#4CAF50" : "transparent"
|
||||
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 12
|
||||
anchors.centerIn: parent
|
||||
radius: 2
|
||||
color: "#FFFFFF"
|
||||
visible: noTriggerToggle.checked
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
saveSettings("noTrigger", checked)
|
||||
if (checked) {
|
||||
saveSettings("trigger", "")
|
||||
} else {
|
||||
saveSettings("trigger", triggerField.text || "#")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 12
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
visible: !noTriggerToggle.checked
|
||||
|
||||
Text {
|
||||
text: "Trigger:"
|
||||
font.pixelSize: 14
|
||||
color: "#FFFFFF"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: triggerField
|
||||
width: 100
|
||||
height: 40
|
||||
text: loadSettings("trigger", "#")
|
||||
placeholderText: "#"
|
||||
backgroundColor: "#30FFFFFF"
|
||||
textColor: "#FFFFFF"
|
||||
|
||||
onTextEdited: {
|
||||
const newTrigger = text.trim()
|
||||
saveSettings("trigger", newTrigger || "#")
|
||||
saveSettings("noTrigger", newTrigger === "")
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Examples: #, !, @, !ex, etc."
|
||||
font.pixelSize: 12
|
||||
color: "#AAFFFFFF"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width - 32
|
||||
height: 1
|
||||
color: "#30FFFFFF"
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
width: parent.width - 32
|
||||
|
||||
Text {
|
||||
text: "Example Items:"
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 4
|
||||
leftPadding: 16
|
||||
|
||||
Text {
|
||||
text: "• Test Item 1, 2, 3 - Show toast notifications"
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "• Example Copy Action - Copy text to clipboard"
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "• Example Script Action - Demonstrate script execution"
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width - 32
|
||||
height: 1
|
||||
color: "#30FFFFFF"
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
width: parent.width - 32
|
||||
|
||||
Text {
|
||||
text: "Usage:"
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 4
|
||||
leftPadding: 16
|
||||
bottomPadding: 24
|
||||
|
||||
Text {
|
||||
text: "1. Open Launcher (Ctrl+Space or click launcher button)"
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: noTriggerToggle.checked ? "2. Items are always visible in the launcher" : "2. Type your trigger (default: #) to filter to this plugin"
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: noTriggerToggle.checked ? "3. Search works normally with plugin items included" : "3. Optionally add search terms: '# test' to find test items"
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "4. Select an item and press Enter to execute its action"
|
||||
font.pixelSize: 12
|
||||
color: "#CCFFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function saveSettings(key, value) {
|
||||
if (pluginService) {
|
||||
pluginService.savePluginData("launcherExample", key, value)
|
||||
}
|
||||
}
|
||||
|
||||
function loadSettings(key, defaultValue) {
|
||||
if (pluginService) {
|
||||
return pluginService.loadPluginData("launcherExample", key, defaultValue)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
240
quickshell/PLUGINS/LauncherExample/README.md
Normal file
240
quickshell/PLUGINS/LauncherExample/README.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# LauncherExample Plugin
|
||||
|
||||
A demonstration plugin that showcases the DMS launcher plugin system capabilities.
|
||||
|
||||
## Purpose
|
||||
|
||||
This plugin serves as a comprehensive example for developers creating launcher plugins for DMS. It demonstrates:
|
||||
|
||||
- **Plugin Structure**: Proper manifest, launcher, and settings components
|
||||
- **Trigger System**: Customizable trigger strings for plugin activation (including empty triggers)
|
||||
- **Item Management**: Providing searchable items to the launcher
|
||||
- **Action Execution**: Handling different types of actions (toast, copy, script)
|
||||
- **Settings Integration**: Configurable plugin settings with persistence
|
||||
|
||||
## Features
|
||||
|
||||
### Example Items
|
||||
- **Test Items 1-3**: Demonstrate toast notifications
|
||||
- **Copy Action**: Shows clipboard integration
|
||||
- **Script Action**: Demonstrates command execution
|
||||
|
||||
### Trigger System
|
||||
- **Default Trigger**: `#` (configurable in settings)
|
||||
- **Empty Trigger Option**: Items can always be visible without needing a trigger
|
||||
- **Usage**: Type `#` in launcher to filter to this plugin (when trigger is set)
|
||||
- **Search**: Type `# test` to search within plugin items
|
||||
|
||||
### Action Types
|
||||
- `toast:message` - Shows toast notification
|
||||
- `copy:text` - Copies text to clipboard
|
||||
- `script:command` - Executes shell command (demo only)
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
PLUGINS/LauncherExample/
|
||||
├── plugin.json # Plugin manifest
|
||||
├── LauncherExampleLauncher.qml # Main launcher component
|
||||
├── LauncherExampleSettings.qml # Settings interface
|
||||
└── README.md # This documentation
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Plugin Directory**: Copy to `~/.config/DankMaterialShell/plugins/LauncherExample`
|
||||
2. **Enable Plugin**: Settings → Plugins → Enable "LauncherExample"
|
||||
3. **Configure**: Set custom trigger in plugin settings if desired
|
||||
|
||||
## Usage
|
||||
|
||||
### With Trigger (Default)
|
||||
1. Open launcher (Ctrl+Space or launcher button)
|
||||
2. Type `#` to activate plugin trigger
|
||||
3. Browse available items or add search terms
|
||||
4. Press Enter to execute selected item
|
||||
|
||||
### Without Trigger (Empty Trigger Mode)
|
||||
1. Enable "No trigger (always show)" in plugin settings
|
||||
2. Open launcher - plugin items are always visible
|
||||
3. Search works normally with plugin items included
|
||||
4. Press Enter to execute selected item
|
||||
|
||||
### Search Examples
|
||||
- `#` - Show all plugin items (with trigger enabled)
|
||||
- `# test` - Show items matching "test"
|
||||
- `# copy` - Show items matching "copy"
|
||||
- `test` - Show all items matching "test" (with empty trigger enabled)
|
||||
|
||||
## Developer Guide
|
||||
|
||||
### Plugin Contract
|
||||
|
||||
**Launcher Component Requirements**:
|
||||
```qml
|
||||
// Required properties
|
||||
property var pluginService: null
|
||||
property string trigger: "#"
|
||||
|
||||
// Required signals
|
||||
signal itemsChanged()
|
||||
|
||||
// Required functions
|
||||
function getItems(query): array
|
||||
function executeItem(item): void
|
||||
```
|
||||
|
||||
**Item Structure**:
|
||||
```javascript
|
||||
{
|
||||
name: "Item Name", // Display name
|
||||
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 four formats:
|
||||
|
||||
1. **Material Design Icons** - Use `material:` prefix:
|
||||
```javascript
|
||||
icon: "material:lightbulb" // Material Symbols Rounded font
|
||||
```
|
||||
Examples: `material:star`, `material:favorite`, `material:settings`
|
||||
|
||||
2. **Unicode/Emoji Icons** - Use `unicode:` prefix:
|
||||
```javascript
|
||||
icon: "unicode:🚀" // Unicode character or emoji
|
||||
```
|
||||
Display any Unicode character or emoji as the icon. Examples: `unicode:😀`, `unicode:⚡`, `unicode:🎨`
|
||||
|
||||
3. **Desktop Theme Icons** - Use icon name directly:
|
||||
```javascript
|
||||
icon: "firefox" // Uses system icon theme
|
||||
```
|
||||
Examples: `firefox`, `chrome`, `folder`, `text-editor`
|
||||
|
||||
4. **No Icon** - Omit the `icon` field entirely:
|
||||
```javascript
|
||||
{
|
||||
name: "😀 Grinning Face",
|
||||
// No icon field
|
||||
comment: "Copy emoji",
|
||||
action: "copy:😀",
|
||||
categories: ["MyPlugin"]
|
||||
}
|
||||
```
|
||||
When icon is omitted, the launcher hides the icon area and displays only text
|
||||
|
||||
**Action Format**: `type:data` where:
|
||||
- `type` - Action handler (toast, copy, script, etc.)
|
||||
- `data` - Action-specific data
|
||||
|
||||
### Settings Integration
|
||||
```qml
|
||||
// Save setting
|
||||
pluginService.savePluginData("pluginId", "key", value)
|
||||
|
||||
// Load setting
|
||||
pluginService.loadPluginData("pluginId", "key", defaultValue)
|
||||
```
|
||||
|
||||
### Trigger Configuration
|
||||
|
||||
The trigger can be configured in two ways:
|
||||
|
||||
1. **Empty Trigger** (No Trigger Mode):
|
||||
- Check "No trigger (always show)" in settings
|
||||
- Saves `trigger: ""` and `noTrigger: true`
|
||||
- Items always appear in launcher alongside regular apps
|
||||
|
||||
2. **Custom Trigger**:
|
||||
- Enter any string (e.g., `#`, `!`, `@`, `!ex`)
|
||||
- Uncheck "No trigger" checkbox
|
||||
- Items only appear when trigger is typed
|
||||
|
||||
### Manifest Structure
|
||||
```json
|
||||
{
|
||||
"id": "launcherExample",
|
||||
"name": "LauncherExample",
|
||||
"type": "launcher",
|
||||
"capabilities": ["launcher"],
|
||||
"component": "./LauncherExampleLauncher.qml",
|
||||
"settings": "./LauncherExampleSettings.qml",
|
||||
"permissions": ["settings_read", "settings_write"]
|
||||
}
|
||||
```
|
||||
|
||||
Note: The `trigger` field in the manifest is optional and serves as the default trigger value.
|
||||
|
||||
## Extending This Plugin
|
||||
|
||||
### Adding New Items
|
||||
```qml
|
||||
function getItems(query) {
|
||||
return [
|
||||
{
|
||||
name: "My Item",
|
||||
icon: "custom_icon",
|
||||
comment: "Does something cool",
|
||||
action: "custom:action_data",
|
||||
categories: ["LauncherExample"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Adding New Actions
|
||||
```qml
|
||||
function executeItem(item) {
|
||||
const actionParts = item.action.split(":")
|
||||
const actionType = actionParts[0]
|
||||
const actionData = actionParts.slice(1).join(":")
|
||||
|
||||
switch (actionType) {
|
||||
case "custom":
|
||||
handleCustomAction(actionData)
|
||||
break
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Trigger Logic
|
||||
```qml
|
||||
Component.onCompleted: {
|
||||
if (pluginService) {
|
||||
trigger = pluginService.loadPluginData("launcherExample", "trigger", "#")
|
||||
}
|
||||
}
|
||||
|
||||
onTriggerChanged: {
|
||||
if (pluginService) {
|
||||
pluginService.savePluginData("launcherExample", "trigger", trigger)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Unique Triggers**: Choose triggers that don't conflict with other plugins
|
||||
2. **Clear Descriptions**: Write helpful item comments
|
||||
3. **Error Handling**: Gracefully handle action failures
|
||||
4. **Performance**: Return results quickly in getItems()
|
||||
5. **Cleanup**: Destroy temporary objects in executeItem()
|
||||
6. **Empty Trigger Support**: Consider if your plugin should support empty trigger mode
|
||||
|
||||
## Testing
|
||||
|
||||
Test the plugin by:
|
||||
1. Installing and enabling in DMS
|
||||
2. Testing with trigger enabled
|
||||
3. Testing with empty trigger (no trigger mode)
|
||||
4. Trying each action type
|
||||
5. Testing search functionality
|
||||
6. Verifying settings persistence
|
||||
|
||||
This plugin provides a solid foundation for building more sophisticated launcher plugins with custom functionality!
|
||||
17
quickshell/PLUGINS/LauncherExample/plugin.json
Normal file
17
quickshell/PLUGINS/LauncherExample/plugin.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "launcherExample",
|
||||
"name": "LauncherExample",
|
||||
"description": "Example launcher plugin demonstrating the launcher plugin system",
|
||||
"version": "1.0.0",
|
||||
"author": "DMS Team",
|
||||
"icon": "extension",
|
||||
"type": "launcher",
|
||||
"capabilities": ["clipboard", "command-execution"],
|
||||
"component": "./LauncherExampleLauncher.qml",
|
||||
"settings": "./LauncherExampleSettings.qml",
|
||||
"trigger": "#",
|
||||
"permissions": [
|
||||
"settings_read",
|
||||
"settings_write"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user