diff --git a/dank-qml-common b/dank-qml-common
index 2416d14b8..b3dc38d17 160000
--- a/dank-qml-common
+++ b/dank-qml-common
@@ -1 +1 @@
-Subproject commit 2416d14b889d6dcba43d5ec010f7f7da1d3480ea
+Subproject commit b3dc38d175e2f4f1767da1b05f0b599a1e2015b9
diff --git a/flake.lock b/flake.lock
index d0a2a878d..8c1e66226 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,11 +3,11 @@
"dank-qml-common": {
"flake": false,
"locked": {
- "lastModified": 1792156771,
- "narHash": "sha256-mhJY/aO1R4kovs+uliO2EWIMOlYKCO7yLB7KO8FlzEo=",
+ "lastModified": 1792372175,
+ "narHash": "sha256-LfTuMjjNEKXX6NMLwJ6VV1TVdgKX52bBGMlkY/8LAzw=",
"owner": "AvengeMedia",
"repo": "dank-qml-common",
- "rev": "2416d14b889d6dcba43d5ec010f7f7da1d3480ea",
+ "rev": "b3dc38d175e2f4f1767da1b05f0b599a1e2015b9",
"type": "github"
},
"original": {
diff --git a/quickshell/Modules/Settings/PluginBrowser.qml b/quickshell/Modules/Settings/PluginBrowser.qml
index 0984f1fb7..eeb4f4867 100644
--- a/quickshell/Modules/Settings/PluginBrowser.qml
+++ b/quickshell/Modules/Settings/PluginBrowser.qml
@@ -37,7 +37,6 @@ RegistryBrowserWindow {
property var discoverShelves: []
readonly property var detailPlugin: resolveDetailPlugin(detailPluginId, allPlugins)
- property bool discoverCollapsed: false
readonly property bool discoverVisible: {
if (isLoading || loadError !== "" || searchQuery !== "" || typeFilter !== "" || authorFilter !== "")
return false;
@@ -361,7 +360,6 @@ RegistryBrowserWindow {
if (!pluginGrid)
return;
pluginGrid.cancelFlick();
- discoverCollapsed = false;
pluginGrid.positionViewAtBeginning();
Qt.callLater(() => {
if (pluginGrid)
@@ -622,6 +620,46 @@ RegistryBrowserWindow {
ToastService.showInfo(I18n.tr("Link copied", "plugin browser, link copied to clipboard"));
}
+ function linkMarkup(href, label) {
+ return `${label}`;
+ }
+
+ function authorMarkup(plugin) {
+ if (!plugin.author)
+ return I18n.tr("by %1", "author attribution").arg(I18n.tr("Unknown", "unknown author"));
+ return I18n.tr("by %1", "author attribution").arg(linkMarkup("author:", String(plugin.author).replace(/&/g, "&").replace(/ 0 && pluginGrid.visible
-
- Behavior on height {
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Theme.standardEasing
- }
- }
-
- PluginDiscover {
- id: discover
- x: pluginGrid.cardSpacing / 2
- width: pluginGrid.cellWidth * pluginGrid.columns - pluginGrid.cardSpacing
- picks: root.discoverPicks
- shelves: root.discoverShelves
- columns: pluginGrid.columns
- cardSpacing: pluginGrid.cardSpacing
- onPluginOpened: plugin => root.openPluginDetail(plugin)
- onInstallRequested: plugin => root.installPlugin(plugin.id, plugin.name, plugin.type === "desktop")
- }
- },
DGridView {
id: pluginGrid
@@ -1017,7 +1022,6 @@ RegistryBrowserWindow {
readonly property int infoHeight: Theme.iconButtonSize + Theme.fontSizeSmall * 4 + Theme.spacingS
anchors.fill: parent
- anchors.topMargin: discoverPane.height
anchors.rightMargin: root.showLetterIndex ? Theme.iconButtonSize : 0
cellWidth: Math.floor(width / columns)
cellHeight: previewHeight + infoHeight + Math.round(cardSpacing) + Theme.spacingS * 2 + Theme.spacingM
@@ -1025,14 +1029,23 @@ RegistryBrowserWindow {
clip: true
visible: !root.isLoading && root.loadError === ""
cacheBuffer: cellHeight * 2
- onContentYChanged: {
- if (!root.discoverCollapsed) {
- if (contentY > cellHeight / 4)
- root.discoverCollapsed = true;
- return;
+
+ header: Item {
+ width: pluginGrid.width
+ height: root.discoverVisible ? discover.height + Theme.spacingM : 0
+ visible: root.discoverVisible
+
+ PluginDiscover {
+ id: discover
+ x: pluginGrid.cardSpacing / 2
+ width: pluginGrid.cellWidth * pluginGrid.columns - pluginGrid.cardSpacing
+ picks: root.discoverPicks
+ shelves: root.discoverShelves
+ columns: pluginGrid.columns
+ cardSpacing: pluginGrid.cardSpacing
+ onPluginOpened: plugin => root.openPluginDetail(plugin)
+ onInstallRequested: plugin => root.installPlugin(plugin.id, plugin.name, plugin.type === "desktop")
}
- if (contentY <= 0 && contentHeight > height + cellHeight)
- root.discoverCollapsed = false;
}
delegate: Item {
@@ -1288,355 +1301,375 @@ RegistryBrowserWindow {
}
}
- MouseArea {
- anchors.fill: parent
- hoverEnabled: true
- }
+ Rectangle {
+ id: detailDialog
+ anchors.centerIn: parent
+ width: Math.min(SettingsMetrics.mediaMaxWidth + PopoutMetrics.contentPadding * 2, parent.width - Theme.spacingL * 2)
+ height: Math.min(parent.height - Theme.spacingL * 2, detailHeader.height + detailColumn.height + detailActions.height + PopoutMetrics.contentGap * 2 + PopoutMetrics.contentPadding * 2)
+ radius: Theme.windowRadius
+ color: Theme.foregroundColor(Theme.cardSurface, Theme.isFloatingWindow(root))
+ border.color: Theme.outlineMedium
+ border.width: Theme.layerOutlineWidth
- Item {
- id: detailHeader
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- height: Theme.buttonHeightS
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ }
- DActionButton {
- id: detailBackButton
+ Item {
+ id: detailHeader
+ anchors.top: parent.top
anchors.left: parent.left
- anchors.verticalCenter: parent.verticalCenter
- iconName: "arrow_back"
- Accessible.name: I18n.tr("Back")
- iconSize: Theme.iconSize
- iconColor: Theme.surfaceText
- onClicked: root.closePluginDetail()
- }
-
- DIcon {
- id: detailIcon
- anchors.left: detailBackButton.right
- anchors.leftMargin: Theme.spacingS
- anchors.verticalCenter: parent.verticalCenter
- name: detailPane.plugin.icon || "extension"
- size: Theme.iconSize
- color: Theme.primary
- }
-
- StyledText {
- anchors.left: detailIcon.right
- anchors.leftMargin: Theme.spacingS
- anchors.right: detailUpvoteButton.visible ? detailUpvoteButton.left : detailInstallButton.left
- anchors.rightMargin: Theme.spacingM
- anchors.verticalCenter: parent.verticalCenter
- text: detailPane.plugin.name || ""
- font.pixelSize: Theme.fontSizeLarge
- font.weight: Theme.fontWeightMedium
- color: Theme.surfaceText
- elide: Text.ElideRight
- maximumLineCount: 1
- }
-
- DButton {
- id: detailUpvoteButton
- anchors.right: detailInstallButton.left
- anchors.rightMargin: Theme.spacingS
- anchors.verticalCenter: parent.verticalCenter
- visible: !!detailPane.plugin.issueUrl
- readonly property bool upvoted: !!PluginService.upvotedPlugins[detailPane.plugin.id]
- text: PluginService.upvoteCount(detailPane.plugin)
- iconName: "thumb_up"
- enabled: !PluginService.upvotingPlugins[detailPane.plugin.id]
- backgroundColor: upvoted ? Theme.primaryContainer : SettingsMetrics.controlSurface
- textColor: upvoted ? Theme.onPrimaryContainer : Theme.surfaceText
- tooltipText: upvoted ? I18n.tr("Remove upvote", "plugin upvote button tooltip") : I18n.tr("Upvote on GitHub", "plugin upvote button tooltip")
- Accessible.name: tooltipText
- onClicked: PluginService.upvotePlugin(detailPane.plugin)
- }
-
- DButton {
- id: detailInstallButton
- readonly property bool compatible: PluginService.checkPluginCompatibility(detailPane.plugin.requires_dms)
anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- text: detailPane.plugin.installed ? I18n.tr("Installed", "adjective, plugin or theme is already installed, button state and filter chip") : compatible ? I18n.tr("Install") : I18n.tr("Requires %1", "version requirement").arg(detailPane.plugin.requires_dms || "")
- iconName: detailPane.plugin.installed ? "check" : "download"
- enabled: !detailPane.plugin.installed && compatible && !PluginService.installingPlugins[detailPane.plugin.id]
- onClicked: root.installPlugin(detailPane.plugin.id, detailPane.plugin.name, detailPane.plugin.type === "desktop")
- }
- }
+ anchors.margins: PopoutMetrics.contentPadding
+ height: Math.max(detailIcon.height, detailCloseButton.height, detailTitleColumn.implicitHeight)
- DFlickable {
- id: detailFlickable
- anchors.top: detailHeader.bottom
- anchors.topMargin: Theme.spacingM
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- clip: true
- contentHeight: detailColumn.height + Theme.spacingL
- contentWidth: width
-
- Column {
- id: detailColumn
- width: parent.width
- anchors.horizontalCenter: parent.horizontalCenter
- spacing: Theme.spacingL
-
- Rectangle {
- width: Math.min(SettingsMetrics.mediaMaxWidth, parent.width)
- height: Math.round(width * SettingsMetrics.choiceCardPreviewRatio)
- radius: Theme.cornerRadiusM
- color: SettingsMetrics.rowColor
- border.color: Theme.outlineMedium
- border.width: Theme.layerOutlineWidth
-
- ClippingRectangle {
- anchors.fill: parent
- anchors.margins: Theme.outlineWidth
- radius: Theme.cornerRadiusM - Theme.outlineWidth
- color: "transparent"
-
- CachingImage {
- id: heroImage
- anchors.fill: parent
- imagePath: detailPane.heroFallback ? PluginService.previewUrl(detailPane.plugin) : PluginService.heroUrl(detailPane.plugin)
- maxCacheSize: 1600
- fillMode: Image.PreserveAspectFit
- visible: status === Image.Ready
- onStatusChanged: {
- if (status !== Image.Error || detailPane.heroFallback)
- return;
- detailPane.heroFallback = true;
- }
- }
- }
-
- DSpinner {
- anchors.centerIn: parent
- running: heroImage.status === Image.Loading
- visible: running
- }
-
- Column {
- anchors.centerIn: parent
- spacing: Theme.spacingXS
- visible: heroImage.imagePath.length === 0 || heroImage.status === Image.Error
-
- DIcon {
- anchors.horizontalCenter: parent.horizontalCenter
- name: "image_not_supported"
- size: Theme.iconSizeLarge
- color: Theme.onSurfaceVariant
- }
-
- StyledText {
- anchors.horizontalCenter: parent.horizontalCenter
- text: heroImage.status === Image.Error ? I18n.tr("Screenshot unavailable", "plugin browser screenshot error") : I18n.tr("No screenshot provided", "plugin browser no screenshot")
- font.pixelSize: Theme.fontSizeSmall
- color: Theme.onSurfaceVariant
- }
- }
- }
-
- Flow {
- width: parent.width
- spacing: Theme.spacingXS
-
- Repeater {
- model: PluginService.badgeModel(detailPane.plugin)
-
- PluginBadge {
- required property var modelData
- label: modelData.label
- iconName: modelData.icon
- tone: PluginService.badgeTone(modelData.tone)
- }
- }
-
- Repeater {
- model: root.detailMetaBadges(detailPane.plugin)
-
- PluginBadge {
- required property var modelData
- label: modelData.label
- iconName: modelData.icon
- tone: Theme.outline
- }
- }
- }
-
- StyledText {
- width: parent.width
- text: {
- const plugin = detailPane.plugin;
- const anchor = (href, label) => `${label}`;
- const link = (href, label) => ` • ${anchor(href, label)}`;
- const authorName = plugin.author ? anchor("author:", String(plugin.author).replace(/&/g, "&").replace(/ {
- if (url === "author:")
- root.showAuthor(detailPane.plugin.author);
- else if (url === "copy:")
- root.copyLink(detailPane.plugin);
- else if (/^https:\/\//.test(url))
- Qt.openUrlExternally(url);
- }
-
- MouseArea {
- anchors.fill: parent
- cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
- acceptedButtons: Qt.NoButton
- propagateComposedEvents: true
- }
- }
-
- StyledText {
- width: parent.width
- text: detailPane.plugin.description || ""
- font.pixelSize: Theme.fontSizeMedium
- color: Theme.surfaceText
- wrapMode: Text.WordWrap
- visible: (detailPane.plugin.description || "").length > 0
+ DIcon {
+ id: detailIcon
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ name: detailPane.plugin.icon || "extension"
+ size: Theme.iconSizeLarge
+ color: Theme.primary
}
Column {
- width: parent.width
- spacing: Theme.spacingS
- visible: (detailPane.plugin.capabilities || []).length > 0
+ id: detailTitleColumn
+ anchors.left: detailIcon.right
+ anchors.leftMargin: Theme.spacingM
+ anchors.right: detailCloseButton.left
+ anchors.rightMargin: Theme.spacingM
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: Theme.spacingXXS
StyledText {
- text: I18n.tr("Capabilities", "plugin detail section")
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Theme.fontWeightMedium
- color: Theme.surfaceVariantText
- }
-
- Flow {
width: parent.width
- spacing: Theme.spacingXS
+ text: detailPane.plugin.name || ""
+ font.pixelSize: Theme.fontSizeLarge
+ font.weight: Theme.fontWeightMedium
+ color: Theme.surfaceText
+ elide: Text.ElideRight
+ maximumLineCount: 1
+ }
- Repeater {
- model: detailPane.plugin.capabilities || []
+ StyledText {
+ width: parent.width
+ text: root.authorMarkup(detailPane.plugin)
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.onSurfaceVariant
+ linkColor: Theme.primary
+ textFormat: Text.RichText
+ onLinkActivated: url => root.openDetailLink(detailPane.plugin, url)
- PluginBadge {
- required property string modelData
- label: modelData
- tone: Theme.primary
- }
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ acceptedButtons: Qt.NoButton
}
}
}
+ DActionButton {
+ id: detailCloseButton
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ buttonSize: Theme.buttonHeightXS
+ iconSize: Theme.iconSizeSmall
+ iconName: "close"
+ backgroundColor: Theme.secondaryContainer
+ iconColor: Theme.onSecondaryContainer
+ Accessible.name: I18n.tr("Close")
+ onClicked: root.closePluginDetail()
+ }
+ }
+
+ Row {
+ id: detailActions
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: PopoutMetrics.contentPadding
+ spacing: Theme.spacingS
+
+ DButton {
+ id: detailUpvoteButton
+ visible: !!detailPane.plugin.issueUrl
+ readonly property bool upvoted: !!PluginService.upvotedPlugins[detailPane.plugin.id]
+ text: PluginService.upvoteCount(detailPane.plugin)
+ iconName: "thumb_up"
+ enabled: !PluginService.upvotingPlugins[detailPane.plugin.id]
+ backgroundColor: upvoted ? Theme.primaryContainer : SettingsMetrics.controlSurface
+ textColor: upvoted ? Theme.onPrimaryContainer : Theme.surfaceText
+ tooltipText: upvoted ? I18n.tr("Remove upvote", "plugin upvote button tooltip") : I18n.tr("Upvote on GitHub", "plugin upvote button tooltip")
+ Accessible.name: tooltipText
+ onClicked: PluginService.upvotePlugin(detailPane.plugin)
+ }
+
+ DButton {
+ id: detailInstallButton
+ readonly property bool compatible: PluginService.checkPluginCompatibility(detailPane.plugin.requires_dms)
+ text: detailPane.plugin.installed ? I18n.tr("Installed", "adjective, plugin or theme is already installed, button state and filter chip") : compatible ? I18n.tr("Install") : I18n.tr("Requires %1", "version requirement").arg(detailPane.plugin.requires_dms || "")
+ iconName: detailPane.plugin.installed ? "check" : "download"
+ enabled: !detailPane.plugin.installed && compatible && !PluginService.installingPlugins[detailPane.plugin.id]
+ onClicked: root.installPlugin(detailPane.plugin.id, detailPane.plugin.name, detailPane.plugin.type === "desktop")
+ }
+ }
+
+ DFlickable {
+ id: detailFlickable
+ anchors.top: detailHeader.bottom
+ anchors.bottom: detailActions.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.topMargin: PopoutMetrics.contentGap
+ anchors.bottomMargin: PopoutMetrics.contentGap
+ anchors.leftMargin: PopoutMetrics.contentPadding
+ anchors.rightMargin: PopoutMetrics.contentPadding
+ clip: true
+ contentHeight: detailColumn.height
+ contentWidth: width
+
Column {
+ id: detailColumn
width: parent.width
- spacing: Theme.spacingS
- visible: (detailPane.plugin.permissions || []).length > 0
+ spacing: Theme.spacingL
- StyledText {
- text: I18n.tr("This plugin can", "plugin detail section, introduces the permission list")
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Theme.fontWeightMedium
- color: Theme.surfaceVariantText
- }
+ Rectangle {
+ width: parent.width
+ height: Math.round(width * SettingsMetrics.choiceCardPreviewRatio)
+ radius: Theme.cornerRadiusM
+ color: SettingsMetrics.rowColor
+ border.color: Theme.outlineMedium
+ border.width: Theme.layerOutlineWidth
- Repeater {
- model: PluginRanking.sortPermissions(detailPane.plugin.permissions)
+ ClippingRectangle {
+ anchors.fill: parent
+ anchors.margins: Theme.outlineWidth
+ radius: Theme.cornerRadiusM - Theme.outlineWidth
+ color: "transparent"
- Row {
- id: permissionRow
- required property string modelData
- readonly property bool sensitive: PluginRanking.permissionIsSensitive(modelData)
- width: parent.width
- spacing: Theme.spacingS
+ CachingImage {
+ id: heroImage
+ anchors.fill: parent
+ imagePath: detailPane.heroFallback ? PluginService.previewUrl(detailPane.plugin) : PluginService.heroUrl(detailPane.plugin)
+ maxCacheSize: 1600
+ fillMode: Image.PreserveAspectFit
+ visible: status === Image.Ready
+ onStatusChanged: {
+ if (status !== Image.Error || detailPane.heroFallback)
+ return;
+ detailPane.heroFallback = true;
+ }
+ }
+ }
+
+ DSpinner {
+ anchors.centerIn: parent
+ running: heroImage.status === Image.Loading
+ visible: running
+ }
+
+ Column {
+ anchors.centerIn: parent
+ spacing: Theme.spacingXS
+ visible: heroImage.imagePath.length === 0 || heroImage.status === Image.Error
DIcon {
- anchors.verticalCenter: parent.verticalCenter
- name: root.permissionIcon(permissionRow.modelData)
- size: Theme.iconSizeSmall
- color: permissionRow.sensitive ? Theme.warning : Theme.surfaceVariantText
+ anchors.horizontalCenter: parent.horizontalCenter
+ name: "image_not_supported"
+ size: Theme.iconSizeLarge
+ color: Theme.onSurfaceVariant
}
StyledText {
- anchors.verticalCenter: parent.verticalCenter
- width: parent.width - Theme.iconSizeSmall - Theme.spacingS
- text: root.permissionLabel(permissionRow.modelData)
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: heroImage.status === Image.Error ? I18n.tr("Screenshot unavailable", "plugin browser screenshot error") : I18n.tr("No screenshot provided", "plugin browser no screenshot")
font.pixelSize: Theme.fontSizeSmall
- color: permissionRow.sensitive ? Theme.surfaceText : Theme.surfaceVariantText
- wrapMode: Text.WordWrap
+ color: Theme.onSurfaceVariant
}
}
}
- }
-
- Column {
- width: parent.width
- spacing: Theme.spacingS
- visible: (detailPane.plugin.dependencies || []).length > 0
-
- StyledText {
- text: I18n.tr("Needs", "plugin detail section, introduces the programs a plugin depends on")
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Theme.fontWeightMedium
- color: Theme.surfaceVariantText
- }
Flow {
width: parent.width
spacing: Theme.spacingXS
Repeater {
- model: PluginRanking.dependencyStates(detailPane.plugin.dependencies, root.foundCommands, root.checkedCommands)
+ model: PluginService.badgeModel(detailPane.plugin)
PluginBadge {
required property var modelData
- label: modelData.state === "missing" ? I18n.tr("%1 not found", "plugin dependency missing, %1 is a program name").arg(modelData.name) : modelData.name
- iconName: modelData.state === "ok" ? "check_circle" : modelData.state === "missing" ? "warning" : "package_2"
- tone: modelData.state === "ok" ? Theme.primary : modelData.state === "missing" ? Theme.warning : Theme.outline
+ label: modelData.label
+ iconName: modelData.icon
+ tone: PluginService.badgeTone(modelData.tone)
+ }
+ }
+
+ Repeater {
+ model: root.detailMetaBadges(detailPane.plugin)
+
+ PluginBadge {
+ required property var modelData
+ label: modelData.label
+ iconName: modelData.icon
+ tone: Theme.outline
}
}
}
- }
-
- Column {
- width: parent.width
- spacing: Theme.spacingS
- visible: root.relatedPlugins(detailPane.plugin).length > 0
StyledText {
- text: I18n.tr("Related: %1", "related plugins").arg("").trim()
+ width: parent.width
+ text: root.detailLinksMarkup(detailPane.plugin)
+ visible: text !== ""
font.pixelSize: Theme.fontSizeSmall
- font.weight: Theme.fontWeightMedium
- color: Theme.surfaceVariantText
+ color: Theme.onSurfaceVariant
+ linkColor: Theme.primary
+ textFormat: Text.RichText
+ wrapMode: Text.WordWrap
+ onLinkActivated: url => root.openDetailLink(detailPane.plugin, url)
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ acceptedButtons: Qt.NoButton
+ }
}
- Flow {
+ StyledText {
width: parent.width
- spacing: Theme.spacingXS
+ text: detailPane.plugin.description || ""
+ font.pixelSize: Theme.fontSizeMedium
+ color: Theme.surfaceText
+ wrapMode: Text.WordWrap
+ visible: (detailPane.plugin.description || "").length > 0
+ }
+
+ Column {
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: (detailPane.plugin.capabilities || []).length > 0
+
+ StyledText {
+ text: I18n.tr("Capabilities", "plugin detail section")
+ font.pixelSize: Theme.fontSizeSmall
+ font.weight: Theme.fontWeightMedium
+ color: Theme.surfaceVariantText
+ }
+
+ Flow {
+ width: parent.width
+ spacing: Theme.spacingXS
+
+ Repeater {
+ model: detailPane.plugin.capabilities || []
+
+ PluginBadge {
+ required property string modelData
+ label: modelData
+ tone: Theme.primary
+ }
+ }
+ }
+ }
+
+ Column {
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: (detailPane.plugin.permissions || []).length > 0
+
+ StyledText {
+ text: I18n.tr("This plugin can", "plugin detail section, introduces the permission list")
+ font.pixelSize: Theme.fontSizeSmall
+ font.weight: Theme.fontWeightMedium
+ color: Theme.surfaceVariantText
+ }
Repeater {
- model: root.relatedPlugins(detailPane.plugin)
+ model: PluginRanking.sortPermissions(detailPane.plugin.permissions)
- DButton {
- required property var modelData
- text: modelData.name
- iconName: "extension"
- backgroundColor: Theme.secondaryContainer
- textColor: Theme.onSecondaryContainer
- enabled: modelData.key !== ""
- onClicked: root.detailPluginId = modelData.key
+ Row {
+ id: permissionRow
+ required property string modelData
+ readonly property bool sensitive: PluginRanking.permissionIsSensitive(modelData)
+ width: parent.width
+ spacing: Theme.spacingS
+
+ DIcon {
+ anchors.verticalCenter: parent.verticalCenter
+ name: root.permissionIcon(permissionRow.modelData)
+ size: Theme.iconSizeSmall
+ color: permissionRow.sensitive ? Theme.warning : Theme.surfaceVariantText
+ }
+
+ StyledText {
+ anchors.verticalCenter: parent.verticalCenter
+ width: parent.width - Theme.iconSizeSmall - Theme.spacingS
+ text: root.permissionLabel(permissionRow.modelData)
+ font.pixelSize: Theme.fontSizeSmall
+ color: permissionRow.sensitive ? Theme.surfaceText : Theme.surfaceVariantText
+ wrapMode: Text.WordWrap
+ }
+ }
+ }
+ }
+
+ Column {
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: (detailPane.plugin.dependencies || []).length > 0
+
+ StyledText {
+ text: I18n.tr("Needs", "plugin detail section, introduces the programs a plugin depends on")
+ font.pixelSize: Theme.fontSizeSmall
+ font.weight: Theme.fontWeightMedium
+ color: Theme.surfaceVariantText
+ }
+
+ Flow {
+ width: parent.width
+ spacing: Theme.spacingXS
+
+ Repeater {
+ model: PluginRanking.dependencyStates(detailPane.plugin.dependencies, root.foundCommands, root.checkedCommands)
+
+ PluginBadge {
+ required property var modelData
+ label: modelData.state === "missing" ? I18n.tr("%1 not found", "plugin dependency missing, %1 is a program name").arg(modelData.name) : modelData.name
+ iconName: modelData.state === "ok" ? "check_circle" : modelData.state === "missing" ? "warning" : "package_2"
+ tone: modelData.state === "ok" ? Theme.primary : modelData.state === "missing" ? Theme.warning : Theme.outline
+ }
+ }
+ }
+ }
+
+ Column {
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: root.relatedPlugins(detailPane.plugin).length > 0
+
+ StyledText {
+ text: I18n.tr("Related: %1", "related plugins").arg("").trim()
+ font.pixelSize: Theme.fontSizeSmall
+ font.weight: Theme.fontWeightMedium
+ color: Theme.surfaceVariantText
+ }
+
+ Flow {
+ width: parent.width
+ spacing: Theme.spacingXS
+
+ Repeater {
+ model: root.relatedPlugins(detailPane.plugin)
+
+ DButton {
+ required property var modelData
+ text: modelData.name
+ iconName: "extension"
+ buttonHeight: Theme.buttonHeightXS
+ backgroundColor: Theme.secondaryContainer
+ textColor: Theme.onSecondaryContainer
+ enabled: modelData.key !== ""
+ onClicked: root.detailPluginId = modelData.key
+ }
}
}
}
diff --git a/quickshell/Modules/Settings/RegistryBrowserWindow.qml b/quickshell/Modules/Settings/RegistryBrowserWindow.qml
index eff9e3d53..fd5b03e00 100644
--- a/quickshell/Modules/Settings/RegistryBrowserWindow.qml
+++ b/quickshell/Modules/Settings/RegistryBrowserWindow.qml
@@ -194,8 +194,6 @@ DFloatingWindow {
Item {
id: browserBody
anchors.fill: parent
- opacity: 1 - overlaySlot.opacity
- visible: opacity > 0
Column {
id: aboveSearchSlot
@@ -253,20 +251,40 @@ DFloatingWindow {
}
}
}
+ }
+
+ Item {
+ id: overlayLayer
+ LayoutMirroring.enabled: I18n.isRtl
+ LayoutMirroring.childrenInherit: true
+ anchors.fill: parent
+ opacity: root.overlayActive ? 1 : 0
+ visible: opacity > 0
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: Theme.expressiveDurations.expressiveEffects
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
+ }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: Theme.scrimColor
+ opacity: Theme.scrimAlpha
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: root.handleEscape()
+ onWheel: wheel => wheel.accepted = true
+ }
Item {
id: overlaySlot
anchors.fill: parent
- opacity: root.overlayActive ? 1 : 0
- visible: opacity > 0
-
- Behavior on opacity {
- NumberAnimation {
- duration: Theme.expressiveDurations.expressiveEffects
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
- }
- }
}
}
}