summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/dms/Modules/Settings/DisplayConfigTab.qml
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-08 19:50:49 +0200
committerNippy <nippy@rp1.hu>2026-05-08 19:50:49 +0200
commit3462bcc46ecf74f576ea4397f16492c16bd75b10 (patch)
treec5ab7aef4498647e057bda8d49c11e5f9dda74c9 /raveos-hyprland-theme/theme-data/dms/Modules/Settings/DisplayConfigTab.qml
parent56616f693b4f263cbf0d47da43b67424efa6022d (diff)
downloadRaveOS-PKGBUILD-3462bcc46ecf74f576ea4397f16492c16bd75b10.tar.gz
RaveOS-PKGBUILD-3462bcc46ecf74f576ea4397f16492c16bd75b10.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/dms/Modules/Settings/DisplayConfigTab.qml')
-rw-r--r--raveos-hyprland-theme/theme-data/dms/Modules/Settings/DisplayConfigTab.qml540
1 files changed, 540 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/dms/Modules/Settings/DisplayConfigTab.qml b/raveos-hyprland-theme/theme-data/dms/Modules/Settings/DisplayConfigTab.qml
new file mode 100644
index 0000000..61b373d
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/dms/Modules/Settings/DisplayConfigTab.qml
@@ -0,0 +1,540 @@
+import QtQuick
+import qs.Common
+import qs.Modals
+import qs.Services
+import qs.Widgets
+import qs.Modules.Settings.DisplayConfig
+
+Item {
+ id: root
+
+ LayoutMirroring.enabled: I18n.isRtl
+ LayoutMirroring.childrenInherit: true
+
+ property string selectedProfileId: SettingsData.getActiveDisplayProfile(CompositorService.compositor)
+ property bool showNewProfileDialog: false
+ property bool showDeleteConfirmDialog: false
+ property bool showRenameDialog: false
+ property string newProfileName: ""
+ property string renameProfileName: ""
+
+ function getProfileOptions() {
+ const profiles = DisplayConfigState.validatedProfiles;
+ const options = [];
+ for (const id in profiles)
+ options.push(profiles[id].name);
+ return options;
+ }
+
+ function getProfileIds() {
+ return Object.keys(DisplayConfigState.validatedProfiles);
+ }
+
+ function getProfileIdByName(name) {
+ const profiles = DisplayConfigState.validatedProfiles;
+ for (const id in profiles) {
+ if (profiles[id].name === name)
+ return id;
+ }
+ return "";
+ }
+
+ function getProfileNameById(id) {
+ const profiles = DisplayConfigState.validatedProfiles;
+ return profiles[id]?.name || "";
+ }
+
+ Connections {
+ target: DisplayConfigState
+ function onChangesApplied(changeDescriptions) {
+ confirmationModal.changes = changeDescriptions;
+ confirmationModal.open();
+ }
+ function onChangesConfirmed() {
+ }
+ function onChangesReverted() {
+ }
+ function onProfileActivated(profileId, profileName) {
+ root.selectedProfileId = profileId;
+ ToastService.showInfo(I18n.tr("Profile activated: %1").arg(profileName));
+ }
+ function onProfileSaved(profileId, profileName) {
+ root.selectedProfileId = profileId;
+ ToastService.showInfo(I18n.tr("Profile saved: %1").arg(profileName));
+ }
+ function onProfileDeleted(profileId) {
+ root.selectedProfileId = SettingsData.getActiveDisplayProfile(CompositorService.compositor);
+ ToastService.showInfo(I18n.tr("Profile deleted"));
+ }
+ function onProfileError(message) {
+ ToastService.showError(I18n.tr("Profile error"), message);
+ }
+ }
+
+ DankFlickable {
+ anchors.fill: parent
+ clip: true
+ contentHeight: mainColumn.height + Theme.spacingXL
+ contentWidth: width
+
+ Column {
+ id: mainColumn
+ topPadding: 4
+
+ width: Math.min(550, parent.width - Theme.spacingL * 2)
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: Theme.spacingXL
+
+ IncludeWarningBox {
+ width: parent.width
+ }
+
+ StyledRect {
+ width: parent.width
+ height: profileSection.implicitHeight + Theme.spacingL * 2
+ radius: Theme.cornerRadius
+ color: Theme.surfaceContainerHigh
+ border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
+ border.width: 0
+ visible: DisplayConfigState.hasOutputBackend
+
+ Column {
+ id: profileSection
+ anchors.fill: parent
+ anchors.margins: Theme.spacingL
+ spacing: Theme.spacingM
+
+ Row {
+ width: parent.width
+ spacing: Theme.spacingM
+
+ DankIcon {
+ name: "tune"
+ size: Theme.iconSize
+ color: Theme.primary
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Column {
+ width: parent.width - Theme.iconSize - Theme.spacingM - autoSelectColumn.width - Theme.spacingM
+ spacing: Theme.spacingXS
+ anchors.verticalCenter: parent.verticalCenter
+
+ StyledText {
+ text: I18n.tr("Display Profiles")
+ font.pixelSize: Theme.fontSizeLarge
+ font.weight: Font.Medium
+ color: Theme.surfaceText
+ width: parent.width
+ horizontalAlignment: Text.AlignLeft
+ }
+
+ StyledText {
+ text: I18n.tr("Save and switch between display configurations")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ wrapMode: Text.WordWrap
+ width: parent.width
+ horizontalAlignment: Text.AlignLeft
+ }
+ }
+
+ // ! TODO - auto profile switching is buggy on niri and other compositors
+ Column {
+ id: autoSelectColumn
+ visible: false // disabled for now
+ spacing: Theme.spacingXS
+ anchors.verticalCenter: parent.verticalCenter
+
+ StyledText {
+ text: I18n.tr("Auto")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ horizontalAlignment: Text.AlignHCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ DankToggle {
+ id: autoSelectToggle
+ checked: false // disabled for now
+ enabled: false
+ onToggled: checked => {
+ // disabled for now
+ // SettingsData.displayProfileAutoSelect = checked;
+ // SettingsData.saveSettings();
+ }
+ }
+ }
+ }
+
+ Row {
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: !root.showNewProfileDialog && !root.showDeleteConfirmDialog && !root.showRenameDialog
+
+ DankDropdown {
+ id: profileDropdown
+ width: parent.width - newButton.width - deleteButton.width - Theme.spacingS * 2
+ compactMode: true
+ dropdownWidth: width
+ options: root.getProfileOptions()
+ currentValue: root.getProfileNameById(root.selectedProfileId)
+ emptyText: I18n.tr("No profiles")
+ onValueChanged: value => {
+ const profileId = root.getProfileIdByName(value);
+ if (profileId && profileId !== root.selectedProfileId)
+ DisplayConfigState.activateProfile(profileId);
+ }
+ }
+
+ DankButton {
+ id: newButton
+ iconName: "add"
+ text: ""
+ buttonHeight: 40
+ horizontalPadding: Theme.spacingM
+ backgroundColor: Theme.surfaceContainer
+ textColor: Theme.surfaceText
+ onClicked: {
+ root.newProfileName = "";
+ root.showNewProfileDialog = true;
+ }
+ }
+
+ DankButton {
+ id: deleteButton
+ iconName: "delete"
+ text: ""
+ buttonHeight: 40
+ horizontalPadding: Theme.spacingM
+ backgroundColor: Theme.surfaceContainer
+ textColor: Theme.error
+ enabled: root.selectedProfileId !== ""
+ onClicked: root.showDeleteConfirmDialog = true
+ }
+ }
+
+ Rectangle {
+ width: parent.width
+ height: newProfileRow.height + Theme.spacingM * 2
+ radius: Theme.cornerRadius
+ color: Theme.surfaceContainer
+ visible: root.showNewProfileDialog
+
+ Row {
+ id: newProfileRow
+ anchors.centerIn: parent
+ width: parent.width - Theme.spacingM * 2
+ spacing: Theme.spacingS
+
+ DankTextField {
+ id: newProfileField
+ width: parent.width - createButton.width - cancelNewButton.width - Theme.spacingS * 2
+ placeholderText: I18n.tr("Profile name")
+ text: root.newProfileName
+ onTextChanged: root.newProfileName = text
+ onAccepted: {
+ if (text.trim())
+ DisplayConfigState.createProfile(text.trim());
+ root.showNewProfileDialog = false;
+ }
+ Component.onCompleted: forceActiveFocus()
+ }
+
+ DankButton {
+ id: createButton
+ text: I18n.tr("Create")
+ enabled: root.newProfileName.trim() !== ""
+ onClicked: {
+ DisplayConfigState.createProfile(root.newProfileName.trim());
+ root.showNewProfileDialog = false;
+ }
+ }
+
+ DankButton {
+ id: cancelNewButton
+ text: I18n.tr("Cancel")
+ backgroundColor: "transparent"
+ textColor: Theme.surfaceText
+ onClicked: root.showNewProfileDialog = false
+ }
+ }
+ }
+
+ Rectangle {
+ width: parent.width
+ height: deleteConfirmColumn.height + Theme.spacingM * 2
+ radius: Theme.cornerRadius
+ color: Theme.surfaceContainer
+ visible: root.showDeleteConfirmDialog
+
+ Column {
+ id: deleteConfirmColumn
+ anchors.centerIn: parent
+ width: parent.width - Theme.spacingM * 2
+ spacing: Theme.spacingS
+
+ StyledText {
+ text: I18n.tr("Delete profile \"%1\"?").arg(root.getProfileNameById(root.selectedProfileId))
+ font.pixelSize: Theme.fontSizeMedium
+ color: Theme.surfaceText
+ width: parent.width
+ wrapMode: Text.WordWrap
+ horizontalAlignment: Text.AlignLeft
+ }
+
+ Row {
+ spacing: Theme.spacingS
+ anchors.right: parent.right
+
+ DankButton {
+ text: I18n.tr("Delete")
+ backgroundColor: Theme.error
+ textColor: Theme.primaryText
+ onClicked: {
+ DisplayConfigState.deleteProfile(root.selectedProfileId);
+ root.showDeleteConfirmDialog = false;
+ }
+ }
+
+ DankButton {
+ text: I18n.tr("Cancel")
+ backgroundColor: "transparent"
+ textColor: Theme.surfaceText
+ onClicked: root.showDeleteConfirmDialog = false
+ }
+ }
+ }
+ }
+
+ Row {
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: DisplayConfigState.matchedProfile !== ""
+
+ DankIcon {
+ name: "check_circle"
+ size: 16
+ color: Theme.success
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ StyledText {
+ text: I18n.tr("Matches profile: %1").arg(root.getProfileNameById(DisplayConfigState.matchedProfile))
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.success
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ }
+ }
+
+ StyledRect {
+ width: parent.width
+ height: monitorConfigSection.implicitHeight + Theme.spacingL * 2
+ radius: Theme.cornerRadius
+ color: Theme.surfaceContainerHigh
+ border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
+ border.width: 0
+ visible: DisplayConfigState.hasOutputBackend
+
+ Column {
+ id: monitorConfigSection
+ anchors.fill: parent
+ anchors.margins: Theme.spacingL
+ spacing: Theme.spacingM
+
+ Row {
+ width: parent.width
+ spacing: Theme.spacingM
+
+ DankIcon {
+ name: "monitor"
+ size: Theme.iconSize
+ color: Theme.primary
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Column {
+ width: parent.width - Theme.iconSize - Theme.spacingM - (displayFormatColumn.visible ? displayFormatColumn.width + Theme.spacingM : 0) - (snapColumn.visible ? snapColumn.width + Theme.spacingM : 0)
+ spacing: Theme.spacingXS
+ anchors.verticalCenter: parent.verticalCenter
+
+ StyledText {
+ text: I18n.tr("Monitor Configuration")
+ font.pixelSize: Theme.fontSizeLarge
+ font.weight: Font.Medium
+ color: Theme.surfaceText
+ width: parent.width
+ horizontalAlignment: Text.AlignLeft
+ }
+
+ StyledText {
+ text: I18n.tr("Arrange displays and configure resolution, refresh rate, and VRR")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ wrapMode: Text.WordWrap
+ width: parent.width
+ horizontalAlignment: Text.AlignLeft
+ }
+ }
+
+ Column {
+ id: snapColumn
+ visible: true
+ spacing: Theme.spacingXS
+ anchors.verticalCenter: parent.verticalCenter
+
+ StyledText {
+ text: I18n.tr("Snap")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ horizontalAlignment: Text.AlignHCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ DankToggle {
+ id: snapToggle
+ checked: SettingsData.displaySnapToEdge
+ onToggled: checked => {
+ SettingsData.displaySnapToEdge = checked;
+ SettingsData.saveSettings();
+ }
+ }
+ }
+
+ Column {
+ id: displayFormatColumn
+ visible: !CompositorService.isDwl
+ spacing: Theme.spacingXS
+ anchors.verticalCenter: parent.verticalCenter
+
+ StyledText {
+ text: I18n.tr("Config Format")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ horizontalAlignment: Text.AlignHCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ DankButtonGroup {
+ id: displayFormatGroup
+ model: [I18n.tr("Name"), I18n.tr("Model")]
+ currentIndex: SettingsData.displayNameMode === "model" ? 1 : 0
+ onSelectionChanged: (index, selected) => {
+ if (!selected)
+ return;
+ const newMode = index === 1 ? "model" : "system";
+ DisplayConfigState.setOriginalDisplayNameMode(SettingsData.displayNameMode);
+ SettingsData.displayNameMode = newMode;
+ }
+
+ Connections {
+ target: SettingsData
+ function onDisplayNameModeChanged() {
+ displayFormatGroup.currentIndex = SettingsData.displayNameMode === "model" ? 1 : 0;
+ }
+ }
+ }
+ }
+ }
+
+ MonitorCanvas {
+ width: parent.width
+ }
+
+ Column {
+ width: parent.width
+ spacing: Theme.spacingS
+
+ Row {
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: {
+ const all = DisplayConfigState.allOutputs || {};
+ const disconnected = Object.keys(all).filter(k => !all[k]?.connected);
+ return disconnected.length > 0;
+ }
+
+ StyledText {
+ text: {
+ const all = DisplayConfigState.allOutputs || {};
+ const disconnected = Object.keys(all).filter(k => !all[k]?.connected);
+ if (SettingsData.displayShowDisconnected)
+ return I18n.tr("%1 disconnected").arg(disconnected.length);
+ return I18n.tr("%1 disconnected (hidden)").arg(disconnected.length);
+ }
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ StyledText {
+ text: SettingsData.displayShowDisconnected ? I18n.tr("Hide") : I18n.tr("Show")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.primary
+ anchors.verticalCenter: parent.verticalCenter
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ SettingsData.displayShowDisconnected = !SettingsData.displayShowDisconnected;
+ SettingsData.saveSettings();
+ }
+ }
+ }
+ }
+
+ Repeater {
+ model: {
+ const keys = Object.keys(DisplayConfigState.allOutputs || {});
+ if (SettingsData.displayShowDisconnected)
+ return keys;
+ return keys.filter(k => DisplayConfigState.allOutputs[k]?.connected);
+ }
+
+ delegate: OutputCard {
+ required property string modelData
+ outputName: modelData
+ outputData: DisplayConfigState.allOutputs[modelData]
+ }
+ }
+ }
+
+ Row {
+ LayoutMirroring.enabled: false
+ width: parent.width
+ spacing: Theme.spacingS
+ visible: DisplayConfigState.hasPendingChanges
+ layoutDirection: Qt.RightToLeft
+
+ DankButton {
+ text: I18n.tr("Apply Changes")
+ iconName: "check"
+ onClicked: DisplayConfigState.applyChanges()
+ }
+
+ DankButton {
+ text: I18n.tr("Discard")
+ backgroundColor: "transparent"
+ textColor: Theme.surfaceText
+ onClicked: DisplayConfigState.discardChanges()
+ }
+ }
+ }
+ }
+
+ NoBackendMessage {
+ width: parent.width
+ visible: !DisplayConfigState.hasOutputBackend
+ }
+ }
+ }
+
+ DisplayConfirmationModal {
+ id: confirmationModal
+ onConfirmed: DisplayConfigState.confirmChanges()
+ onReverted: DisplayConfigState.revertChanges()
+ }
+}