diff options
| author | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2025-10-07 14:32:22 +0200 |
|---|---|---|
| committer | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2025-10-08 12:10:15 +0200 |
| commit | 0149d1422990b82bb83ee2783121bcba1b9018ff (patch) | |
| tree | ab45e5430c127eed85287e0646c291773db778f4 /src/quickcontrols/macos/impl | |
| parent | f5e34266ea15c6e44e9816f01f4e627d5f038f0c (diff) | |
mac style: update Switch
On macOS Tahoe 26, the native AppKit Switch has changed apparance
to become wider, with a liquid glass effect on the handle.
This patch will therefore update the Controls Switch to do the
same. That is, change the Switch to be equally wider if the app
is running with liquid glass, and change the appearance of the
handle to look a bit closer to the native handle.
Note that the Switch in Controls has always been drawn
'manually' with QML, so this change is not really fixing a
regression, but is more of a style update.
Task-number: QTBUG-138942
Change-Id: I1c7c9beb35845dac29c0fc67bd0813fffa313116
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/quickcontrols/macos/impl')
| -rw-r--r-- | src/quickcontrols/macos/impl/SwitchHandle.qml | 109 | ||||
| -rw-r--r-- | src/quickcontrols/macos/impl/SwitchIndicator.qml | 122 |
2 files changed, 159 insertions, 72 deletions
diff --git a/src/quickcontrols/macos/impl/SwitchHandle.qml b/src/quickcontrols/macos/impl/SwitchHandle.qml index f1f1a64edf..e80c366954 100644 --- a/src/quickcontrols/macos/impl/SwitchHandle.qml +++ b/src/quickcontrols/macos/impl/SwitchHandle.qml @@ -1,38 +1,97 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default import QtQuick import QtQuick.Templates as T +import QtQuick.NativeStyle as NativeStyle +import QtQuick.Shapes import QtQuick.Effects -Rectangle { +Item { id: handle implicitWidth: 22 implicitHeight: 22 - radius: height / 2 - color: "transparent" - border.color: Application.styleHints.accessibility.contrastPreference === Qt.HighContrast ? Application.styleHints.colorScheme === Qt.Light ? "#b3000000" : "#b3ffffff" : "transparent" - - required property bool down - - Rectangle { - x: 1 - y: 1 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: Application.styleHints.colorScheme === Qt.Light - ? Qt.darker(palette.base, handle.down ? 1.05 : 1) - : Qt.lighter("#cdcbc9", handle.down ? 1.05 : 1) - - layer.enabled: Application.styleHints.accessibility.contrastPreference !== Qt.HighContrast - layer.effect: MultiEffect { - shadowEnabled: true - blurMax: 10 - shadowBlur: 0.2 - shadowScale: 0.92 - shadowOpacity: 1 + + required property SwitchIndicator indicator + readonly property T.AbstractButton control: indicator.control + property color handleColor: Application.styleHints.colorScheme === Qt.Light ? palette.base : "#cdcbc9" + + Behavior on x { + SmoothedAnimation { + velocity: NativeStyle.StyleConstants.runningWithLiquidGlass ? 100 : 200 + } + } + + Loader { + active: NativeStyle.StyleConstants.runningWithLiquidGlass + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: parent.width + (parent.control.down ? 14 : 0) + height: parent.height + (parent.control.down ? 14 : 0) + Behavior on width { NumberAnimation { duration: 200 } } + Behavior on height { NumberAnimation { duration: 200 } } + sourceComponent: Rectangle { + radius: width / 2 + readonly property color fillColor1: indicator.color + readonly property color fillColor2: "black" + gradient: RadialGradient { + GradientStop { position: 0.0; color: control.down ? Qt.alpha(fillColor1, 1.0) : handleColor } + GradientStop { position: 0.15; color: control.down ? Qt.alpha(fillColor2, 0.2) : handleColor } + GradientStop { position: 0.4; color: control.down ? Qt.alpha(fillColor2, 0.1) : handleColor } + GradientStop { position: 0.6; color: control.down ? Qt.alpha(fillColor2, 0.1) : handleColor } + GradientStop { position: 0.9; color: control.down ? Qt.alpha(fillColor2, 0.2) : handleColor } + GradientStop { position: 1.0; color: control.down ? Qt.alpha(fillColor1, 1.0) : handleColor } + } + border.color: Application.styleHints.accessibility.contrastPreference === Qt.HighContrast + ? Application.styleHints.colorScheme === Qt.Light ? "#b3000000" : "#b3ffffff" + : Application.styleHints.colorScheme === Qt.Light + ? Qt.alpha(handleColor, 1.0) : Qt.alpha(handleColor, 0.3) + border.width: 1 + + Behavior on color { ColorAnimation { duration: 200 } } + + layer.enabled: Application.styleHints.accessibility.contrastPreference !== Qt.HighContrast + layer.effect: MultiEffect { + shadowEnabled: true + blurMax: 10 + shadowBlur: 1 + shadowScale: 1.3 + shadowOpacity: 0.05 + } + } + } + + Loader { + active: !NativeStyle.StyleConstants.runningWithLiquidGlass + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: parent.width - 2 + height: parent.height - 2 + sourceComponent: Rectangle { + radius: width / 2 + color: { + const light = Application.styleHints.colorScheme === Qt.Light + if (!control.enabled) + return light ? palette.base : "#64676a"; + if (light) + return Qt.darker(palette.base, handle.control.down ? 1.05 : 1) + return Qt.lighter("#cdcbc9", handle.control.down ? 1.05 : 1) + } + + border.color: Application.styleHints.accessibility.contrastPreference === Qt.HighContrast + ? Application.styleHints.colorScheme === Qt.Light ? "#b3000000" : "#b3ffffff" + : "transparent" + border.width: 1 + + layer.enabled: Application.styleHints.accessibility.contrastPreference !== Qt.HighContrast + layer.effect: MultiEffect { + shadowEnabled: true + blurMax: 10 + shadowBlur: 0.2 + shadowScale: 0.92 + shadowOpacity: 1 + } } } } diff --git a/src/quickcontrols/macos/impl/SwitchIndicator.qml b/src/quickcontrols/macos/impl/SwitchIndicator.qml index 2256d6faed..1255bcb360 100644 --- a/src/quickcontrols/macos/impl/SwitchIndicator.qml +++ b/src/quickcontrols/macos/impl/SwitchIndicator.qml @@ -3,13 +3,14 @@ // Qt-Security score:significant reason:default import QtQuick +import QtQuick.NativeStyle as NativeStyle import QtQuick.Templates as T Rectangle { - id: indicator - implicitWidth: 38 - implicitHeight: 22 - radius: implicitHeight / 2 + id: root + implicitWidth: NativeStyle.StyleConstants.runningWithLiquidGlass ? 55 : 38 + implicitHeight: NativeStyle.StyleConstants.runningWithLiquidGlass ? 25 : 22 + radius: height / 2 required property T.AbstractButton control readonly property real downTintFactor: 1.05 @@ -17,42 +18,69 @@ Rectangle { // For QQuickMacFocusFrame. readonly property real __focusFrameRadius: radius - color: Application.styleHints.colorScheme === Qt.Light - ? Qt.darker(indicator.control.checked - ? indicator.palette.accent : "#d9d6d2", indicator.control.down ? indicator.downTintFactor : 1) - : Qt.lighter(indicator.control.checked - ? indicator.palette.accent : "#454545", indicator.control.down ? indicator.downTintFactor : 1) - - border.color: Application.styleHints.accessibility.contrastPreference === Qt.HighContrast ? Application.styleHints.colorScheme === Qt.Light ? "#b3000000" : "#b3ffffff" : "transparent" - - states: [ - State { - name: "checked" - when: indicator.control.checked - - // Do a bit of duplication with the bindings here just so that - // we can trigger the property change for the transition. We only - // the ColorAnimation to happen when changing checked state. - PropertyChanges { - target: indicator - color: Application.styleHints.colorScheme === Qt.Light - ? indicator.control.checked ? indicator.palette.accent : "#d9d6d2" - : indicator.control.checked ? indicator.palette.accent : "#454545" + color: { + const light = Application.styleHints.colorScheme === Qt.Light + if (Application.state === Qt.ApplicationActive) { + if (!control.enabled) { + if (checked) { + return Qt.alpha(control.palette.accent, 0.5) + } else { + if (light) + return control.palette.window.darker(1.08) + else + return control.palette.window.darker(1.2) + } + } + if (checked) { + if (light) { + if (pressed) + return control.palette.accent.darker(1.1) + else + return control.palette.accent + } else { + if (pressed) + return control.palette.accent.lighter(1.1) + else + return control.palette.accent + } + } else { // not checked + if (light) { + if (pressed) + return control.palette.window.darker(1.4) + else + return control.palette.window.darker(1.2) + } else { + if (pressed) + return control.palette.window.lighter(1.4) + else + return control.palette.window.lighter(1.2) + } + } + } else { // Qt.ApplicationInactive + if (!control.enabled) { + if (light) + return control.palette.window.darker(1.08) + else + return control.palette.window.darker(1.2) + } + if (checked) { + if (light) + return control.palette.window.darker(1.5) + else + return control.palette.window.lighter(1.8) + } else { // not checked + if (light) + return control.palette.window.darker(1.2) + else + return control.palette.window.lighter(1.2) } - } - ] - - transitions: Transition { - ColorAnimation { - targets: indicator - property: "color" - // We try to match the speed of x's SmoothedAnimation below, - // and 17 pixels (handle travel distance) / 75 pixels a second = 0.226. - duration: 226 - easing.type: Easing.InOutQuad } } + Behavior on color { ColorAnimation { duration: 226 } } + + border.color: Application.styleHints.accessibility.contrastPreference === Qt.HighContrast ? Application.styleHints.colorScheme === Qt.Light ? "#b3000000" : "#b3ffffff" : "transparent" + // Since an equivalent to InnerShadow doesn't exist in Qt 6 (QTBUG-116161), // we approximate it using semi-transparent rectangle borders. Rectangle { @@ -61,8 +89,8 @@ Rectangle { radius: height / 2 color: "transparent" border.color: Application.styleHints.colorScheme === Qt.Light - ? Qt.darker("#06000000", indicator.control.down ? indicator.downTintFactor : 1) - : Qt.lighter("#1affffff", indicator.control.down ? indicator.downTintFactor : 1) + ? Qt.darker("#06000000", root.control.down ? root.downTintFactor : 1) + : Qt.lighter("#1affffff", root.control.down ? root.downTintFactor : 1) Rectangle { x: 1 @@ -72,24 +100,24 @@ Rectangle { radius: parent.radius color: "transparent" border.color: Application.styleHints.colorScheme === Qt.Light - ? Qt.darker("#02000000", indicator.control.down ? indicator.downTintFactor : 1) - : Qt.lighter("#04ffffff", indicator.control.down ? indicator.downTintFactor : 1) + ? Qt.darker("#02000000", root.control.down ? root.downTintFactor : 1) + : Qt.lighter("#04ffffff", root.control.down ? root.downTintFactor : 1) } } SwitchHandle { id: handle - x: Math.max(0, Math.min(parent.width - width, indicator.control.visualPosition * parent.width - (width / 2))) + readonly property real handlePadding: NativeStyle.StyleConstants.runningWithLiquidGlass ? 2 : 0 + x: Math.max(handlePadding, Math.min(parent.width - width, root.control.visualPosition * parent.width - (width / 2)) - handlePadding) y: (parent.height - height) / 2 - down: indicator.control.down + width: NativeStyle.StyleConstants.runningWithLiquidGlass ? 32 : implicitWidth + height: NativeStyle.StyleConstants.runningWithLiquidGlass ? 20 : root.height + indicator: root - // We have this here because we don't want this behavior for RangeSlider, - // which also uses SwitchHandle. Behavior on x { - enabled: !handle.down - + // NumberAnimation { SmoothedAnimation { - velocity: 200 + velocity: 100 } } } |
