mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-04 11:32:12 -04:00
feat(DMS FrameMode): A New Connected Unified Surface & Animation Overhaul
- Introduces Standalone & Connected Modes - Updated Animations & Motion effects for both modes - Numerous QOL tweaks and updates throughout the system - Highly inspired to the OG Caelestia Shell / @Soramanew
This commit is contained in:
@@ -27,6 +27,7 @@ Item {
|
||||
const pos = selectedBarConfig?.position ?? SettingsData.Position.Top;
|
||||
return pos === SettingsData.Position.Left || pos === SettingsData.Position.Right;
|
||||
}
|
||||
readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive
|
||||
|
||||
Timer {
|
||||
id: horizontalBarChangeDebounce
|
||||
@@ -695,6 +696,8 @@ Item {
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: CompositorService.isNiri
|
||||
enabled: !SettingsData.frameEnabled
|
||||
opacity: SettingsData.frameEnabled ? 0.5 : 1.0
|
||||
text: I18n.tr("Show on Overview")
|
||||
checked: selectedBarConfig?.openOnOverview ?? false
|
||||
onToggled: toggled => {
|
||||
@@ -705,11 +708,18 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: SettingsData.frameEnabled
|
||||
parentModal: dankBarTab.parentModal
|
||||
settingLabel: I18n.tr("Bar spacing and size")
|
||||
reason: I18n.tr("Managed by Frame")
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
iconName: "space_bar"
|
||||
title: I18n.tr("Spacing")
|
||||
settingKey: "barSpacing"
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled
|
||||
|
||||
SettingsSliderRow {
|
||||
id: edgeSpacingSlider
|
||||
@@ -860,6 +870,7 @@ Item {
|
||||
|
||||
SettingsSliderRow {
|
||||
id: barTransparencySlider
|
||||
visible: !SettingsData.frameEnabled
|
||||
text: I18n.tr("Bar Transparency")
|
||||
value: (selectedBarConfig?.transparency ?? 1.0) * 100
|
||||
minimum: 0
|
||||
@@ -901,6 +912,13 @@ Item {
|
||||
restoreMode: Binding.RestoreBinding
|
||||
}
|
||||
}
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: SettingsData.frameEnabled
|
||||
parentModal: dankBarTab.parentModal
|
||||
settingLabel: I18n.tr("Bar transparency")
|
||||
reason: I18n.tr("Managed by Frame")
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSliderCard {
|
||||
@@ -961,8 +979,16 @@ Item {
|
||||
expanded: false
|
||||
visible: selectedBarConfig?.enabled
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: SettingsData.frameEnabled
|
||||
parentModal: dankBarTab.parentModal
|
||||
settingLabel: I18n.tr("Bar corners and background")
|
||||
reason: I18n.tr("Managed by Frame")
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Square Corners")
|
||||
visible: !SettingsData.frameEnabled
|
||||
checked: selectedBarConfig?.squareCorners ?? false
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
squareCorners: checked
|
||||
@@ -971,6 +997,7 @@ Item {
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("No Background")
|
||||
visible: !SettingsData.frameEnabled
|
||||
checked: selectedBarConfig?.noBackground ?? false
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
noBackground: checked
|
||||
@@ -1010,6 +1037,7 @@ Item {
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Goth Corners")
|
||||
visible: !SettingsData.frameEnabled
|
||||
checked: selectedBarConfig?.gothCornersEnabled ?? false
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
gothCornersEnabled: checked
|
||||
@@ -1345,6 +1373,13 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: dankBarTab.connectedFrameModeActive
|
||||
parentModal: dankBarTab.parentModal
|
||||
settingLabel: I18n.tr("Bar shadow, border, and corners")
|
||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
id: shadowCard
|
||||
iconName: "layers"
|
||||
@@ -1352,7 +1387,7 @@ Item {
|
||||
settingKey: "barShadow"
|
||||
collapsible: true
|
||||
expanded: false
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: (selectedBarConfig?.enabled ?? false) && !dankBarTab.connectedFrameModeActive
|
||||
|
||||
readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0
|
||||
readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom"
|
||||
|
||||
@@ -8,6 +8,9 @@ import qs.Modules.Settings.Widgets
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var parentModal: null
|
||||
readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive
|
||||
|
||||
FileBrowserModal {
|
||||
id: dockLogoFileBrowser
|
||||
browserTitle: I18n.tr("Select Dock Launcher Logo")
|
||||
@@ -604,6 +607,7 @@ Item {
|
||||
|
||||
SettingsSliderRow {
|
||||
text: I18n.tr("Exclusive Zone Offset")
|
||||
visible: !root.connectedFrameModeActive
|
||||
value: SettingsData.dockBottomGap
|
||||
minimum: -100
|
||||
maximum: 100
|
||||
@@ -613,6 +617,7 @@ Item {
|
||||
|
||||
SettingsSliderRow {
|
||||
text: I18n.tr("Margin")
|
||||
visible: !root.connectedFrameModeActive
|
||||
value: SettingsData.dockMargin
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
@@ -621,11 +626,19 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: root.connectedFrameModeActive
|
||||
parentModal: root.parentModal
|
||||
settingLabel: I18n.tr("Dock spacing, transparency, and border")
|
||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "opacity"
|
||||
title: I18n.tr("Transparency")
|
||||
settingKey: "dockTransparency"
|
||||
visible: !root.connectedFrameModeActive
|
||||
|
||||
SettingsSliderRow {
|
||||
text: I18n.tr("Dock Transparency")
|
||||
@@ -645,6 +658,7 @@ Item {
|
||||
settingKey: "dockBorder"
|
||||
collapsible: true
|
||||
expanded: false
|
||||
visible: !root.connectedFrameModeActive
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Border")
|
||||
|
||||
370
quickshell/Modules/Settings/FrameTab.qml
Normal file
370
quickshell/Modules/Settings/FrameTab.qml
Normal file
@@ -0,0 +1,370 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
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
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "frame_source"
|
||||
title: I18n.tr("Frame")
|
||||
settingKey: "frameEnabled"
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "frameEnable"
|
||||
tags: ["frame", "border", "outline", "display"]
|
||||
text: I18n.tr("Enable Frame")
|
||||
description: I18n.tr("Draw a connected picture-frame border around the entire display")
|
||||
checked: SettingsData.frameEnabled
|
||||
onToggled: checked => SettingsData.set("frameEnabled", checked)
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "tune"
|
||||
title: I18n.tr("Mode")
|
||||
settingKey: "frameMode"
|
||||
visible: SettingsData.frameEnabled
|
||||
|
||||
SettingsButtonGroupRow {
|
||||
settingKey: "frameModeSelector"
|
||||
tags: ["frame", "mode", "connected", "separate", "popout"]
|
||||
text: I18n.tr("Surface Behavior")
|
||||
description: SettingsData.frameMode === "connected" ? I18n.tr("Surfaces emerge flush from the bar") : I18n.tr("Surfaces float independently of the frame")
|
||||
model: [I18n.tr("Separate"), I18n.tr("Connected")]
|
||||
currentIndex: SettingsData.frameMode === "connected" ? 1 : 0
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
switch (index) {
|
||||
case 1:
|
||||
SettingsData.set("frameMode", "connected");
|
||||
break;
|
||||
default:
|
||||
SettingsData.set("frameMode", "separate");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "border_outer"
|
||||
title: I18n.tr("Border")
|
||||
settingKey: "frameBorder"
|
||||
collapsible: true
|
||||
visible: SettingsData.frameEnabled
|
||||
|
||||
SettingsSliderRow {
|
||||
id: roundingSlider
|
||||
settingKey: "frameRounding"
|
||||
tags: ["frame", "border", "rounding", "radius", "corner"]
|
||||
text: I18n.tr("Border Radius")
|
||||
unit: "px"
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
step: 1
|
||||
defaultValue: 23
|
||||
value: SettingsData.frameRounding
|
||||
onSliderDragFinished: v => SettingsData.set("frameRounding", v)
|
||||
|
||||
Binding {
|
||||
target: roundingSlider
|
||||
property: "value"
|
||||
value: SettingsData.frameRounding
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
id: thicknessSlider
|
||||
settingKey: "frameThickness"
|
||||
tags: ["frame", "border", "thickness", "size", "width"]
|
||||
text: I18n.tr("Border Width")
|
||||
unit: "px"
|
||||
minimum: 2
|
||||
maximum: 100
|
||||
step: 1
|
||||
defaultValue: 16
|
||||
value: SettingsData.frameThickness
|
||||
onSliderDragFinished: v => SettingsData.set("frameThickness", v)
|
||||
|
||||
Binding {
|
||||
target: thicknessSlider
|
||||
property: "value"
|
||||
value: SettingsData.frameThickness
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
id: barThicknessSlider
|
||||
settingKey: "frameBarSize"
|
||||
tags: ["frame", "bar", "thickness", "size", "height", "width"]
|
||||
text: I18n.tr("Size")
|
||||
description: I18n.tr("Horizontal and vertical bar thickness")
|
||||
unit: "px"
|
||||
minimum: 24
|
||||
maximum: 100
|
||||
step: 1
|
||||
defaultValue: 40
|
||||
value: SettingsData.frameBarSize
|
||||
onSliderDragFinished: v => SettingsData.set("frameBarSize", v)
|
||||
|
||||
Binding {
|
||||
target: barThicknessSlider
|
||||
property: "value"
|
||||
value: SettingsData.frameBarSize
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
id: opacitySlider
|
||||
settingKey: "frameOpacity"
|
||||
tags: ["frame", "border", "surface", "popup", "opacity", "transparency"]
|
||||
text: I18n.tr("Surface Opacity")
|
||||
unit: "%"
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
defaultValue: 100
|
||||
value: SettingsData.frameOpacity * 100
|
||||
onSliderDragFinished: v => SettingsData.set("frameOpacity", v / 100)
|
||||
|
||||
Binding {
|
||||
target: opacitySlider
|
||||
property: "value"
|
||||
value: SettingsData.frameOpacity * 100
|
||||
}
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
id: frameBlurToggle
|
||||
settingKey: "frameBlurEnabled"
|
||||
tags: ["frame", "blur", "background", "glass", "transparency", "frosted"]
|
||||
text: I18n.tr("Frame Blur")
|
||||
description: !BlurService.available ? I18n.tr("Requires a newer version of Quickshell") : I18n.tr("Apply compositor blur behind the frame border")
|
||||
checked: SettingsData.frameBlurEnabled
|
||||
onToggled: checked => SettingsData.set("frameBlurEnabled", checked)
|
||||
enabled: BlurService.available && SettingsData.blurEnabled
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
visible: BlurService.available
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: BlurService.available && !SettingsData.blurEnabled
|
||||
width: parent.width
|
||||
height: blurToggleNote.height + Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
id: blurToggleNote
|
||||
x: Theme.spacingM
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "blur_on"
|
||||
size: Theme.fontSizeMedium
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Frame Blur follows Background Blur in Theme & Colors")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width - Theme.fontSizeMedium - Theme.spacingS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsButtonGroupRow {
|
||||
settingKey: "frameColor"
|
||||
tags: ["frame", "border", "color", "theme", "primary", "surface", "default"]
|
||||
text: I18n.tr("Border Color")
|
||||
model: [I18n.tr("Default"), I18n.tr("Primary"), I18n.tr("Surface"), I18n.tr("Custom")]
|
||||
currentIndex: {
|
||||
const fc = SettingsData.frameColor;
|
||||
if (!fc || fc === "default")
|
||||
return 0;
|
||||
switch (fc) {
|
||||
case "primary":
|
||||
return 1;
|
||||
case "surface":
|
||||
return 2;
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
switch (index) {
|
||||
case 0:
|
||||
SettingsData.set("frameColor", "");
|
||||
break;
|
||||
case 1:
|
||||
SettingsData.set("frameColor", "primary");
|
||||
break;
|
||||
case 2:
|
||||
SettingsData.set("frameColor", "surface");
|
||||
break;
|
||||
case 3:
|
||||
const cur = SettingsData.frameColor;
|
||||
const isPreset = !cur || cur === "primary" || cur === "surface";
|
||||
if (isPreset)
|
||||
SettingsData.set("frameColor", "#2a2a2a");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: {
|
||||
const fc = SettingsData.frameColor;
|
||||
return !!(fc && fc !== "primary" && fc !== "surface");
|
||||
}
|
||||
width: parent.width
|
||||
height: customColorRow.height + Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
id: customColorRow
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: I18n.tr("Custom Color")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 32
|
||||
height: 32
|
||||
radius: 16
|
||||
color: SettingsData.effectiveFrameColor
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
PopoutService.colorPickerModal.selectedColor = SettingsData.effectiveFrameColor;
|
||||
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Frame Border Color");
|
||||
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
|
||||
SettingsData.set("frameColor", color.toString());
|
||||
};
|
||||
PopoutService.colorPickerModal.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "blur_linear"
|
||||
title: I18n.tr("Connected Options")
|
||||
settingKey: "frameConnectedOptions"
|
||||
collapsible: true
|
||||
expanded: true
|
||||
visible: SettingsData.frameEnabled && SettingsData.frameMode === "connected"
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "frameCloseGaps"
|
||||
tags: ["frame", "connected", "gap", "edge", "curves", "arcs", "expose", "popout", "notification"]
|
||||
text: I18n.tr("Expose the Arcs")
|
||||
description: I18n.tr("Reveal the arcs where surfaces meet the frame")
|
||||
checked: !SettingsData.frameCloseGaps
|
||||
onToggled: checked => SettingsData.set("frameCloseGaps", !checked)
|
||||
}
|
||||
|
||||
SettingsButtonGroupRow {
|
||||
settingKey: "frameLauncherEmergeSide"
|
||||
tags: ["frame", "connected", "launcher", "modal", "emerge", "direction", "bottom", "top"]
|
||||
text: I18n.tr("Launcher Emerge Side")
|
||||
description: I18n.tr("Edge the launcher slides from")
|
||||
model: [I18n.tr("Bottom"), I18n.tr("Top")]
|
||||
currentIndex: SettingsData.frameLauncherEmergeSide === "top" ? 1 : 0
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
SettingsData.set("frameLauncherEmergeSide", index === 1 ? "top" : "bottom");
|
||||
}
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "frameLauncherArcExtender"
|
||||
tags: ["frame", "connected", "launcher", "arc", "extender", "center"]
|
||||
text: I18n.tr("Arc Extender")
|
||||
description: I18n.tr("Use the extended surface for launcher content")
|
||||
checked: SettingsData.frameLauncherArcExtender
|
||||
onToggled: checked => SettingsData.set("frameLauncherArcExtender", checked)
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "toolbar"
|
||||
title: I18n.tr("Integrations")
|
||||
settingKey: "frameBarIntegration"
|
||||
collapsible: true
|
||||
expanded: true
|
||||
visible: SettingsData.frameEnabled && CompositorService.isNiri
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "frameShowOnOverview"
|
||||
tags: ["frame", "overview", "show", "hide", "niri"]
|
||||
text: I18n.tr("Show on Overview")
|
||||
description: I18n.tr("Show during Niri overview")
|
||||
checked: SettingsData.frameShowOnOverview
|
||||
onToggled: checked => SettingsData.set("frameShowOnOverview", checked)
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "monitor"
|
||||
title: I18n.tr("Display Assignment")
|
||||
settingKey: "frameDisplays"
|
||||
collapsible: true
|
||||
expanded: false
|
||||
visible: SettingsData.frameEnabled
|
||||
|
||||
SettingsDisplayPicker {
|
||||
displayPreferences: SettingsData.frameScreenPreferences
|
||||
onPreferencesChanged: prefs => SettingsData.set("frameScreenPreferences", prefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,11 +344,7 @@ Item {
|
||||
return I18n.tr("%1 exists but is not included in config. Custom keybinds will not work until this is fixed.").arg(bindsFile);
|
||||
if (warningBox.showWarning) {
|
||||
const count = warningBox.status.overriddenBy;
|
||||
return I18n.ntr(
|
||||
"%1 DMS bind may be overridden by config binds that come after the include.",
|
||||
"%1 DMS binds may be overridden by config binds that come after the include.",
|
||||
count
|
||||
).arg(count);
|
||||
return I18n.ntr("%1 DMS bind may be overridden by config binds that come after the include.", "%1 DMS binds may be overridden by config binds that come after the include.", count).arg(count);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -543,13 +539,11 @@ Item {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (KeybindsService.loading)
|
||||
return I18n.tr("Shortcuts");
|
||||
const count = keybindsTab._filteredBinds.length;
|
||||
return count === 1
|
||||
? I18n.tr("Shortcut (%1)").arg(count)
|
||||
: I18n.tr("Shortcuts (%1)").arg(count);
|
||||
}
|
||||
if (KeybindsService.loading)
|
||||
return I18n.tr("Shortcuts");
|
||||
const count = keybindsTab._filteredBinds.length;
|
||||
return count === 1 ? I18n.tr("Shortcut (%1)").arg(count) : I18n.tr("Shortcuts (%1)").arg(count);
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
@@ -569,7 +563,7 @@ Item {
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
RotationAnimation on rotation {
|
||||
RotationAnimator on rotation {
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
|
||||
@@ -36,36 +36,36 @@ Item {
|
||||
|
||||
function normalizePinList(value) {
|
||||
if (Array.isArray(value))
|
||||
return value.filter(v => v)
|
||||
return value.filter(v => v);
|
||||
if (typeof value === "string" && value.length > 0)
|
||||
return [value]
|
||||
return []
|
||||
return [value];
|
||||
return [];
|
||||
}
|
||||
|
||||
function getPinnedWifiNetworks() {
|
||||
const pins = SettingsData.wifiNetworkPins || {}
|
||||
return normalizePinList(pins["preferredWifi"])
|
||||
const pins = SettingsData.wifiNetworkPins || {};
|
||||
return normalizePinList(pins["preferredWifi"]);
|
||||
}
|
||||
|
||||
function toggleWifiPin(ssid) {
|
||||
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}))
|
||||
let pinnedList = normalizePinList(pins["preferredWifi"])
|
||||
const pinIndex = pinnedList.indexOf(ssid)
|
||||
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}));
|
||||
let pinnedList = normalizePinList(pins["preferredWifi"]);
|
||||
const pinIndex = pinnedList.indexOf(ssid);
|
||||
|
||||
if (pinIndex !== -1) {
|
||||
pinnedList.splice(pinIndex, 1)
|
||||
pinnedList.splice(pinIndex, 1);
|
||||
} else {
|
||||
pinnedList.unshift(ssid)
|
||||
pinnedList.unshift(ssid);
|
||||
if (pinnedList.length > maxPinnedWifiNetworks)
|
||||
pinnedList = pinnedList.slice(0, maxPinnedWifiNetworks)
|
||||
pinnedList = pinnedList.slice(0, maxPinnedWifiNetworks);
|
||||
}
|
||||
|
||||
if (pinnedList.length > 0)
|
||||
pins["preferredWifi"] = pinnedList
|
||||
pins["preferredWifi"] = pinnedList;
|
||||
else
|
||||
delete pins["preferredWifi"]
|
||||
delete pins["preferredWifi"];
|
||||
|
||||
SettingsData.set("wifiNetworkPins", pins)
|
||||
SettingsData.set("wifiNetworkPins", pins);
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
@@ -340,9 +340,7 @@ Item {
|
||||
if (devices.length === 0)
|
||||
return I18n.tr("No adapters");
|
||||
if (connected === 0)
|
||||
return devices.length === 1
|
||||
? I18n.tr("%1 adapter, none connected").arg(devices.length)
|
||||
: I18n.tr("%1 adapters, none connected").arg(devices.length);
|
||||
return devices.length === 1 ? I18n.tr("%1 adapter, none connected").arg(devices.length) : I18n.tr("%1 adapters, none connected").arg(devices.length);
|
||||
return I18n.tr("%1 connected").arg(connected);
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
@@ -658,16 +656,14 @@ Item {
|
||||
SequentialAnimation {
|
||||
running: NetworkService.networkWiredInfoLoading
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: wiredLoadIcon
|
||||
property: "opacity"
|
||||
to: 0.3
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: wiredLoadIcon
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
@@ -1046,16 +1042,14 @@ Item {
|
||||
SequentialAnimation {
|
||||
running: NetworkService.isScanning
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: scanningIcon
|
||||
property: "opacity"
|
||||
to: 0.3
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: scanningIcon
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
@@ -1087,14 +1081,14 @@ Item {
|
||||
|
||||
let sorted = [...networks];
|
||||
sorted.sort((a, b) => {
|
||||
const aPinnedIndex = pinnedList.indexOf(a.ssid)
|
||||
const bPinnedIndex = pinnedList.indexOf(b.ssid)
|
||||
const aPinnedIndex = pinnedList.indexOf(a.ssid);
|
||||
const bPinnedIndex = pinnedList.indexOf(b.ssid);
|
||||
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
|
||||
if (aPinnedIndex === -1)
|
||||
return 1
|
||||
return 1;
|
||||
if (bPinnedIndex === -1)
|
||||
return -1
|
||||
return aPinnedIndex - bPinnedIndex
|
||||
return -1;
|
||||
return aPinnedIndex - bPinnedIndex;
|
||||
}
|
||||
if (a.ssid === ssid)
|
||||
return -1;
|
||||
@@ -1297,7 +1291,7 @@ Item {
|
||||
buttonSize: 28
|
||||
iconColor: isPinned ? Theme.primary : Theme.surfaceVariantText
|
||||
onClicked: {
|
||||
networkTab.toggleWifiPin(modelData.ssid)
|
||||
networkTab.toggleWifiPin(modelData.ssid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1375,16 +1369,14 @@ Item {
|
||||
SequentialAnimation {
|
||||
running: NetworkService.networkInfoLoading
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: wifiInfoLoadIcon
|
||||
property: "opacity"
|
||||
to: 0.3
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: wifiInfoLoadIcon
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
@@ -1866,16 +1858,14 @@ Item {
|
||||
SequentialAnimation {
|
||||
running: VPNService.configLoading
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: vpnLoadIcon
|
||||
property: "opacity"
|
||||
to: 0.3
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
OpacityAnimator {
|
||||
target: vpnLoadIcon
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
duration: 400
|
||||
easing.type: Easing.InOutQuad
|
||||
@@ -1984,7 +1974,9 @@ Item {
|
||||
checked: configData ? (configData.autoconnect || false) : false
|
||||
visible: !VPNService.configLoading && configData !== null
|
||||
onToggled: checked => {
|
||||
VPNService.updateConfig(modelData.uuid, {autoconnect: checked});
|
||||
VPNService.updateConfig(modelData.uuid, {
|
||||
autoconnect: checked
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -458,7 +458,7 @@ Item {
|
||||
enabled: !CupsService.loadingDevices
|
||||
onClicked: CupsService.getDevices()
|
||||
|
||||
RotationAnimation on rotation {
|
||||
RotationAnimator on rotation {
|
||||
running: CupsService.loadingDevices
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
@@ -736,7 +736,7 @@ Item {
|
||||
enabled: !CupsService.loadingPPDs
|
||||
onClicked: CupsService.getPPDs()
|
||||
|
||||
RotationAnimation on rotation {
|
||||
RotationAnimator on rotation {
|
||||
running: CupsService.loadingPPDs
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
|
||||
@@ -11,6 +11,8 @@ import qs.Modules.Settings.Widgets
|
||||
Item {
|
||||
id: themeColorsTab
|
||||
|
||||
property var parentModal: null
|
||||
readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive
|
||||
property var cachedIconThemes: SettingsData.availableIconThemes
|
||||
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
||||
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
|
||||
@@ -1613,12 +1615,20 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: themeColorsTab.connectedFrameModeActive
|
||||
parentModal: themeColorsTab.parentModal
|
||||
settingLabel: I18n.tr("Surface Opacity")
|
||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
tab: "theme"
|
||||
tags: ["popup", "transparency", "opacity", "modal"]
|
||||
tags: ["surface", "popup", "transparency", "opacity", "modal"]
|
||||
settingKey: "popupTransparency"
|
||||
text: I18n.tr("Popup Transparency")
|
||||
text: I18n.tr("Surface Opacity")
|
||||
description: I18n.tr("Controls opacity of all popouts, modals, and their content layers")
|
||||
visible: !themeColorsTab.connectedFrameModeActive
|
||||
value: Math.round(SettingsData.popupTransparency * 100)
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
@@ -1837,7 +1847,7 @@ Item {
|
||||
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
||||
settingKey: "blurEnabled"
|
||||
text: I18n.tr("Background Blur")
|
||||
description: BlurService.available ? I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support and configuration.") : I18n.tr("Requires a newer version of Quickshell")
|
||||
description: !BlurService.available ? I18n.tr("Requires a newer version of Quickshell") : I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support and configuration.")
|
||||
checked: SettingsData.blurEnabled ?? false
|
||||
enabled: BlurService.available
|
||||
onToggled: checked => SettingsData.set("blurEnabled", checked)
|
||||
@@ -2240,12 +2250,20 @@ Item {
|
||||
settingKey: "modalBackground"
|
||||
iconName: "layers"
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: themeColorsTab.connectedFrameModeActive
|
||||
parentModal: themeColorsTab.parentModal
|
||||
settingLabel: I18n.tr("Darken Modal Background")
|
||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
tab: "theme"
|
||||
tags: ["modal", "darken", "background", "overlay"]
|
||||
settingKey: "modalDarkenBackground"
|
||||
text: I18n.tr("Darken Modal Background")
|
||||
description: I18n.tr("Show darkened overlay behind modal dialogs")
|
||||
visible: !themeColorsTab.connectedFrameModeActive
|
||||
checked: SettingsData.modalDarkenBackground
|
||||
onToggled: checked => SettingsData.set("modalDarkenBackground", checked)
|
||||
}
|
||||
|
||||
@@ -693,7 +693,7 @@ Item {
|
||||
onTriggered: refreshButton.isRefreshing = false
|
||||
}
|
||||
|
||||
NumberAnimation on rotation {
|
||||
RotationAnimator on rotation {
|
||||
running: refreshButton.isRefreshing
|
||||
from: 0
|
||||
to: 360
|
||||
|
||||
@@ -55,6 +55,144 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingXL
|
||||
|
||||
SettingsCard {
|
||||
tab: "typography"
|
||||
tags: ["animation", "variant", "style", "slide", "fluent", "dynamic", "motion"]
|
||||
title: I18n.tr("Animation Style")
|
||||
settingKey: "animationVariant"
|
||||
iconName: "auto_awesome_motion"
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: animVariantGroup.implicitHeight
|
||||
clip: true
|
||||
|
||||
DankButtonGroup {
|
||||
id: animVariantGroup
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
buttonPadding: parent.width < 480 ? Theme.spacingS : Theme.spacingL
|
||||
minButtonWidth: parent.width < 480 ? 64 : 96
|
||||
textSize: parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||
model: [I18n.tr("Material"), I18n.tr("Fluent"), I18n.tr("Dynamic")]
|
||||
selectionMode: "single"
|
||||
currentIndex: SettingsData.animationVariant
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
SettingsData.set("animationVariant", index);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onAnimationVariantChanged() {
|
||||
animVariantGroup.currentIndex = SettingsData.animationVariant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.outline
|
||||
opacity: 0.15
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: variantDescription.implicitHeight + Theme.spacingS * 2
|
||||
|
||||
StyledText {
|
||||
id: variantDescription
|
||||
x: Theme.spacingM
|
||||
y: Theme.spacingS
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
text: {
|
||||
switch (SettingsData.animationVariant) {
|
||||
case 1:
|
||||
return I18n.tr("Fluent: Smooth cubic deceleration in, quick snap out — clean, elegant curves.");
|
||||
case 2:
|
||||
return I18n.tr("Dynamic: Spring bezier with overshoot — entry briefly exceeds its target then settles. Expressive and alive.");
|
||||
default:
|
||||
return I18n.tr("Material: Material Design 3 Expressive bezier curves. The DMS default feel.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
tab: "typography"
|
||||
tags: ["animation", "motion", "effect", "slide", "directional", "depth", "spring", "physics"]
|
||||
title: I18n.tr("Motion Effects")
|
||||
settingKey: "motionEffect"
|
||||
iconName: "motion_photos_on"
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: motionEffectGroup.implicitHeight
|
||||
clip: true
|
||||
|
||||
DankButtonGroup {
|
||||
id: motionEffectGroup
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
buttonPadding: parent.width < 480 ? Theme.spacingS : Theme.spacingL
|
||||
minButtonWidth: parent.width < 480 ? 64 : 96
|
||||
textSize: parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||
model: [I18n.tr("Standard"), I18n.tr("Directional"), I18n.tr("Depth")]
|
||||
selectionMode: "single"
|
||||
currentIndex: SettingsData.motionEffect
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
SettingsData.set("motionEffect", index);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onMotionEffectChanged() {
|
||||
motionEffectGroup.currentIndex = SettingsData.motionEffect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.outline
|
||||
opacity: 0.15
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: motionEffectDescription.implicitHeight + Theme.spacingS * 2
|
||||
|
||||
StyledText {
|
||||
id: motionEffectDescription
|
||||
x: Theme.spacingM
|
||||
y: Theme.spacingS
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
text: {
|
||||
switch (SettingsData.motionEffect) {
|
||||
case 1:
|
||||
return I18n.tr("Directional: Panels glide in from a larger distance at full size — no scale change, pure clean motion.");
|
||||
case 2:
|
||||
return I18n.tr("Depth: Panels scale up from small as they slide in — a dramatic pop-forward depth effect.");
|
||||
default:
|
||||
return I18n.tr("Standard: Classic Material Design 3 — panels rise from below with a subtle scale. The DMS default.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
tab: "typography"
|
||||
tags: ["font", "family", "text", "typography"]
|
||||
@@ -285,12 +423,6 @@ Item {
|
||||
description: I18n.tr("Popouts and Modals follow global Animation Speed (disable to customize independently)")
|
||||
checked: SettingsData.syncComponentAnimationSpeeds
|
||||
onToggled: checked => SettingsData.set("syncComponentAnimationSpeeds", checked)
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onSyncComponentAnimationSpeedsChanged() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property string settingLabel: ""
|
||||
property string reason: ""
|
||||
property var parentModal: null
|
||||
|
||||
width: parent?.width ?? 0
|
||||
height: contentRow.implicitHeight + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.primary, 0.08)
|
||||
border.color: Theme.withAlpha(Theme.primary, 0.18)
|
||||
border.width: 1
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "frame_source"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSize - openButton.width - Theme.spacingM * 2
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
text: root.settingLabel
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: root.reason
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
visible: root.reason !== ""
|
||||
}
|
||||
}
|
||||
|
||||
DankButton {
|
||||
id: openButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: I18n.tr("Open Frame")
|
||||
backgroundColor: Theme.primary
|
||||
textColor: Theme.primaryText
|
||||
buttonHeight: 32
|
||||
horizontalPadding: Theme.spacingM
|
||||
onClicked: {
|
||||
if (!root.parentModal)
|
||||
return;
|
||||
root.parentModal.showWithTabName("frame");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,6 @@ Item {
|
||||
description: modelData.width + "×" + modelData.height
|
||||
checked: localChecked
|
||||
onToggled: isChecked => {
|
||||
localChecked = isChecked;
|
||||
var prefs = JSON.parse(JSON.stringify(root.displayPreferences));
|
||||
if (!Array.isArray(prefs) || prefs.includes("all"))
|
||||
prefs = [];
|
||||
@@ -94,6 +93,11 @@ Item {
|
||||
model: modelData.model || ""
|
||||
});
|
||||
}
|
||||
if (prefs.length === 0) {
|
||||
localChecked = true;
|
||||
return;
|
||||
}
|
||||
localChecked = isChecked;
|
||||
root.preferencesChanged(prefs);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user