mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-25 05:52:50 -05:00
279 lines
12 KiB
QML
279 lines
12 KiB
QML
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
StyledRect {
|
|
id: root
|
|
|
|
required property string outputName
|
|
required property var outputData
|
|
property bool isConnected: outputData?.connected ?? false
|
|
|
|
width: parent.width
|
|
height: settingsColumn.implicitHeight + Theme.spacingM * 2
|
|
radius: Theme.cornerRadius
|
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, isConnected ? 0.5 : 0.3)
|
|
border.color: Theme.withAlpha(Theme.outline, 0.3)
|
|
border.width: 1
|
|
opacity: isConnected ? 1.0 : 0.7
|
|
|
|
Column {
|
|
id: settingsColumn
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingM
|
|
spacing: Theme.spacingS
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
DankIcon {
|
|
name: root.isConnected ? "desktop_windows" : "desktop_access_disabled"
|
|
size: Theme.iconSize - 4
|
|
color: root.isConnected ? Theme.primary : Theme.surfaceVariantText
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
Column {
|
|
width: parent.width - Theme.iconSize - Theme.spacingM - (disconnectedBadge.visible ? disconnectedBadge.width + Theme.spacingS : 0)
|
|
spacing: 2
|
|
|
|
StyledText {
|
|
text: DisplayConfigState.getOutputDisplayName(root.outputData, root.outputName)
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: root.isConnected ? Theme.surfaceText : Theme.surfaceVariantText
|
|
}
|
|
|
|
StyledText {
|
|
text: (root.outputData?.model ?? "") + (root.outputData?.make ? " - " + root.outputData.make : "")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: disconnectedBadge
|
|
visible: !root.isConnected
|
|
width: disconnectedText.implicitWidth + Theme.spacingM
|
|
height: disconnectedText.implicitHeight + Theme.spacingXS
|
|
radius: height / 2
|
|
color: Theme.withAlpha(Theme.outline, 0.3)
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
StyledText {
|
|
id: disconnectedText
|
|
text: I18n.tr("Disconnected")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
anchors.centerIn: parent
|
|
}
|
|
}
|
|
}
|
|
|
|
DankDropdown {
|
|
width: parent.width
|
|
text: I18n.tr("Resolution & Refresh")
|
|
visible: root.isConnected
|
|
currentValue: {
|
|
const pendingMode = DisplayConfigState.getPendingValue(root.outputName, "mode");
|
|
if (pendingMode)
|
|
return pendingMode;
|
|
const data = DisplayConfigState.outputs[root.outputName];
|
|
if (!data?.modes || data?.current_mode === undefined)
|
|
return "Auto";
|
|
const mode = data.modes[data.current_mode];
|
|
return mode ? DisplayConfigState.formatMode(mode) : "Auto";
|
|
}
|
|
options: {
|
|
const data = DisplayConfigState.outputs[root.outputName];
|
|
if (!data?.modes)
|
|
return ["Auto"];
|
|
const opts = [];
|
|
for (var i = 0; i < data.modes.length; i++) {
|
|
opts.push(DisplayConfigState.formatMode(data.modes[i]));
|
|
}
|
|
return opts;
|
|
}
|
|
onValueChanged: value => DisplayConfigState.setPendingChange(root.outputName, "mode", value)
|
|
}
|
|
|
|
StyledText {
|
|
visible: !root.isConnected
|
|
text: I18n.tr("Configuration will be preserved when this display reconnects")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
visible: root.isConnected
|
|
|
|
Column {
|
|
width: (parent.width - Theme.spacingM) / 2
|
|
spacing: Theme.spacingXS
|
|
|
|
StyledText {
|
|
text: I18n.tr("Scale")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
}
|
|
|
|
Item {
|
|
id: scaleContainer
|
|
width: parent.width
|
|
height: scaleDropdown.visible ? scaleDropdown.height : scaleInput.height
|
|
|
|
property bool customMode: false
|
|
property string currentScale: {
|
|
const pendingScale = DisplayConfigState.getPendingValue(root.outputName, "scale");
|
|
if (pendingScale !== undefined)
|
|
return parseFloat(pendingScale.toFixed(2)).toString();
|
|
const scale = DisplayConfigState.outputs[root.outputName]?.logical?.scale ?? 1.0;
|
|
return parseFloat(scale.toFixed(2)).toString();
|
|
}
|
|
|
|
DankDropdown {
|
|
id: scaleDropdown
|
|
width: parent.width
|
|
dropdownWidth: parent.width
|
|
visible: !scaleContainer.customMode
|
|
currentValue: scaleContainer.currentScale
|
|
options: {
|
|
const standard = ["0.5", "0.75", "1", "1.25", "1.5", "1.75", "2", "2.5", "3", I18n.tr("Custom...")];
|
|
const current = scaleContainer.currentScale;
|
|
if (standard.slice(0, -1).includes(current))
|
|
return standard;
|
|
const opts = [...standard.slice(0, -1), current, standard[standard.length - 1]];
|
|
return opts.sort((a, b) => {
|
|
if (a === I18n.tr("Custom..."))
|
|
return 1;
|
|
if (b === I18n.tr("Custom..."))
|
|
return -1;
|
|
return parseFloat(a) - parseFloat(b);
|
|
});
|
|
}
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Custom...")) {
|
|
scaleContainer.customMode = true;
|
|
scaleInput.text = scaleContainer.currentScale;
|
|
scaleInput.forceActiveFocus();
|
|
scaleInput.selectAll();
|
|
return;
|
|
}
|
|
DisplayConfigState.setPendingChange(root.outputName, "scale", parseFloat(value));
|
|
}
|
|
}
|
|
|
|
DankTextField {
|
|
id: scaleInput
|
|
width: parent.width
|
|
height: 40
|
|
visible: scaleContainer.customMode
|
|
placeholderText: "0.5 - 4.0"
|
|
|
|
function applyValue() {
|
|
const val = parseFloat(text);
|
|
if (isNaN(val) || val < 0.25 || val > 4) {
|
|
text = scaleContainer.currentScale;
|
|
scaleContainer.customMode = false;
|
|
return;
|
|
}
|
|
DisplayConfigState.setPendingChange(root.outputName, "scale", parseFloat(val.toFixed(2)));
|
|
scaleContainer.customMode = false;
|
|
}
|
|
|
|
onAccepted: applyValue()
|
|
onEditingFinished: applyValue()
|
|
Keys.onEscapePressed: {
|
|
text = scaleContainer.currentScale;
|
|
scaleContainer.customMode = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: (parent.width - Theme.spacingM) / 2
|
|
spacing: Theme.spacingXS
|
|
|
|
StyledText {
|
|
text: I18n.tr("Transform")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
}
|
|
|
|
DankDropdown {
|
|
width: parent.width
|
|
dropdownWidth: parent.width
|
|
currentValue: {
|
|
const pendingTransform = DisplayConfigState.getPendingValue(root.outputName, "transform");
|
|
if (pendingTransform)
|
|
return DisplayConfigState.getTransformLabel(pendingTransform);
|
|
const data = DisplayConfigState.outputs[root.outputName];
|
|
return DisplayConfigState.getTransformLabel(data?.logical?.transform ?? "Normal");
|
|
}
|
|
options: [I18n.tr("Normal"), I18n.tr("90°"), I18n.tr("180°"), I18n.tr("270°"), I18n.tr("Flipped"), I18n.tr("Flipped 90°"), I18n.tr("Flipped 180°"), I18n.tr("Flipped 270°")]
|
|
onValueChanged: value => DisplayConfigState.setPendingChange(root.outputName, "transform", DisplayConfigState.getTransformValue(value))
|
|
}
|
|
}
|
|
}
|
|
|
|
DankToggle {
|
|
width: parent.width
|
|
text: I18n.tr("Variable Refresh Rate")
|
|
visible: root.isConnected && !CompositorService.isDwl && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false)
|
|
checked: {
|
|
const pendingVrr = DisplayConfigState.getPendingValue(root.outputName, "vrr");
|
|
if (pendingVrr !== undefined)
|
|
return pendingVrr;
|
|
return DisplayConfigState.outputs[root.outputName]?.vrr_enabled ?? false;
|
|
}
|
|
onToggled: checked => DisplayConfigState.setPendingChange(root.outputName, "vrr", checked)
|
|
}
|
|
|
|
DankToggle {
|
|
width: parent.width
|
|
text: I18n.tr("VRR On-Demand")
|
|
description: I18n.tr("VRR activates only when applications request it")
|
|
visible: root.isConnected && CompositorService.isNiri && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false)
|
|
checked: DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "vrrOnDemand", false)
|
|
onToggled: checked => DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "vrrOnDemand", checked)
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: 1
|
|
color: Theme.withAlpha(Theme.outline, 0.2)
|
|
visible: compositorSettingsLoader.active
|
|
}
|
|
|
|
Loader {
|
|
id: compositorSettingsLoader
|
|
width: parent.width
|
|
active: root.isConnected && compositorSettingsSource !== ""
|
|
source: compositorSettingsSource
|
|
|
|
property string compositorSettingsSource: {
|
|
switch (CompositorService.compositor) {
|
|
case "niri":
|
|
return "NiriOutputSettings.qml";
|
|
case "hyprland":
|
|
return "";
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
onLoaded: {
|
|
item.outputName = root.outputName;
|
|
item.outputData = root.outputData;
|
|
}
|
|
}
|
|
}
|
|
}
|