mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-07 22:15:38 -05:00
plugins: support for multiple widgets per-plugin (variants)
This commit is contained in:
116
PLUGINS/ExampleWithVariants/README.md
Normal file
116
PLUGINS/ExampleWithVariants/README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Example with Variants Plugin
|
||||
|
||||
This plugin demonstrates the dynamic variant system for DankMaterialShell plugins.
|
||||
|
||||
## What are Variants?
|
||||
|
||||
Variants allow a single plugin to create multiple widget instances with different configurations. Each variant appears as a separate widget option in the Bar Settings "Add Widget" menu.
|
||||
|
||||
## How It Works
|
||||
|
||||
### Plugin Architecture
|
||||
|
||||
1. **Single Component**: The plugin defines one widget component (`VariantWidget.qml`)
|
||||
2. **Variant Data**: Each variant stores its configuration in plugin settings
|
||||
3. **Dynamic Creation**: Variants are created through the plugin's settings UI
|
||||
4. **Widget ID Format**: Variants use the format `pluginId:variantId` (e.g., `exampleVariants:variant_1234567890`)
|
||||
|
||||
### Widget Properties
|
||||
|
||||
Each variant widget receives:
|
||||
- `pluginService`: Reference to PluginService
|
||||
- `pluginId`: The base plugin ID (e.g., "exampleVariants")
|
||||
- `variantId`: The specific variant ID (e.g., "variant_1234567890")
|
||||
- `variantData`: The variant's configuration object
|
||||
|
||||
### Variant Configuration
|
||||
|
||||
This example stores:
|
||||
```javascript
|
||||
{
|
||||
id: "variant_1234567890",
|
||||
name: "My Variant",
|
||||
text: "Display Text",
|
||||
icon: "star",
|
||||
color: "#FF5722"
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Your Own Variant Plugin
|
||||
|
||||
### 1. Widget Component
|
||||
|
||||
Create a widget that accepts variant data:
|
||||
|
||||
```qml
|
||||
Rectangle {
|
||||
property var pluginService: null
|
||||
property string pluginId: ""
|
||||
property string variantId: ""
|
||||
property var variantData: null
|
||||
|
||||
// Use variantData for configuration
|
||||
property string displayText: variantData?.text || "Default"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Settings Component
|
||||
|
||||
Provide UI to manage variants:
|
||||
|
||||
```qml
|
||||
FocusScope {
|
||||
property var pluginService: null
|
||||
|
||||
// Create variant
|
||||
pluginService.createPluginVariant(pluginId, variantName, variantConfig)
|
||||
|
||||
// Remove variant
|
||||
pluginService.removePluginVariant(pluginId, variantId)
|
||||
|
||||
// Update variant
|
||||
pluginService.updatePluginVariant(pluginId, variantId, variantConfig)
|
||||
|
||||
// Get all variants
|
||||
var variants = pluginService.getPluginVariants(pluginId)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Plugin Manifest
|
||||
|
||||
Standard plugin manifest - no special configuration needed:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "yourPlugin",
|
||||
"name": "Your Plugin",
|
||||
"component": "./Widget.qml",
|
||||
"settings": "./Settings.qml"
|
||||
}
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Script Runner**: Multiple variants running different scripts
|
||||
- **System Monitors**: Different monitoring targets (CPU, GPU, Network)
|
||||
- **Quick Launchers**: Different apps or commands per variant
|
||||
- **Custom Indicators**: Different data sources or APIs
|
||||
- **Time Zones**: Multiple clocks for different time zones
|
||||
|
||||
## API Reference
|
||||
|
||||
### PluginService Functions
|
||||
|
||||
- `getPluginVariants(pluginId)`: Get all variants for a plugin
|
||||
- `getAllPluginVariants()`: Get all variants across all plugins
|
||||
- `createPluginVariant(pluginId, name, config)`: Create new variant
|
||||
- `removePluginVariant(pluginId, variantId)`: Delete variant
|
||||
- `updatePluginVariant(pluginId, variantId, config)`: Update variant
|
||||
- `getPluginVariantData(pluginId, variantId)`: Get specific variant data
|
||||
|
||||
### Widget Properties
|
||||
|
||||
- `pluginId`: Base plugin identifier
|
||||
- `variantId`: Variant identifier (null if no variant)
|
||||
- `variantData`: Variant configuration object
|
||||
- `pluginService`: PluginService reference
|
||||
303
PLUGINS/ExampleWithVariants/VariantSettings.qml
Normal file
303
PLUGINS/ExampleWithVariants/VariantSettings.qml
Normal file
@@ -0,0 +1,303 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Plugins
|
||||
|
||||
PluginSettings {
|
||||
id: root
|
||||
pluginId: "exampleVariants"
|
||||
|
||||
onVariantsChanged: {
|
||||
variantsModel.clear()
|
||||
for (var i = 0; i < variants.length; i++) {
|
||||
variantsModel.append(variants[i])
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: "Variant Manager"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: "Create multiple widget variants with different text, icons, and colors"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: addVariantColumn.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
|
||||
Column {
|
||||
id: addVariantColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: "Add New Variant"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Column {
|
||||
width: (parent.width - Theme.spacingM * 2) / 3
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Name"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: nameField
|
||||
width: parent.width
|
||||
placeholderText: "Variant Name"
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: (parent.width - Theme.spacingM * 2) / 3
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Icon"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: iconField
|
||||
width: parent.width
|
||||
placeholderText: "star"
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: (parent.width - Theme.spacingM * 2) / 3
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Text"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: textField
|
||||
width: parent.width
|
||||
placeholderText: "Display Text"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: "Create Variant"
|
||||
iconName: "add"
|
||||
onClicked: {
|
||||
if (!nameField.text) {
|
||||
ToastService.showError("Please enter a variant name")
|
||||
return
|
||||
}
|
||||
|
||||
var variantConfig = {
|
||||
text: textField.text || nameField.text,
|
||||
icon: iconField.text || "widgets"
|
||||
}
|
||||
|
||||
createVariant(nameField.text, variantConfig)
|
||||
ToastService.showInfo("Variant created: " + nameField.text)
|
||||
|
||||
nameField.text = ""
|
||||
iconField.text = ""
|
||||
textField.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: Math.max(200, variantsColumn.implicitHeight + Theme.spacingL * 2)
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
|
||||
Column {
|
||||
id: variantsColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: "Existing Variants"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
ListView {
|
||||
width: parent.width
|
||||
height: Math.max(100, contentHeight)
|
||||
clip: true
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
model: ListModel {
|
||||
id: variantsModel
|
||||
}
|
||||
|
||||
delegate: StyledRect {
|
||||
required property var model
|
||||
width: ListView.view.width
|
||||
height: variantRow.implicitHeight + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: variantMouseArea.containsMouse ? Theme.surfaceContainerHighest : Theme.surfaceContainer
|
||||
|
||||
Row {
|
||||
id: variantRow
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Item {
|
||||
width: Theme.iconSize
|
||||
height: Theme.iconSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: model.icon || "widgets"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
width: Theme.iconSize
|
||||
height: Theme.iconSize
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingXS
|
||||
width: parent.width - Theme.iconSize - deleteButton.width - Theme.spacingM * 4
|
||||
|
||||
StyledText {
|
||||
text: model.name || "Unnamed"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Text: " + (model.text || "") + " | Icon: " + (model.icon || "")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: deleteButton
|
||||
width: 32
|
||||
height: 32
|
||||
radius: 16
|
||||
color: deleteArea.containsMouse ? Theme.error : "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "delete"
|
||||
size: 16
|
||||
color: deleteArea.containsMouse ? Theme.onError : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: deleteArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
removeVariant(model.id)
|
||||
ToastService.showInfo("Variant removed: " + model.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: variantMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: "No variants created yet"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
visible: variantsModel.count === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: instructionsColumn.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surface
|
||||
|
||||
Column {
|
||||
id: instructionsColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "info"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "How to Use Variants"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "1. Create variants with different names, icons, and text\n2. Go to Bar Settings and click 'Add Widget'\n3. Each variant will appear as a separate widget option\n4. Add variants to your bar just like any other widget"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
lineHeight: 1.4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
PLUGINS/ExampleWithVariants/VariantWidget.qml
Normal file
57
PLUGINS/ExampleWithVariants/VariantWidget.qml
Normal file
@@ -0,0 +1,57 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Plugins
|
||||
|
||||
PluginComponent {
|
||||
id: root
|
||||
|
||||
property string variantId: ""
|
||||
property var variantData: null
|
||||
|
||||
property string displayText: variantData?.text || "Default Text"
|
||||
property string displayIcon: variantData?.icon || "widgets"
|
||||
|
||||
horizontalBarPill: Component {
|
||||
Row {
|
||||
spacing: 3
|
||||
|
||||
DankIcon {
|
||||
name: root.displayIcon
|
||||
size: Theme.iconSize - 8
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: root.displayText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verticalBarPill: Component {
|
||||
Column {
|
||||
spacing: 1
|
||||
|
||||
DankIcon {
|
||||
name: root.displayIcon
|
||||
size: Theme.iconSize - 8
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: root.displayText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
PLUGINS/ExampleWithVariants/plugin.json
Normal file
14
PLUGINS/ExampleWithVariants/plugin.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "exampleVariants",
|
||||
"name": "Example with Variants",
|
||||
"description": "Demonstrates dynamic variant creation for plugins",
|
||||
"version": "1.0.0",
|
||||
"author": "DMS",
|
||||
"icon": "widgets",
|
||||
"component": "./VariantWidget.qml",
|
||||
"settings": "./VariantSettings.qml",
|
||||
"permissions": [
|
||||
"settings_read",
|
||||
"settings_write"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user