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

dwl/mangowc: add layout switcher and viewer widget

This commit is contained in:
bbedward
2025-11-13 12:44:56 -05:00
parent cf75c1aad0
commit 5d2f5557e5
17 changed files with 1291 additions and 229 deletions

View File

@@ -14,6 +14,7 @@ Item {
required property var processListPopoutLoader
required property var notificationCenterLoader
required property var batteryPopoutLoader
required property var layoutPopoutLoader
required property var vpnPopoutLoader
required property var controlCenterLoader
required property var clipboardHistoryModalPopup
@@ -95,6 +96,9 @@ Item {
}, {
"loader": batteryPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": layoutPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": vpnPopoutLoader,
"prop": "shouldBeVisible"

View File

@@ -427,6 +427,9 @@ Item {
}, {
"loader": batteryPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": layoutPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": vpnPopoutLoader,
"prop": "shouldBeVisible"
@@ -839,6 +842,7 @@ Item {
"gpuTemp": gpuTempComponent,
"notificationButton": notificationButtonComponent,
"battery": batteryComponent,
"layout": layoutComponent,
"controlCenterButton": controlCenterButtonComponent,
"idleInhibitor": idleInhibitorComponent,
"spacer": spacerComponent,
@@ -878,6 +882,7 @@ Item {
"gpuTempComponent": gpuTempComponent,
"notificationButtonComponent": notificationButtonComponent,
"batteryComponent": batteryComponent,
"layoutComponent": layoutComponent,
"controlCenterButtonComponent": controlCenterButtonComponent,
"idleInhibitorComponent": idleInhibitorComponent,
"spacerComponent": spacerComponent,
@@ -1342,6 +1347,27 @@ Item {
}
}
Component {
id: layoutComponent
DWLLayout {
layoutPopupVisible: layoutPopoutLoader.item ? layoutPopoutLoader.item.shouldBeVisible : false
widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "center"
popoutTarget: {
layoutPopoutLoader.active = true
return layoutPopoutLoader.item
}
parentScreen: barWindow.screen
onToggleLayoutPopup: {
layoutPopoutLoader.active = true
layoutPopoutLoader.item?.toggle()
}
}
}
Component {
id: vpnComponent

View File

@@ -0,0 +1,314 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
DankPopout {
id: root
layerNamespace: "dms:layout"
property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) {
triggerX = x
triggerY = y
triggerWidth = width
triggerSection = section
triggerScreen = screen
updateOutputState()
}
function updateOutputState() {
if (triggerScreen && DwlService.dwlAvailable) {
outputState = DwlService.getOutputState(triggerScreen.name)
} else {
outputState = null
}
}
property var outputState: null
property string currentLayoutSymbol: outputState?.layoutSymbol || ""
readonly property var layoutNames: ({
"CT": I18n.tr("Center Tiling"),
"G": I18n.tr("Grid"),
"K": I18n.tr("Deck"),
"M": I18n.tr("Monocle"),
"RT": I18n.tr("Right Tiling"),
"S": I18n.tr("Scrolling"),
"T": I18n.tr("Tiling"),
"VG": I18n.tr("Vertical Grid"),
"VK": I18n.tr("Vertical Deck"),
"VS": I18n.tr("Vertical Scrolling"),
"VT": I18n.tr("Vertical Tiling")
})
readonly property var layoutIcons: ({
"CT": "view_compact",
"G": "grid_view",
"K": "layers",
"M": "fullscreen",
"RT": "view_sidebar",
"S": "view_carousel",
"T": "view_quilt",
"VG": "grid_on",
"VK": "view_day",
"VS": "scrollable_header",
"VT": "clarify"
})
function getLayoutName(symbol) {
return layoutNames[symbol] || symbol
}
function getLayoutIcon(symbol) {
return layoutIcons[symbol] || "view_quilt"
}
Connections {
target: DwlService
function onStateChanged() {
updateOutputState()
}
}
onShouldBeVisibleChanged: {
if (shouldBeVisible) {
updateOutputState()
}
}
Component.onCompleted: {
updateOutputState()
}
popupWidth: 300
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 550
triggerX: Screen.width - 380 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
triggerWidth: 70
positioning: ""
screen: triggerScreen
shouldBeVisible: false
visible: shouldBeVisible
content: Component {
Rectangle {
id: layoutContent
implicitHeight: contentColumn.implicitHeight + Theme.spacingL * 2
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
border.color: Theme.outlineMedium
border.width: 0
antialiasing: true
smooth: true
focus: true
Component.onCompleted: {
if (root.shouldBeVisible) {
forceActiveFocus()
}
}
Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) {
root.close()
event.accepted = true
}
}
Connections {
target: root
function onShouldBeVisibleChanged() {
if (root.shouldBeVisible) {
Qt.callLater(() => {
layoutContent.forceActiveFocus()
})
}
}
}
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: Theme.outlineStrong
border.width: 0
radius: parent.radius
z: -1
}
Column {
id: contentColumn
width: parent.width - Theme.spacingL * 2
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
height: 40
spacing: Theme.spacingM
DankIcon {
name: "view_quilt"
size: Theme.iconSizeLarge
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
width: parent.width - Theme.iconSizeLarge - 32 - Theme.spacingM * 2
StyledText {
text: I18n.tr("Layout")
font.pixelSize: Theme.fontSizeXLarge
color: Theme.surfaceText
font.weight: Font.Bold
}
StyledText {
text: root.currentLayoutSymbol
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}
}
Rectangle {
width: 32
height: 32
radius: 16
color: closeLayoutArea.containsMouse ? Theme.errorHover : "transparent"
anchors.top: parent.top
DankIcon {
anchors.centerIn: parent
name: "close"
size: Theme.iconSize - 4
color: closeLayoutArea.containsMouse ? Theme.error : Theme.surfaceText
}
MouseArea {
id: closeLayoutArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
root.close()
}
}
}
}
StyledText {
text: I18n.tr("Available Layouts")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
font.weight: Font.Medium
}
Column {
width: parent.width
spacing: Theme.spacingS
Repeater {
model: DwlService.layouts
delegate: Rectangle {
required property string modelData
required property int index
property bool isActive: modelData === root.currentLayoutSymbol
width: parent.width
height: 40
radius: Theme.cornerRadius
color: layoutArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: root.getLayoutIcon(modelData)
size: 20
color: parent.parent.isActive ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 2
StyledText {
text: root.getLayoutName(modelData)
font.pixelSize: Theme.fontSizeSmall
color: parent.parent.parent.isActive ? Theme.primary : Theme.surfaceText
font.weight: parent.parent.parent.isActive ? Font.Medium : Font.Normal
}
StyledText {
text: modelData
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}
}
}
MouseArea {
id: layoutArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
console.log("DWLLayoutPopout: Pressed layout", modelData, "at index", index)
console.log("DWLLayoutPopout: triggerScreen:", root.triggerScreen, "dwlAvailable:", DwlService.dwlAvailable)
if (!root.triggerScreen) {
console.error("DWLLayoutPopout: triggerScreen is null!")
return
}
if (!DwlService.dwlAvailable) {
console.error("DWLLayoutPopout: DwlService not available!")
return
}
console.log("DWLLayoutPopout: CALLING setLayout with output:", root.triggerScreen.name, "index:", index)
DwlService.setLayout(root.triggerScreen.name, index)
root.close()
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
StyledText {
text: I18n.tr("Right-click bar widget to cycle")
font.pixelSize: Theme.fontSizeSmall
color: Theme.outline
width: parent.width
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
}
}
}

View File

@@ -200,7 +200,8 @@ Loader {
"vpn": components.vpnComponent,
"notepadButton": components.notepadButtonComponent,
"colorPicker": components.colorPickerComponent,
"systemUpdate": components.systemUpdateComponent
"systemUpdate": components.systemUpdateComponent,
"layout": components.layoutComponent
}
if (componentMap[widgetId]) {
@@ -220,7 +221,8 @@ Loader {
"memUsage": dgopAvailable,
"cpuTemp": dgopAvailable,
"gpuTemp": dgopAvailable,
"network_speed_monitor": dgopAvailable
"network_speed_monitor": dgopAvailable,
"layout": CompositorService.isDwl && DwlService.dwlAvailable
}
return widgetVisibility[widgetId] ?? true

View File

@@ -0,0 +1,116 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Modules.Plugins
import qs.Services
import qs.Widgets
BasePill {
id: layout
property bool layoutPopupVisible: false
property var popoutTarget: null
signal toggleLayoutPopup()
visible: CompositorService.isDwl && DwlService.dwlAvailable
property var outputState: parentScreen ? DwlService.getOutputState(parentScreen.name) : null
property string currentLayoutSymbol: outputState?.layoutSymbol || ""
property int currentLayoutIndex: outputState?.layout || 0
readonly property var layoutIcons: ({
"CT": "view_compact",
"G": "grid_view",
"K": "layers",
"M": "fullscreen",
"RT": "view_sidebar",
"S": "view_carousel",
"T": "view_quilt",
"VG": "grid_on",
"VK": "view_day",
"VS": "scrollable_header",
"VT": "clarify"
})
function getLayoutIcon(symbol) {
return layoutIcons[symbol] || "view_quilt"
}
Connections {
target: DwlService
function onStateChanged() {
outputState = parentScreen ? DwlService.getOutputState(parentScreen.name) : null
}
}
content: Component {
Item {
implicitWidth: layout.isVerticalOrientation ? (layout.widgetThickness - layout.horizontalPadding * 2) : layoutContent.implicitWidth
implicitHeight: layout.isVerticalOrientation ? layoutColumn.implicitHeight : (layout.widgetThickness - layout.horizontalPadding * 2)
Column {
id: layoutColumn
visible: layout.isVerticalOrientation
anchors.centerIn: parent
spacing: 1
DankIcon {
name: layout.getLayoutIcon(layout.currentLayoutSymbol)
size: Theme.barIconSize(layout.barThickness)
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: layout.currentLayoutSymbol
font.pixelSize: Theme.barTextSize(layout.barThickness)
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
Row {
id: layoutContent
visible: !layout.isVerticalOrientation
anchors.centerIn: parent
spacing: SettingsData.dankBarNoBackground ? 1 : 2
DankIcon {
name: layout.getLayoutIcon(layout.currentLayoutSymbol)
size: Theme.barIconSize(layout.barThickness, -4)
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: layout.currentLayoutSymbol
font.pixelSize: Theme.barTextSize(layout.barThickness)
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
onClicked: {
if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = layout.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, layout.visualWidth)
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
toggleLayoutPopup()
}
onRightClicked: {
if (!parentScreen || !DwlService.dwlAvailable || DwlService.layouts.length === 0) {
return
}
const currentIndex = layout.currentLayoutIndex
const nextIndex = (currentIndex + 1) % DwlService.layouts.length
DwlService.setLayout(parentScreen.name, nextIndex)
}
}