1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00
Files
DankMaterialShell/quickshell/Modules/Settings/DisplayConfig/NiriOutputSettings.qml
2025-12-16 13:36:00 -05:00

369 lines
16 KiB
QML

import QtQuick
import qs.Common
import qs.Widgets
Column {
id: root
property string outputName: ""
property var outputData: null
property bool expanded: false
width: parent.width
spacing: 0
Rectangle {
width: parent.width
height: headerRow.implicitHeight + Theme.spacingS * 2
color: headerMouse.containsMouse ? Theme.withAlpha(Theme.primary, 0.1) : "transparent"
radius: Theme.cornerRadius / 2
Row {
id: headerRow
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
spacing: Theme.spacingS
DankIcon {
name: root.expanded ? "expand_more" : "chevron_right"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Compositor Settings")
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: headerMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.expanded = !root.expanded
}
}
Column {
width: parent.width
spacing: Theme.spacingS
visible: root.expanded
topPadding: Theme.spacingS
DankToggle {
width: parent.width
text: I18n.tr("Disable Output")
checked: DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "disabled", false)
onToggled: checked => DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "disabled", checked)
}
DankToggle {
width: parent.width
text: I18n.tr("Focus at Startup")
checked: DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "focusAtStartup", false)
onToggled: checked => DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "focusAtStartup", checked)
}
DankDropdown {
width: parent.width
text: I18n.tr("Hot Corners")
addHorizontalPadding: true
property var hotCornersData: {
void (DisplayConfigState.pendingNiriChanges);
return DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "hotCorners", null);
}
currentValue: {
if (!hotCornersData)
return I18n.tr("Inherit");
if (hotCornersData.off)
return I18n.tr("Off");
const corners = hotCornersData.corners || [];
if (corners.length === 0)
return I18n.tr("Inherit");
if (corners.length === 4)
return I18n.tr("All");
return I18n.tr("Select...");
}
options: [I18n.tr("Inherit"), I18n.tr("Off"), I18n.tr("All"), I18n.tr("Select...")]
onValueChanged: value => {
switch (value) {
case I18n.tr("Inherit"):
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "hotCorners", null);
break;
case I18n.tr("Off"):
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "hotCorners", {
"off": true
});
break;
case I18n.tr("All"):
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "hotCorners", {
"corners": ["top-left", "top-right", "bottom-left", "bottom-right"]
});
break;
case I18n.tr("Select..."):
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "hotCorners", {
"corners": []
});
break;
}
}
}
Item {
width: parent.width
height: hotCornersGroup.implicitHeight
clip: true
property var hotCornersData: {
void (DisplayConfigState.pendingNiriChanges);
return DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "hotCorners", null);
}
visible: hotCornersData && !hotCornersData.off && hotCornersData.corners !== undefined
DankButtonGroup {
id: hotCornersGroup
anchors.horizontalCenter: parent.horizontalCenter
selectionMode: "multi"
checkEnabled: false
buttonHeight: 32
buttonPadding: parent.width < 400 ? Theme.spacingXS : Theme.spacingM
minButtonWidth: parent.width < 400 ? 28 : 56
textSize: parent.width < 400 ? 11 : Theme.fontSizeMedium
model: [I18n.tr("Top Left"), I18n.tr("Top Right"), I18n.tr("Bottom Left"), I18n.tr("Bottom Right")]
property var cornerKeys: ["top-left", "top-right", "bottom-left", "bottom-right"]
currentSelection: {
const hcData = parent.hotCornersData;
if (!hcData?.corners)
return [];
return hcData.corners.map(key => {
const idx = cornerKeys.indexOf(key);
return idx >= 0 ? model[idx] : null;
}).filter(v => v !== null);
}
onSelectionChanged: (index, selected) => {
const corners = currentSelection.map(label => {
const idx = model.indexOf(label);
return idx >= 0 ? cornerKeys[idx] : null;
}).filter(v => v !== null);
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "hotCorners", {
"corners": corners
});
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.withAlpha(Theme.outline, 0.15)
}
Item {
width: parent.width
height: layoutColumn.implicitHeight
Column {
id: layoutColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingS
Column {
width: parent.width
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Layout Overrides")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("Override global layout settings for this output")
font.pixelSize: Theme.fontSizeSmall
color: Theme.withAlpha(Theme.surfaceVariantText, 0.7)
wrapMode: Text.WordWrap
width: parent.width
}
}
Row {
width: parent.width
spacing: Theme.spacingM
Column {
width: (parent.width - Theme.spacingM) / 2
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Window Gaps (px)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
DankTextField {
width: parent.width
height: 40
placeholderText: I18n.tr("Inherit")
text: {
const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null);
if (layout?.gaps === undefined)
return "";
return layout.gaps.toString();
}
onEditingFinished: {
const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", {}) || {};
const trimmed = text.trim();
if (!trimmed) {
delete layout.gaps;
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", Object.keys(layout).length > 0 ? layout : null);
return;
}
const val = parseInt(trimmed);
if (isNaN(val) || val < 0)
return;
layout.gaps = val;
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", layout);
}
}
}
Column {
width: (parent.width - Theme.spacingM) / 2
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Default Width (%)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
DankTextField {
width: parent.width
height: 40
placeholderText: I18n.tr("Inherit")
text: {
const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null);
if (!layout?.defaultColumnWidth)
return "";
if (layout.defaultColumnWidth.type !== "proportion")
return "";
const percent = layout.defaultColumnWidth.value * 100;
return parseFloat(percent.toFixed(4)).toString();
}
onEditingFinished: {
const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", {}) || {};
const trimmed = text.trim().replace("%", "");
if (!trimmed) {
delete layout.defaultColumnWidth;
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", Object.keys(layout).length > 0 ? layout : null);
return;
}
const val = parseFloat(trimmed);
if (isNaN(val) || val <= 0 || val > 100)
return;
layout.defaultColumnWidth = {
"type": "proportion",
"value": parseFloat((val / 100).toFixed(6))
};
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", layout);
}
}
}
}
Column {
width: parent.width
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Preset Widths (%)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: "e.g. 33.33, 50, 66.67"
font.pixelSize: Theme.fontSizeSmall
color: Theme.withAlpha(Theme.surfaceVariantText, 0.7)
}
DankTextField {
width: parent.width
height: 40
placeholderText: I18n.tr("Inherit")
text: {
const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null);
const presets = layout?.presetColumnWidths || [];
if (presets.length === 0)
return "";
return presets.filter(p => p.type === "proportion").map(p => parseFloat((p.value * 100).toFixed(4))).join(", ");
}
onEditingFinished: {
const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", {}) || {};
const trimmed = text.trim();
if (!trimmed) {
delete layout.presetColumnWidths;
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", Object.keys(layout).length > 0 ? layout : null);
return;
}
const parts = trimmed.split(/[,\s]+/).filter(s => s);
const presets = [];
for (const part of parts) {
const val = parseFloat(part.replace("%", ""));
if (!isNaN(val) && val > 0 && val <= 100)
presets.push({
"type": "proportion",
"value": parseFloat((val / 100).toFixed(6))
});
}
if (presets.length === 0) {
delete layout.presetColumnWidths;
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", Object.keys(layout).length > 0 ? layout : null);
return;
}
presets.sort((a, b) => a.value - b.value);
layout.presetColumnWidths = presets;
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", layout);
}
}
}
}
}
DankToggle {
width: parent.width
text: I18n.tr("Center Single Column")
property var layoutData: DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null)
checked: layoutData?.alwaysCenterSingleColumn ?? false
onToggled: checked => {
const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", {}) || {};
if (checked) {
layout.alwaysCenterSingleColumn = true;
} else {
delete layout.alwaysCenterSingleColumn;
}
DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "layout", Object.keys(layout).length > 0 ? layout : null);
}
}
}
}