diff --git a/components/containers/StyledWindow.qml b/components/containers/StyledWindow.qml index c93465c7..8ea3c227 100644 --- a/components/containers/StyledWindow.qml +++ b/components/containers/StyledWindow.qml @@ -8,16 +8,10 @@ PanelWindow { id: root required property string name - readonly property alias configScope: scope - default property alias contentData: scope.data WlrLayershell.namespace: `caelestia-${name}` color: "transparent" - ConfigScope { - id: scope - - anchors.fill: parent - screen: root.screen.name - } + contentItem.Config.screen: screen.name + contentItem.Tokens.screen: screen.name } diff --git a/modules/drawers/ContentWindow.qml b/modules/drawers/ContentWindow.qml index 4ff12c16..a321543b 100644 --- a/modules/drawers/ContentWindow.qml +++ b/modules/drawers/ContentWindow.qml @@ -30,9 +30,9 @@ StyledWindow { } return monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false; } - property real borderThickness: hasFullscreen ? 0 : configScope.Config.border.thickness - readonly property real borderLayoutThickness: hasFullscreen ? 0 : configScope.Config.border.thickness - property real borderRounding: hasFullscreen ? 0 : configScope.Config.border.rounding + property real borderThickness: hasFullscreen ? 0 : contentItem.Config.border.thickness + readonly property real borderLayoutThickness: hasFullscreen ? 0 : contentItem.Config.border.thickness + property real borderRounding: hasFullscreen ? 0 : contentItem.Config.border.rounding property real shadowOpacity: hasFullscreen ? 0 : 0.7 readonly property int dragMaskPadding: { @@ -44,8 +44,8 @@ StyledWindow { const thresholds = []; for (const panel of ["dashboard", "launcher", "session", "sidebar"]) - if (configScope.Config[panel].enabled) - thresholds.push(configScope.Config[panel].dragThreshold); + if (contentItem.Config[panel].enabled) + thresholds.push(contentItem.Config[panel].dragThreshold); return Math.max(...thresholds); } diff --git a/modules/drawers/Regions.qml b/modules/drawers/Regions.qml index 26b7b4c6..47ad0680 100644 --- a/modules/drawers/Regions.qml +++ b/modules/drawers/Regions.qml @@ -12,8 +12,8 @@ Region { required property Panels panels required property var win - readonly property real borderThickness: win.configScope.Config.border.thickness - readonly property real clampedThickness: win.configScope.Config.border.clampedThickness + readonly property real borderThickness: win.contentItem.Config.border.thickness + readonly property real clampedThickness: win.contentItem.Config.border.clampedThickness x: bar.clampedWidth + win.dragMaskPadding y: clampedThickness + win.dragMaskPadding diff --git a/modules/lock/LockSurface.qml b/modules/lock/LockSurface.qml index 70c754d6..23bfd65f 100644 --- a/modules/lock/LockSurface.qml +++ b/modules/lock/LockSurface.qml @@ -15,6 +15,9 @@ WlSessionLockSurface { readonly property alias unlocking: unlockAnim.running + contentItem.Config.screen: screen.name + contentItem.Tokens.screen: screen.name + color: "transparent" Connections { @@ -159,79 +162,72 @@ WlSessionLockSurface { } } - ConfigScope { - id: configScope + ScreencopyView { + id: background anchors.fill: parent - screen: root.screen + captureSource: root.screen + opacity: 0 - ScreencopyView { - id: background + layer.enabled: true + layer.effect: MultiEffect { + autoPaddingEnabled: false + blurEnabled: true + blur: 1 + blurMax: 64 + blurMultiplier: 1 + } + } + + Item { + id: lockContent + + readonly property int size: lockIcon.implicitHeight + Tokens.padding.large * 4 + readonly property int radius: size / 4 * Tokens.rounding.scale + + anchors.centerIn: parent + implicitWidth: size + implicitHeight: size + + rotation: 180 + scale: 0 + + StyledRect { + id: lockBg anchors.fill: parent - captureSource: root.screen - opacity: 0 + color: Colours.palette.m3surface + radius: parent.radius + opacity: Colours.transparency.enabled ? Colours.transparency.base : 1 layer.enabled: true layer.effect: MultiEffect { - autoPaddingEnabled: false - blurEnabled: true - blur: 1 - blurMax: 64 - blurMultiplier: 1 + shadowEnabled: true + blurMax: 15 + shadowColor: Qt.alpha(Colours.palette.m3shadow, 0.7) } } - Item { - id: lockContent - - readonly property int size: lockIcon.implicitHeight + Tokens.padding.large * 4 - readonly property int radius: size / 4 * Tokens.rounding.scale + MaterialIcon { + id: lockIcon anchors.centerIn: parent - implicitWidth: size - implicitHeight: size - + text: "lock" + font.pointSize: Tokens.font.size.extraLarge * 4 + font.bold: true rotation: 180 + } + + Content { + id: content + + anchors.centerIn: parent + width: (root.screen?.height ?? 0) * Tokens.sizes.lock.heightMult * Tokens.sizes.lock.ratio - Tokens.padding.large * 2 + height: (root.screen?.height ?? 0) * Tokens.sizes.lock.heightMult - Tokens.padding.large * 2 + + lock: root + opacity: 0 scale: 0 - - StyledRect { - id: lockBg - - anchors.fill: parent - color: Colours.palette.m3surface - radius: parent.radius - opacity: Colours.transparency.enabled ? Colours.transparency.base : 1 - - layer.enabled: true - layer.effect: MultiEffect { - shadowEnabled: true - blurMax: 15 - shadowColor: Qt.alpha(Colours.palette.m3shadow, 0.7) - } - } - - MaterialIcon { - id: lockIcon - - anchors.centerIn: parent - text: "lock" - font.pointSize: Tokens.font.size.extraLarge * 4 - font.bold: true - rotation: 180 - } - - Content { - id: content - - anchors.centerIn: parent - width: (root.screen?.height ?? 0) * Tokens.sizes.lock.heightMult * Tokens.sizes.lock.ratio - Tokens.padding.large * 2 - height: (root.screen?.height ?? 0) * Tokens.sizes.lock.heightMult - Tokens.padding.large * 2 - - lock: root - opacity: 0 - scale: 0 - } } } } diff --git a/plugin/src/Caelestia/CMakeLists.txt b/plugin/src/Caelestia/CMakeLists.txt index 01f43b21..fbee0482 100644 --- a/plugin/src/Caelestia/CMakeLists.txt +++ b/plugin/src/Caelestia/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Qt6 REQUIRED COMPONENTS ShaderTools Core Qml Gui Quick Concurrent Sql Network DBus) +find_package(Qt6 REQUIRED COMPONENTS ShaderTools Core Qml Gui Quick QuickControls2 Concurrent Sql Network DBus) find_package(PkgConfig REQUIRED) pkg_check_modules(Qalculate IMPORTED_TARGET libqalculate REQUIRED) pkg_check_modules(Pipewire IMPORTED_TARGET libpipewire-0.3 REQUIRED) diff --git a/plugin/src/Caelestia/Config/CMakeLists.txt b/plugin/src/Caelestia/Config/CMakeLists.txt index 9322d76c..4b26ea02 100644 --- a/plugin/src/Caelestia/Config/CMakeLists.txt +++ b/plugin/src/Caelestia/Config/CMakeLists.txt @@ -5,7 +5,6 @@ qml_module(caelestia-config configattached.cpp configobject.cpp rootconfig.cpp - configscope.cpp appearanceconfig.cpp tokens.cpp tokensattached.cpp @@ -29,4 +28,5 @@ qml_module(caelestia-config winfoconfig.hpp LIBRARIES Qt::Quick + Qt::QuickControls2 ) diff --git a/plugin/src/Caelestia/Config/anim.hpp b/plugin/src/Caelestia/Config/anim.hpp index 1d5909a7..b90c3bda 100644 --- a/plugin/src/Caelestia/Config/anim.hpp +++ b/plugin/src/Caelestia/Config/anim.hpp @@ -8,11 +8,11 @@ namespace caelestia::config { class AnimCurves; class AnimDurations; -class ConfigScope; class AnimTokens : public QObject { Q_OBJECT - Q_MOC_INCLUDE("appearanceconfig.hpp") + Q_MOC_INCLUDE("tokens.hpp") // AnimCurves + Q_MOC_INCLUDE("appearanceconfig.hpp") // AnimDurations QML_ANONYMOUS Q_PROPERTY(QEasingCurve emphasized READ emphasized NOTIFY curvesChanged) diff --git a/plugin/src/Caelestia/Config/configattached.cpp b/plugin/src/Caelestia/Config/configattached.cpp index cc6ae8f9..5cafe34a 100644 --- a/plugin/src/Caelestia/Config/configattached.cpp +++ b/plugin/src/Caelestia/Config/configattached.cpp @@ -1,33 +1,56 @@ #include "configattached.hpp" #include "config.hpp" -#include "configscope.hpp" #include "monitorconfigmanager.hpp" namespace caelestia::config { -Config::Config(ConfigScope* scope, QObject* parent) - : QObject(parent) - , m_scope(scope) { - connectScope(); +Config::Config(QObject* parent) + : QQuickAttachedPropertyPropagator(parent) { + initialize(); } -ConfigScope* Config::scope() const { - return m_scope; +QString Config::screen() const { + return m_screen; } -void Config::connectScope() { - if (!m_scope) +void Config::inheritScreen(const QString& screen) { + if (screen == m_screen) return; - connect(m_scope, &ConfigScope::configChanged, this, &Config::sourceChanged); - connect(m_scope, &ConfigScope::tokensChanged, this, &Config::sourceChanged); + + m_screen = screen; + + if (m_screen.isEmpty()) + m_config = nullptr; + else + m_config = MonitorConfigManager::instance()->configForScreen(m_screen); + + propagateScreen(); + emit sourceChanged(); +} + +void Config::propagateScreen() { + const auto children = attachedChildren(); + for (auto* const child : children) { + auto* const config = qobject_cast(child); + if (config) + config->inheritScreen(m_screen); + } +} + +void Config::attachedParentChange( + QQuickAttachedPropertyPropagator* newParent, QQuickAttachedPropertyPropagator* oldParent) { + Q_UNUSED(oldParent); + auto* const config = qobject_cast(newParent); + if (config) + inheritScreen(config->screen()); } #define CONFIG_ATTACHED_GETTER(Type, name) \ const Type* Config::name() const { \ - if (m_scope && m_scope->config()) \ - return m_scope->config()->name(); \ + if (m_config) \ + return m_config->name(); \ if (parent()) \ - qCWarning(lcConfig, "Config.%s accessed without a ConfigScope ancestor on %s", #name, \ + qCWarning(lcConfig, "Config.%s accessed without a screen set on %s", #name, \ parent()->metaObject()->className()); \ return GlobalConfig::instance()->name(); \ } @@ -57,7 +80,7 @@ GlobalConfig* Config::forScreen(const QString& screen) { } Config* Config::qmlAttachedProperties(QObject* object) { - return new Config(ConfigScope::find(object), object); + return new Config(object); } } // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/configattached.hpp b/plugin/src/Caelestia/Config/configattached.hpp index d63c330f..2b4c32a1 100644 --- a/plugin/src/Caelestia/Config/configattached.hpp +++ b/plugin/src/Caelestia/Config/configattached.hpp @@ -1,17 +1,18 @@ #pragma once #include "config.hpp" -#include "configscope.hpp" + +#include namespace caelestia::config { -class Config : public QObject { +class Config : public QQuickAttachedPropertyPropagator { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") QML_ATTACHED(Config) - Q_PROPERTY(caelestia::config::ConfigScope* scope READ scope NOTIFY sourceChanged) + Q_PROPERTY(QString screen READ screen WRITE inheritScreen NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearanceConfig* appearance READ appearance NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::GeneralConfig* general READ general NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::BackgroundConfig* background READ background NOTIFY sourceChanged) @@ -31,9 +32,10 @@ class Config : public QObject { Q_PROPERTY(const caelestia::config::UserPaths* paths READ paths NOTIFY sourceChanged) public: - explicit Config(ConfigScope* scope, QObject* parent = nullptr); + explicit Config(QObject* parent = nullptr); - [[nodiscard]] ConfigScope* scope() const; + [[nodiscard]] QString screen() const; + void inheritScreen(const QString& screen); [[nodiscard]] const AppearanceConfig* appearance() const; [[nodiscard]] const GeneralConfig* general() const; @@ -60,10 +62,15 @@ public: signals: void sourceChanged(); -private: - void connectScope(); +protected: + void attachedParentChange( + QQuickAttachedPropertyPropagator* newParent, QQuickAttachedPropertyPropagator* oldParent) override; - ConfigScope* m_scope; +private: + void propagateScreen(); + + QString m_screen; + GlobalConfig* m_config = nullptr; }; } // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/configscope.cpp b/plugin/src/Caelestia/Config/configscope.cpp deleted file mode 100644 index 769f2b73..00000000 --- a/plugin/src/Caelestia/Config/configscope.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "configscope.hpp" -#include "monitorconfigmanager.hpp" - -namespace caelestia::config { - -ConfigScope::ConfigScope(QQuickItem* parent) - : QQuickItem(parent) {} - -QString ConfigScope::screen() const { - return m_screen; -} - -GlobalConfig* ConfigScope::config() const { - return m_config; -} - -TokenConfig* ConfigScope::tokens() const { - return m_tokens; -} - -void ConfigScope::setScreen(const QString& screen) { - if (m_screen == screen) - return; - - m_screen = screen; - emit screenChanged(); - resolveConfig(); -} - -void ConfigScope::resolveConfig() { - GlobalConfig* newConfig = nullptr; - TokenConfig* newTokens = nullptr; - - if (!m_screen.isEmpty()) { - if (auto* mgr = MonitorConfigManager::instance()) { - newConfig = mgr->configForScreen(m_screen); - newTokens = mgr->tokensForScreen(m_screen); - } - } - - if (m_config != newConfig) { - m_config = newConfig; - emit configChanged(); - } - - if (m_tokens != newTokens) { - m_tokens = newTokens; - emit tokensChanged(); - } -} - -ConfigScope* ConfigScope::find(QObject* object) { - auto* item = qobject_cast(object); - - while (item) { - if (auto* scope = qobject_cast(item)) - return scope; - item = item->parentItem(); - } - - return nullptr; -} - -} // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/configscope.hpp b/plugin/src/Caelestia/Config/configscope.hpp deleted file mode 100644 index 82869816..00000000 --- a/plugin/src/Caelestia/Config/configscope.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include - -namespace caelestia::config { - -class GlobalConfig; -class TokenConfig; - -class ConfigScope : public QQuickItem { - Q_OBJECT - Q_MOC_INCLUDE("config.hpp") - Q_MOC_INCLUDE("tokens.hpp") - QML_ELEMENT - - Q_PROPERTY(QString screen READ screen WRITE setScreen NOTIFY screenChanged) - Q_PROPERTY(caelestia::config::GlobalConfig* config READ config NOTIFY configChanged) - Q_PROPERTY(caelestia::config::TokenConfig* tokens READ tokens NOTIFY tokensChanged) - -public: - explicit ConfigScope(QQuickItem* parent = nullptr); - - [[nodiscard]] QString screen() const; - void setScreen(const QString& screen); - - [[nodiscard]] GlobalConfig* config() const; - [[nodiscard]] TokenConfig* tokens() const; - - static ConfigScope* find(QObject* object); - -signals: - void screenChanged(); - void configChanged(); - void tokensChanged(); - -private: - void resolveConfig(); - - QString m_screen; - GlobalConfig* m_config = nullptr; - TokenConfig* m_tokens = nullptr; -}; - -} // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/tokens.hpp b/plugin/src/Caelestia/Config/tokens.hpp index 1a0c7b8f..de0a7c26 100644 --- a/plugin/src/Caelestia/Config/tokens.hpp +++ b/plugin/src/Caelestia/Config/tokens.hpp @@ -9,8 +9,6 @@ namespace caelestia::config { -class ConfigScope; - class AnimCurves : public ConfigObject { Q_OBJECT QML_ANONYMOUS diff --git a/plugin/src/Caelestia/Config/tokensattached.cpp b/plugin/src/Caelestia/Config/tokensattached.cpp index 6c05bb4b..b9cf5745 100644 --- a/plugin/src/Caelestia/Config/tokensattached.cpp +++ b/plugin/src/Caelestia/Config/tokensattached.cpp @@ -1,7 +1,6 @@ #include "tokensattached.hpp" #include "anim.hpp" #include "config.hpp" -#include "configscope.hpp" #include "monitorconfigmanager.hpp" #include "tokens.hpp" @@ -9,29 +8,60 @@ namespace caelestia::config { namespace { -const AppearanceConfig* resolveAppearance(ConfigScope* scope, const char* prop, QObject* parent) { - if (scope && scope->config()) - return scope->config()->appearance(); +const AppearanceConfig* resolveAppearance(GlobalConfig* config, const char* prop, QObject* parent) { + if (config) + return config->appearance(); if (parent) - qCWarning(lcConfig, "Tokens.%s accessed without a ConfigScope ancestor on %s", prop, - parent->metaObject()->className()); + qCWarning(lcConfig, "Tokens.%s accessed without a screen set on %s", prop, parent->metaObject()->className()); return GlobalConfig::instance()->appearance(); } } // namespace -Tokens::Tokens(ConfigScope* scope, QObject* parent) - : QObject(parent) - , m_scope(scope) +Tokens::Tokens(QObject* parent) + : QQuickAttachedPropertyPropagator(parent) , m_anim(new AnimTokens(this)) { - connectScope(); bindAnim(); + initialize(); } -void Tokens::connectScope() { - if (!m_scope) +QString Tokens::screen() const { + return m_screen; +} + +void Tokens::inheritScreen(const QString& screen) { + if (screen == m_screen) return; - connect(m_scope, &ConfigScope::configChanged, this, &Tokens::sourceChanged); + + m_screen = screen; + + if (m_screen.isEmpty()) { + m_config = nullptr; + m_tokens = nullptr; + } else { + m_config = MonitorConfigManager::instance()->configForScreen(m_screen); + m_tokens = MonitorConfigManager::instance()->tokensForScreen(m_screen); + } + + propagateScreen(); + emit sourceChanged(); +} + +void Tokens::propagateScreen() { + const auto children = attachedChildren(); + for (auto* const child : children) { + auto* const tokens = qobject_cast(child); + if (tokens) + tokens->inheritScreen(m_screen); + } +} + +void Tokens::attachedParentChange( + QQuickAttachedPropertyPropagator* newParent, QQuickAttachedPropertyPropagator* oldParent) { + Q_UNUSED(oldParent); + auto* const tokens = qobject_cast(newParent); + if (tokens) + inheritScreen(tokens->screen()); } void Tokens::bindAnim() { @@ -41,7 +71,7 @@ void Tokens::bindAnim() { #define TOKENS_ATTACHED_GETTER(Type, name) \ const Type* Tokens::name() const { \ - auto* a = resolveAppearance(m_scope, #name, parent()); \ + auto* a = resolveAppearance(m_config, #name, parent()); \ return a ? a->name() : nullptr; \ } @@ -57,11 +87,10 @@ const AppearanceTransparency* Tokens::transparency() const { } const SizeTokens* Tokens::sizes() const { - if (m_scope && m_scope->tokens()) - return m_scope->tokens()->sizes(); + if (m_tokens) + return m_tokens->sizes(); if (parent()) - qCWarning(lcConfig, "Tokens.sizes accessed without a ConfigScope ancestor on %s", - parent()->metaObject()->className()); + qCWarning(lcConfig, "Tokens.sizes accessed without a screen set on %s", parent()->metaObject()->className()); return TokenConfig::instance()->sizes(); } @@ -74,7 +103,7 @@ TokenConfig* Tokens::forScreen(const QString& screen) { } Tokens* Tokens::qmlAttachedProperties(QObject* object) { - return new Tokens(ConfigScope::find(object), object); + return new Tokens(object); } } // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/tokensattached.hpp b/plugin/src/Caelestia/Config/tokensattached.hpp index 88cf0102..4fa1a9b5 100644 --- a/plugin/src/Caelestia/Config/tokensattached.hpp +++ b/plugin/src/Caelestia/Config/tokensattached.hpp @@ -2,17 +2,20 @@ #include "anim.hpp" #include "appearanceconfig.hpp" -#include "configscope.hpp" +#include "config.hpp" #include "tokens.hpp" +#include + namespace caelestia::config { -class Tokens : public QObject { +class Tokens : public QQuickAttachedPropertyPropagator { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") QML_ATTACHED(Tokens) + Q_PROPERTY(QString screen READ screen WRITE inheritScreen NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearanceRounding* rounding READ rounding NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearanceSpacing* spacing READ spacing NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearancePadding* padding READ padding NOTIFY sourceChanged) @@ -22,7 +25,10 @@ class Tokens : public QObject { Q_PROPERTY(const caelestia::config::AnimTokens* anim READ anim NOTIFY sourceChanged) public: - explicit Tokens(ConfigScope* scope, QObject* parent = nullptr); + explicit Tokens(QObject* parent = nullptr); + + [[nodiscard]] QString screen() const; + void inheritScreen(const QString& screen); [[nodiscard]] const AppearanceRounding* rounding() const; [[nodiscard]] const AppearanceSpacing* spacing() const; @@ -40,11 +46,17 @@ public: signals: void sourceChanged(); +protected: + void attachedParentChange( + QQuickAttachedPropertyPropagator* newParent, QQuickAttachedPropertyPropagator* oldParent) override; + private: - void connectScope(); + void propagateScreen(); void bindAnim(); - ConfigScope* m_scope; + QString m_screen; + GlobalConfig* m_config = nullptr; + TokenConfig* m_tokens = nullptr; AnimTokens* m_anim = nullptr; };