mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
config/settings: effective override, aka discard override when the value is identical
This commit is contained in:
+469
-18
@@ -3,9 +3,12 @@
|
||||
#include "util/file_utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
@@ -13,6 +16,340 @@ namespace {
|
||||
constexpr Logger kLog("config");
|
||||
constexpr const char* kInternalStateTable = "noctalia_state";
|
||||
constexpr const char* kSetupWizardCompletedKey = "setup_wizard_completed";
|
||||
constexpr double kConfigFloatEpsilon = 1.0e-5;
|
||||
|
||||
std::string overrideCacheKey(const std::vector<std::string>& path) {
|
||||
std::string key;
|
||||
for (const auto& part : path) {
|
||||
if (!key.empty()) {
|
||||
key.push_back('.');
|
||||
}
|
||||
key += part;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
bool nearlyEqual(double a, double b) noexcept { return std::abs(a - b) <= kConfigFloatEpsilon; }
|
||||
|
||||
bool colorEqual(const Color& a, const Color& b) noexcept {
|
||||
return nearlyEqual(a.r, b.r) && nearlyEqual(a.g, b.g) && nearlyEqual(a.b, b.b) && nearlyEqual(a.a, b.a);
|
||||
}
|
||||
|
||||
bool colorSpecEqual(const ColorSpec& a, const ColorSpec& b) noexcept {
|
||||
return a.role == b.role && colorEqual(a.fixed, b.fixed) && nearlyEqual(a.alpha, b.alpha);
|
||||
}
|
||||
|
||||
bool optionalDoubleEqual(const std::optional<double>& a, const std::optional<double>& b) noexcept {
|
||||
if (a.has_value() != b.has_value()) {
|
||||
return false;
|
||||
}
|
||||
return !a.has_value() || nearlyEqual(*a, *b);
|
||||
}
|
||||
|
||||
bool optionalColorSpecEqual(const std::optional<ColorSpec>& a, const std::optional<ColorSpec>& b) noexcept {
|
||||
if (a.has_value() != b.has_value()) {
|
||||
return false;
|
||||
}
|
||||
return !a.has_value() || colorSpecEqual(*a, *b);
|
||||
}
|
||||
|
||||
template <typename T, typename Equal>
|
||||
bool vectorEqual(const std::vector<T>& a, const std::vector<T>& b, Equal equal) {
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
for (std::size_t i = 0; i < a.size(); ++i) {
|
||||
if (!equal(a[i], b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<double> numericWidgetSetting(const WidgetSettingValue& value) {
|
||||
if (const auto* i = std::get_if<std::int64_t>(&value)) {
|
||||
return static_cast<double>(*i);
|
||||
}
|
||||
if (const auto* d = std::get_if<double>(&value)) {
|
||||
return *d;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool widgetSettingEqual(const WidgetSettingValue& a, const WidgetSettingValue& b) {
|
||||
const auto aNum = numericWidgetSetting(a);
|
||||
const auto bNum = numericWidgetSetting(b);
|
||||
if (aNum.has_value() || bNum.has_value()) {
|
||||
return aNum.has_value() && bNum.has_value() && nearlyEqual(*aNum, *bNum);
|
||||
}
|
||||
if (a.index() != b.index()) {
|
||||
return false;
|
||||
}
|
||||
return std::visit(
|
||||
[&](const auto& av) {
|
||||
using T = std::decay_t<decltype(av)>;
|
||||
const auto* bv = std::get_if<T>(&b);
|
||||
return bv != nullptr && av == *bv;
|
||||
},
|
||||
a);
|
||||
}
|
||||
|
||||
bool widgetSettingsEqual(const std::unordered_map<std::string, WidgetSettingValue>& a,
|
||||
const std::unordered_map<std::string, WidgetSettingValue>& b) {
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& [key, value] : a) {
|
||||
const auto it = b.find(key);
|
||||
if (it == b.end() || !widgetSettingEqual(value, it->second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool barBaseConfigEqual(const BarConfig& a, const BarConfig& b) {
|
||||
return a.name == b.name && a.position == b.position && a.enabled == b.enabled && a.autoHide == b.autoHide &&
|
||||
a.reserveSpace == b.reserveSpace && a.thickness == b.thickness &&
|
||||
nearlyEqual(a.backgroundOpacity, b.backgroundOpacity) && a.radius == b.radius &&
|
||||
a.radiusTopLeft == b.radiusTopLeft && a.radiusTopRight == b.radiusTopRight &&
|
||||
a.radiusBottomLeft == b.radiusBottomLeft && a.radiusBottomRight == b.radiusBottomRight &&
|
||||
a.marginEnds == b.marginEnds && a.marginEdge == b.marginEdge && a.padding == b.padding &&
|
||||
a.widgetSpacing == b.widgetSpacing && a.shadow == b.shadow && a.contactShadow == b.contactShadow &&
|
||||
a.attachPanels == b.attachPanels && nearlyEqual(a.scale, b.scale) && a.startWidgets == b.startWidgets &&
|
||||
a.centerWidgets == b.centerWidgets && a.endWidgets == b.endWidgets &&
|
||||
a.widgetCapsuleDefault == b.widgetCapsuleDefault &&
|
||||
colorSpecEqual(a.widgetCapsuleFill, b.widgetCapsuleFill) &&
|
||||
optionalColorSpecEqual(a.widgetCapsuleForeground, b.widgetCapsuleForeground) &&
|
||||
optionalColorSpecEqual(a.widgetColor, b.widgetColor) && a.widgetCapsuleGroups == b.widgetCapsuleGroups &&
|
||||
nearlyEqual(a.widgetCapsulePadding, b.widgetCapsulePadding) &&
|
||||
nearlyEqual(a.widgetCapsuleOpacity, b.widgetCapsuleOpacity) &&
|
||||
a.widgetCapsuleBorderSpecified == b.widgetCapsuleBorderSpecified &&
|
||||
optionalColorSpecEqual(a.widgetCapsuleBorder, b.widgetCapsuleBorder);
|
||||
}
|
||||
|
||||
BarConfig applyMonitorOverrideForComparison(const BarConfig& base, const BarMonitorOverride& ovr) {
|
||||
BarConfig resolved = base;
|
||||
resolved.monitorOverrides.clear();
|
||||
if (ovr.enabled) {
|
||||
resolved.enabled = *ovr.enabled;
|
||||
}
|
||||
if (ovr.autoHide) {
|
||||
resolved.autoHide = *ovr.autoHide;
|
||||
}
|
||||
if (ovr.reserveSpace) {
|
||||
resolved.reserveSpace = *ovr.reserveSpace;
|
||||
}
|
||||
if (ovr.thickness) {
|
||||
resolved.thickness = *ovr.thickness;
|
||||
}
|
||||
if (ovr.backgroundOpacity) {
|
||||
resolved.backgroundOpacity = *ovr.backgroundOpacity;
|
||||
}
|
||||
if (ovr.radius) {
|
||||
resolved.radius = *ovr.radius;
|
||||
resolved.radiusTopLeft = *ovr.radius;
|
||||
resolved.radiusTopRight = *ovr.radius;
|
||||
resolved.radiusBottomLeft = *ovr.radius;
|
||||
resolved.radiusBottomRight = *ovr.radius;
|
||||
}
|
||||
if (ovr.radiusTopLeft) {
|
||||
resolved.radiusTopLeft = *ovr.radiusTopLeft;
|
||||
}
|
||||
if (ovr.radiusTopRight) {
|
||||
resolved.radiusTopRight = *ovr.radiusTopRight;
|
||||
}
|
||||
if (ovr.radiusBottomLeft) {
|
||||
resolved.radiusBottomLeft = *ovr.radiusBottomLeft;
|
||||
}
|
||||
if (ovr.radiusBottomRight) {
|
||||
resolved.radiusBottomRight = *ovr.radiusBottomRight;
|
||||
}
|
||||
if (ovr.marginEnds) {
|
||||
resolved.marginEnds = *ovr.marginEnds;
|
||||
}
|
||||
if (ovr.marginEdge) {
|
||||
resolved.marginEdge = *ovr.marginEdge;
|
||||
}
|
||||
if (ovr.padding) {
|
||||
resolved.padding = *ovr.padding;
|
||||
}
|
||||
if (ovr.widgetSpacing) {
|
||||
resolved.widgetSpacing = *ovr.widgetSpacing;
|
||||
}
|
||||
if (ovr.shadow) {
|
||||
resolved.shadow = *ovr.shadow;
|
||||
}
|
||||
if (ovr.contactShadow) {
|
||||
resolved.contactShadow = *ovr.contactShadow;
|
||||
}
|
||||
if (ovr.attachPanels) {
|
||||
resolved.attachPanels = *ovr.attachPanels;
|
||||
}
|
||||
if (ovr.startWidgets) {
|
||||
resolved.startWidgets = *ovr.startWidgets;
|
||||
}
|
||||
if (ovr.centerWidgets) {
|
||||
resolved.centerWidgets = *ovr.centerWidgets;
|
||||
}
|
||||
if (ovr.endWidgets) {
|
||||
resolved.endWidgets = *ovr.endWidgets;
|
||||
}
|
||||
if (ovr.scale) {
|
||||
resolved.scale = *ovr.scale;
|
||||
}
|
||||
if (ovr.widgetCapsuleDefault) {
|
||||
resolved.widgetCapsuleDefault = *ovr.widgetCapsuleDefault;
|
||||
}
|
||||
if (ovr.widgetCapsuleFill) {
|
||||
resolved.widgetCapsuleFill = *ovr.widgetCapsuleFill;
|
||||
}
|
||||
if (ovr.widgetCapsuleBorderSpecified) {
|
||||
resolved.widgetCapsuleBorderSpecified = true;
|
||||
resolved.widgetCapsuleBorder = ovr.widgetCapsuleBorder;
|
||||
}
|
||||
if (ovr.widgetCapsuleForeground) {
|
||||
resolved.widgetCapsuleForeground = *ovr.widgetCapsuleForeground;
|
||||
}
|
||||
if (ovr.widgetColor) {
|
||||
resolved.widgetColor = *ovr.widgetColor;
|
||||
}
|
||||
if (ovr.widgetCapsuleGroups) {
|
||||
resolved.widgetCapsuleGroups = *ovr.widgetCapsuleGroups;
|
||||
}
|
||||
if (ovr.widgetCapsulePadding) {
|
||||
resolved.widgetCapsulePadding = std::clamp(static_cast<float>(*ovr.widgetCapsulePadding), 0.0f, 48.0f);
|
||||
}
|
||||
if (ovr.widgetCapsuleOpacity) {
|
||||
resolved.widgetCapsuleOpacity = std::clamp(static_cast<float>(*ovr.widgetCapsuleOpacity), 0.0f, 1.0f);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
bool barMonitorOverrideEqual(const BarConfig& base, const BarMonitorOverride& a, const BarMonitorOverride& b) {
|
||||
return a.match == b.match &&
|
||||
barBaseConfigEqual(applyMonitorOverrideForComparison(base, a), applyMonitorOverrideForComparison(base, b));
|
||||
}
|
||||
|
||||
bool barConfigEqual(const BarConfig& a, const BarConfig& b) {
|
||||
return barBaseConfigEqual(a, b) && vectorEqual(a.monitorOverrides, b.monitorOverrides,
|
||||
[&a](const BarMonitorOverride& lhs, const BarMonitorOverride& rhs) {
|
||||
return barMonitorOverrideEqual(a, lhs, rhs);
|
||||
});
|
||||
}
|
||||
|
||||
bool widgetConfigEqual(const WidgetConfig& a, const WidgetConfig& b) {
|
||||
return a.type == b.type && widgetSettingsEqual(a.settings, b.settings);
|
||||
}
|
||||
|
||||
bool widgetMapEqual(const std::unordered_map<std::string, WidgetConfig>& a,
|
||||
const std::unordered_map<std::string, WidgetConfig>& b) {
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& [key, value] : a) {
|
||||
const auto it = b.find(key);
|
||||
if (it == b.end() || !widgetConfigEqual(value, it->second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wallpaperMonitorOverrideEqual(const WallpaperMonitorOverride& a, const WallpaperMonitorOverride& b) {
|
||||
return a.match == b.match && a.enabled == b.enabled && optionalColorSpecEqual(a.fillColor, b.fillColor) &&
|
||||
a.directory == b.directory && a.directoryLight == b.directoryLight && a.directoryDark == b.directoryDark;
|
||||
}
|
||||
|
||||
bool wallpaperConfigEqual(const WallpaperConfig& a, const WallpaperConfig& b) {
|
||||
return a.enabled == b.enabled && a.fillMode == b.fillMode && optionalColorSpecEqual(a.fillColor, b.fillColor) &&
|
||||
a.transitions == b.transitions && nearlyEqual(a.transitionDurationMs, b.transitionDurationMs) &&
|
||||
nearlyEqual(a.edgeSmoothness, b.edgeSmoothness) && a.directory == b.directory &&
|
||||
a.directoryLight == b.directoryLight && a.directoryDark == b.directoryDark &&
|
||||
a.automation.enabled == b.automation.enabled &&
|
||||
a.automation.intervalMinutes == b.automation.intervalMinutes && a.automation.order == b.automation.order &&
|
||||
a.automation.recursive == b.automation.recursive &&
|
||||
vectorEqual(a.monitorOverrides, b.monitorOverrides, wallpaperMonitorOverrideEqual);
|
||||
}
|
||||
|
||||
bool dockConfigEqual(const DockConfig& a, const DockConfig& b) {
|
||||
return a.enabled == b.enabled && a.position == b.position && a.activeMonitorOnly == b.activeMonitorOnly &&
|
||||
a.iconSize == b.iconSize && a.padding == b.padding && a.itemSpacing == b.itemSpacing &&
|
||||
nearlyEqual(a.backgroundOpacity, b.backgroundOpacity) && a.radius == b.radius &&
|
||||
a.marginEnds == b.marginEnds && a.marginEdge == b.marginEdge && a.shadow == b.shadow &&
|
||||
a.showRunning == b.showRunning && a.autoHide == b.autoHide && a.reserveSpace == b.reserveSpace &&
|
||||
nearlyEqual(a.activeScale, b.activeScale) && nearlyEqual(a.inactiveScale, b.inactiveScale) &&
|
||||
nearlyEqual(a.activeOpacity, b.activeOpacity) && nearlyEqual(a.inactiveOpacity, b.inactiveOpacity) &&
|
||||
a.showInstanceCount == b.showInstanceCount && a.pinned == b.pinned;
|
||||
}
|
||||
|
||||
bool shellConfigEqual(const ShellConfig& a, const ShellConfig& b) {
|
||||
return nearlyEqual(a.uiScale, b.uiScale) && a.fontFamily == b.fontFamily && a.lang == b.lang &&
|
||||
a.offlineMode == b.offlineMode && a.telemetryEnabled == b.telemetryEnabled &&
|
||||
a.polkitAgent == b.polkitAgent && a.passwordMaskStyle == b.passwordMaskStyle &&
|
||||
a.animation.enabled == b.animation.enabled && nearlyEqual(a.animation.speed, b.animation.speed) &&
|
||||
a.avatarPath == b.avatarPath && a.settingsShowAdvanced == b.settingsShowAdvanced &&
|
||||
a.showLocation == b.showLocation && a.clipboardAutoPaste == b.clipboardAutoPaste &&
|
||||
a.shadow.blur == b.shadow.blur && a.shadow.offsetX == b.shadow.offsetX &&
|
||||
a.shadow.offsetY == b.shadow.offsetY && nearlyEqual(a.shadow.alpha, b.shadow.alpha) &&
|
||||
a.panel.backgroundBlur == b.panel.backgroundBlur && a.screenCorners.enabled == b.screenCorners.enabled &&
|
||||
a.screenCorners.size == b.screenCorners.size && a.mpris.blacklist == b.mpris.blacklist;
|
||||
}
|
||||
|
||||
bool notificationConfigEqual(const NotificationConfig& a, const NotificationConfig& b) {
|
||||
return a.enableDaemon == b.enableDaemon && a.position == b.position && a.layer == b.layer &&
|
||||
nearlyEqual(a.backgroundOpacity, b.backgroundOpacity) && a.monitors == b.monitors;
|
||||
}
|
||||
|
||||
bool audioConfigEqual(const AudioConfig& a, const AudioConfig& b) {
|
||||
return a.enableOverdrive == b.enableOverdrive && a.enableSounds == b.enableSounds &&
|
||||
nearlyEqual(a.soundVolume, b.soundVolume) && a.volumeChangeSound == b.volumeChangeSound &&
|
||||
a.notificationSound == b.notificationSound;
|
||||
}
|
||||
|
||||
bool nightLightConfigEqual(const NightLightConfig& a, const NightLightConfig& b) {
|
||||
return a.enabled == b.enabled && a.force == b.force && a.useWeatherLocation == b.useWeatherLocation &&
|
||||
a.startTime == b.startTime && a.stopTime == b.stopTime && optionalDoubleEqual(a.latitude, b.latitude) &&
|
||||
optionalDoubleEqual(a.longitude, b.longitude) && a.dayTemperature == b.dayTemperature &&
|
||||
a.nightTemperature == b.nightTemperature;
|
||||
}
|
||||
|
||||
bool idleConfigEqual(const IdleConfig& a, const IdleConfig& b) {
|
||||
return vectorEqual(a.behaviors, b.behaviors, [](const IdleBehaviorConfig& lhs, const IdleBehaviorConfig& rhs) {
|
||||
return lhs.name == rhs.name && lhs.enabled == rhs.enabled && lhs.timeoutSeconds == rhs.timeoutSeconds &&
|
||||
lhs.command == rhs.command && lhs.resumeCommand == rhs.resumeCommand;
|
||||
});
|
||||
}
|
||||
|
||||
bool themeConfigEqual(const ThemeConfig& a, const ThemeConfig& b) {
|
||||
return a.source == b.source && a.builtinPalette == b.builtinPalette && a.communityPalette == b.communityPalette &&
|
||||
a.wallpaperScheme == b.wallpaperScheme && a.mode == b.mode &&
|
||||
a.templates.enableBuiltinTemplates == b.templates.enableBuiltinTemplates &&
|
||||
a.templates.builtinIds == b.templates.builtinIds &&
|
||||
a.templates.enableCommunityTemplates == b.templates.enableCommunityTemplates &&
|
||||
a.templates.communityIds == b.templates.communityIds &&
|
||||
a.templates.enableUserTemplates == b.templates.enableUserTemplates &&
|
||||
a.templates.userConfig == b.templates.userConfig;
|
||||
}
|
||||
|
||||
bool configEqual(const Config& a, const Config& b) {
|
||||
return vectorEqual(a.bars, b.bars, barConfigEqual) && widgetMapEqual(a.widgets, b.widgets) &&
|
||||
wallpaperConfigEqual(a.wallpaper, b.wallpaper) && a.backdrop.enabled == b.backdrop.enabled &&
|
||||
nearlyEqual(a.backdrop.blurIntensity, b.backdrop.blurIntensity) &&
|
||||
nearlyEqual(a.backdrop.tintIntensity, b.backdrop.tintIntensity) && dockConfigEqual(a.dock, b.dock) &&
|
||||
a.desktopWidgets == b.desktopWidgets && shellConfigEqual(a.shell, b.shell) &&
|
||||
a.osd.position == b.osd.position && notificationConfigEqual(a.notification, b.notification) &&
|
||||
a.weather.enabled == b.weather.enabled && a.weather.autoLocate == b.weather.autoLocate &&
|
||||
a.weather.effects == b.weather.effects && a.weather.address == b.weather.address &&
|
||||
a.weather.refreshMinutes == b.weather.refreshMinutes && a.weather.unit == b.weather.unit &&
|
||||
a.system.monitor.enabled == b.system.monitor.enabled && audioConfigEqual(a.audio, b.audio) &&
|
||||
a.brightness == b.brightness && a.keybinds.validate == b.keybinds.validate &&
|
||||
a.keybinds.cancel == b.keybinds.cancel && a.keybinds.left == b.keybinds.left &&
|
||||
a.keybinds.right == b.keybinds.right && a.keybinds.up == b.keybinds.up &&
|
||||
a.keybinds.down == b.keybinds.down && nightLightConfigEqual(a.nightlight, b.nightlight) &&
|
||||
idleConfigEqual(a.idle, b.idle) && a.hooks == b.hooks && themeConfigEqual(a.theme, b.theme) &&
|
||||
a.controlCenter == b.controlCenter;
|
||||
}
|
||||
|
||||
toml::table* ensureTable(toml::table& parent, std::string_view key) {
|
||||
if (auto* existing = parent.get_as<toml::table>(key)) {
|
||||
@@ -99,6 +436,46 @@ namespace {
|
||||
parent->erase(changedPath[depth - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool eraseOverridePath(toml::table& root, const std::vector<std::string>& path, std::size_t preserveDepth = 0) {
|
||||
if (path.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
toml::table* table = &root;
|
||||
for (std::size_t i = 0; i + 1 < path.size(); ++i) {
|
||||
auto* next = table->get_as<toml::table>(path[i]);
|
||||
if (next == nullptr) {
|
||||
return false;
|
||||
}
|
||||
table = next;
|
||||
}
|
||||
|
||||
if (table->erase(path.back()) == 0) {
|
||||
return false;
|
||||
}
|
||||
pruneEmptyOverrideTables(root, path, preserveDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> sortedConfigTomlFiles(std::string_view configDir) {
|
||||
std::vector<std::filesystem::path> files;
|
||||
if (configDir.empty()) {
|
||||
return files;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::is_directory(configDir, ec) || ec) {
|
||||
return files;
|
||||
}
|
||||
for (const auto& entry : std::filesystem::directory_iterator(configDir, ec)) {
|
||||
if (entry.is_regular_file() && entry.path().extension() == ".toml") {
|
||||
files.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
std::sort(files.begin(), files.end());
|
||||
return files;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ConfigService::setThemeMode(ThemeMode mode) {
|
||||
@@ -168,6 +545,94 @@ bool ConfigService::hasOverride(const std::vector<std::string>& path) const {
|
||||
return findOverrideNode(m_overridesTable, path) != nullptr;
|
||||
}
|
||||
|
||||
bool ConfigService::hasEffectiveOverride(const std::vector<std::string>& path) const {
|
||||
if (path.empty() || findOverrideNode(m_overridesTable, path) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string key = overrideCacheKey(path);
|
||||
if (const auto it = m_effectiveOverrideCache.find(key); it != m_effectiveOverrideCache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const bool effective = overridePathEffectiveInTable(path, m_overridesTable, &m_config);
|
||||
m_effectiveOverrideCache[key] = effective;
|
||||
return effective;
|
||||
}
|
||||
|
||||
std::size_t ConfigService::overridePreserveDepthForPath(const std::vector<std::string>& path) const {
|
||||
if (path.size() > 4 && path[0] == "bar" && path[2] == "monitor" && isOverrideOnlyMonitorOverride(path[1], path[3])) {
|
||||
return 4;
|
||||
}
|
||||
if (path.size() > 2 && path[0] == "bar" && isOverrideOnlyBar(path[1])) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<Config> ConfigService::configForOverrides(const toml::table& overrides) const {
|
||||
Config parsed;
|
||||
seedBuiltinWidgets(parsed);
|
||||
|
||||
const auto files = sortedConfigTomlFiles(m_configDir);
|
||||
toml::table merged;
|
||||
for (const auto& path : files) {
|
||||
try {
|
||||
auto tbl = toml::parse_file(path.string());
|
||||
deepMerge(merged, tbl);
|
||||
} catch (const toml::parse_error& e) {
|
||||
kLog.warn("skipping parse error in effective override comparison {}: {}", path.filename().string(),
|
||||
e.description());
|
||||
}
|
||||
}
|
||||
|
||||
deepMerge(merged, overrides);
|
||||
if (files.empty() && overrides.empty()) {
|
||||
parsed.idle.behaviors.push_back(IdleBehaviorConfig{
|
||||
.name = "lock",
|
||||
.enabled = false,
|
||||
.timeoutSeconds = 660,
|
||||
.command = "noctalia:screen-lock",
|
||||
.resumeCommand = "",
|
||||
});
|
||||
parsed.bars.push_back(BarConfig{});
|
||||
return parsed;
|
||||
}
|
||||
|
||||
try {
|
||||
parseTableInto(merged, parsed, false);
|
||||
} catch (const std::exception& e) {
|
||||
kLog.warn("effective override comparison parse failed: {}", e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool ConfigService::overridePathEffectiveInTable(const std::vector<std::string>& path, const toml::table& overrides,
|
||||
const Config* parsedWith) const {
|
||||
if (path.empty() || findOverrideNode(overrides, path) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<Config> ownedWithOverride;
|
||||
if (parsedWith == nullptr) {
|
||||
ownedWithOverride = configForOverrides(overrides);
|
||||
if (!ownedWithOverride.has_value()) {
|
||||
return true;
|
||||
}
|
||||
parsedWith = &*ownedWithOverride;
|
||||
}
|
||||
|
||||
toml::table withoutTable = overrides;
|
||||
eraseOverridePath(withoutTable, path, overridePreserveDepthForPath(path));
|
||||
auto withoutOverride = configForOverrides(withoutTable);
|
||||
if (!withoutOverride.has_value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !configEqual(*parsedWith, *withoutOverride);
|
||||
}
|
||||
|
||||
bool ConfigService::isOverrideOnlyBar(std::string_view name) const {
|
||||
if (name.empty() || !hasOverride({"bar", std::string(name)})) {
|
||||
return false;
|
||||
@@ -412,6 +877,9 @@ bool ConfigService::setOverride(const std::vector<std::string>& path, ConfigOver
|
||||
}
|
||||
|
||||
insertOverrideValue(*table, path.back(), value);
|
||||
if (!overridePathEffectiveInTable(path, m_overridesTable)) {
|
||||
eraseOverridePath(m_overridesTable, path, overridePreserveDepthForPath(path));
|
||||
}
|
||||
|
||||
if (!writeOverridesToFile()) {
|
||||
kLog.warn("failed to write {}", m_overridesPath);
|
||||
@@ -430,26 +898,9 @@ bool ConfigService::clearOverride(const std::vector<std::string>& path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t preserveDepth = 0;
|
||||
if (path.size() > 4 && path[0] == "bar" && path[2] == "monitor" && isOverrideOnlyMonitorOverride(path[1], path[3])) {
|
||||
preserveDepth = 4;
|
||||
} else if (path.size() > 2 && path[0] == "bar" && isOverrideOnlyBar(path[1])) {
|
||||
preserveDepth = 2;
|
||||
}
|
||||
|
||||
toml::table* table = &m_overridesTable;
|
||||
for (std::size_t i = 0; i + 1 < path.size(); ++i) {
|
||||
auto* next = table->get_as<toml::table>(path[i]);
|
||||
if (next == nullptr) {
|
||||
return false;
|
||||
}
|
||||
table = next;
|
||||
}
|
||||
|
||||
if (table->erase(path.back()) == 0) {
|
||||
if (!eraseOverridePath(m_overridesTable, path, overridePreserveDepthForPath(path))) {
|
||||
return false;
|
||||
}
|
||||
pruneEmptyOverrideTables(m_overridesTable, path, preserveDepth);
|
||||
|
||||
if (!writeOverridesToFile()) {
|
||||
kLog.warn("failed to write {}", m_overridesPath);
|
||||
|
||||
@@ -770,6 +770,7 @@ void ConfigService::seedBuiltinWidgets(Config& config) {
|
||||
}
|
||||
|
||||
void ConfigService::loadAll() {
|
||||
m_effectiveOverrideCache.clear();
|
||||
m_config = Config{};
|
||||
seedBuiltinWidgets(m_config);
|
||||
|
||||
@@ -866,7 +867,9 @@ void ConfigService::loadAll() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigService::parseTable(const toml::table& tbl) {
|
||||
void ConfigService::parseTable(const toml::table& tbl) { parseTableInto(tbl, m_config, true); }
|
||||
|
||||
void ConfigService::parseTableInto(const toml::table& tbl, Config& config, bool logSummary) const {
|
||||
// Parse [bar.*] named subtables
|
||||
if (auto* barTblMap = tbl["bar"].as_table()) {
|
||||
std::vector<BarConfig> parsedBars;
|
||||
@@ -1064,7 +1067,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
for (std::size_t i = 0; i < parsedBars.size(); ++i) {
|
||||
if (!used[i] && parsedBars[i].name == orderedName) {
|
||||
used[i] = true;
|
||||
m_config.bars.push_back(std::move(parsedBars[i]));
|
||||
config.bars.push_back(std::move(parsedBars[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1072,7 +1075,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
for (std::size_t i = 0; i < parsedBars.size(); ++i) {
|
||||
if (!used[i]) {
|
||||
m_config.bars.push_back(std::move(parsedBars[i]));
|
||||
config.bars.push_back(std::move(parsedBars[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1090,10 +1093,10 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
if (auto v = (*entryTbl)["type"].value<std::string>()) {
|
||||
wc.type = *v;
|
||||
if (auto it = m_config.widgets.find(widgetName); it != m_config.widgets.end() && it->second.type == wc.type) {
|
||||
if (auto it = config.widgets.find(widgetName); it != config.widgets.end() && it->second.type == wc.type) {
|
||||
wc.settings = it->second.settings;
|
||||
}
|
||||
} else if (auto it = m_config.widgets.find(widgetName); it != m_config.widgets.end()) {
|
||||
} else if (auto it = config.widgets.find(widgetName); it != config.widgets.end()) {
|
||||
wc = it->second;
|
||||
} else {
|
||||
wc.type = widgetName;
|
||||
@@ -1123,13 +1126,13 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
}
|
||||
}
|
||||
|
||||
m_config.widgets[widgetName] = std::move(wc);
|
||||
config.widgets[widgetName] = std::move(wc);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse [shell]
|
||||
if (auto* shellTbl = tbl["shell"].as_table()) {
|
||||
auto& shell = m_config.shell;
|
||||
auto& shell = config.shell;
|
||||
if (auto v = (*shellTbl)["ui_scale"].value<double>()) {
|
||||
shell.uiScale = std::clamp(static_cast<float>(*v), 0.5f, 4.0f);
|
||||
}
|
||||
@@ -1214,7 +1217,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [theme]
|
||||
if (auto* themeTbl = tbl["theme"].as_table()) {
|
||||
auto& theme = m_config.theme;
|
||||
auto& theme = config.theme;
|
||||
if (auto v = (*themeTbl)["source"].value<std::string>()) {
|
||||
if (auto parsed = enumFromKey(kThemeSources, *v)) {
|
||||
theme.source = *parsed;
|
||||
@@ -1264,7 +1267,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [wallpaper]
|
||||
if (auto* wpTbl = tbl["wallpaper"].as_table()) {
|
||||
auto& wp = m_config.wallpaper;
|
||||
auto& wp = config.wallpaper;
|
||||
if (auto v = (*wpTbl)["enabled"].value<bool>())
|
||||
wp.enabled = *v;
|
||||
if (auto v = (*wpTbl)["fill_mode"].value<std::string>()) {
|
||||
@@ -1354,7 +1357,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [backdrop]
|
||||
if (auto* ovTbl = tbl["backdrop"].as_table()) {
|
||||
auto& ov = m_config.backdrop;
|
||||
auto& ov = config.backdrop;
|
||||
if (auto v = (*ovTbl)["enabled"].value<bool>())
|
||||
ov.enabled = *v;
|
||||
if (auto v = (*ovTbl)["blur_intensity"].value<double>())
|
||||
@@ -1365,13 +1368,13 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [osd]
|
||||
if (auto* osdTbl = tbl["osd"].as_table()) {
|
||||
auto& osd = m_config.osd;
|
||||
auto& osd = config.osd;
|
||||
if (auto v = (*osdTbl)["position"].value<std::string>())
|
||||
osd.position = *v;
|
||||
}
|
||||
|
||||
auto parseNotificationTable = [this](const toml::table& notifTable) {
|
||||
auto& notif = m_config.notification;
|
||||
auto parseNotificationTable = [&config](const toml::table& notifTable) {
|
||||
auto& notif = config.notification;
|
||||
if (auto v = notifTable["enable_daemon"].value<bool>())
|
||||
notif.enableDaemon = *v;
|
||||
if (auto v = notifTable["position"].value<std::string>())
|
||||
@@ -1395,7 +1398,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [dock]
|
||||
if (auto* dockTbl = tbl["dock"].as_table()) {
|
||||
auto& dock = m_config.dock;
|
||||
auto& dock = config.dock;
|
||||
if (auto v = (*dockTbl)["enabled"].value<bool>())
|
||||
dock.enabled = *v;
|
||||
if (auto v = (*dockTbl)["active_monitor_only"].value<bool>())
|
||||
@@ -1440,7 +1443,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [desktop_widgets]
|
||||
if (auto* desktopWidgetsTbl = tbl["desktop_widgets"].as_table()) {
|
||||
auto& desktopWidgets = m_config.desktopWidgets;
|
||||
auto& desktopWidgets = config.desktopWidgets;
|
||||
if (auto v = (*desktopWidgetsTbl)["enabled"].value<bool>()) {
|
||||
desktopWidgets.enabled = *v;
|
||||
}
|
||||
@@ -1448,7 +1451,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [weather]
|
||||
if (auto* weatherTbl = tbl["weather"].as_table()) {
|
||||
auto& weather = m_config.weather;
|
||||
auto& weather = config.weather;
|
||||
if (auto v = (*weatherTbl)["enabled"].value<bool>())
|
||||
weather.enabled = *v;
|
||||
if (auto v = (*weatherTbl)["auto_locate"].value<bool>())
|
||||
@@ -1465,7 +1468,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [system]
|
||||
if (auto* systemTbl = tbl["system"].as_table()) {
|
||||
auto& system = m_config.system;
|
||||
auto& system = config.system;
|
||||
if (const auto* monitorTbl = (*systemTbl)["monitor"].as_table()) {
|
||||
if (auto v = (*monitorTbl)["enabled"].value<bool>()) {
|
||||
system.monitor.enabled = *v;
|
||||
@@ -1475,7 +1478,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [audio]
|
||||
if (auto* audioTbl = tbl["audio"].as_table()) {
|
||||
auto& audio = m_config.audio;
|
||||
auto& audio = config.audio;
|
||||
if (auto v = (*audioTbl)["enable_overdrive"].value<bool>()) {
|
||||
audio.enableOverdrive = *v;
|
||||
}
|
||||
@@ -1495,7 +1498,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [brightness]
|
||||
if (auto* brightnessTbl = tbl["brightness"].as_table()) {
|
||||
auto& brightness = m_config.brightness;
|
||||
auto& brightness = config.brightness;
|
||||
if (auto v = (*brightnessTbl)["enable_ddcutil"].value<bool>()) {
|
||||
brightness.enableDdcutil = *v;
|
||||
}
|
||||
@@ -1535,7 +1538,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [keybinds]
|
||||
if (auto* keybindsTbl = tbl["keybinds"].as_table()) {
|
||||
auto& keybinds = m_config.keybinds;
|
||||
auto& keybinds = config.keybinds;
|
||||
|
||||
auto parseAction = [&](std::string_view key, std::vector<KeyChord>& out) {
|
||||
out.clear();
|
||||
@@ -1580,7 +1583,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [nightlight]
|
||||
if (auto* nightlightTbl = tbl["nightlight"].as_table()) {
|
||||
auto& nightlight = m_config.nightlight;
|
||||
auto& nightlight = config.nightlight;
|
||||
if (auto v = (*nightlightTbl)["enabled"].value<bool>()) {
|
||||
nightlight.enabled = *v;
|
||||
}
|
||||
@@ -1624,7 +1627,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
|
||||
// Parse [hooks]
|
||||
if (auto* hooksTbl = tbl["hooks"].as_table()) {
|
||||
auto& hooks = m_config.hooks;
|
||||
auto& hooks = config.hooks;
|
||||
for (const auto& [name, node] : *hooksTbl) {
|
||||
const std::string_view keyView{name.str()};
|
||||
if (keyView == "battery_low_percent_threshold") {
|
||||
@@ -1643,7 +1646,7 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
// Parse [[control_center.shortcuts]]
|
||||
if (auto* ccTbl = tbl["control_center"].as_table()) {
|
||||
if (auto* shortcutsArr = (*ccTbl)["shortcuts"].as_array()) {
|
||||
m_config.controlCenter.shortcuts.clear();
|
||||
config.controlCenter.shortcuts.clear();
|
||||
for (const auto& entry : *shortcutsArr) {
|
||||
auto* entryTbl = entry.as_table();
|
||||
if (entryTbl == nullptr) {
|
||||
@@ -1660,13 +1663,13 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
sc.icon = *v;
|
||||
}
|
||||
if (!sc.type.empty()) {
|
||||
m_config.controlCenter.shortcuts.push_back(std::move(sc));
|
||||
config.controlCenter.shortcuts.push_back(std::move(sc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_config.controlCenter.shortcuts.empty()) {
|
||||
m_config.controlCenter.shortcuts = {
|
||||
if (config.controlCenter.shortcuts.empty()) {
|
||||
config.controlCenter.shortcuts = {
|
||||
{"wifi", {}, {}}, {"bluetooth", {}, {}}, {"caffeine", {}, {}},
|
||||
{"nightlight", {}, {}}, {"notification", {}, {}}, {"power_profile", {}, {}},
|
||||
};
|
||||
@@ -1697,34 +1700,38 @@ void ConfigService::parseTable(const toml::table& tbl) {
|
||||
behavior.resumeCommand = *v;
|
||||
}
|
||||
|
||||
m_config.idle.behaviors.push_back(std::move(behavior));
|
||||
config.idle.behaviors.push_back(std::move(behavior));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_config.bars.empty()) {
|
||||
kLog.info("no [bar.*] defined, using defaults");
|
||||
m_config.bars.push_back(BarConfig{});
|
||||
if (config.bars.empty()) {
|
||||
if (logSummary) {
|
||||
kLog.info("no [bar.*] defined, using defaults");
|
||||
}
|
||||
config.bars.push_back(BarConfig{});
|
||||
}
|
||||
|
||||
std::string barOrder;
|
||||
for (const auto& bar : m_config.bars) {
|
||||
if (!barOrder.empty()) {
|
||||
barOrder += ", ";
|
||||
if (logSummary) {
|
||||
std::string barOrder;
|
||||
for (const auto& bar : config.bars) {
|
||||
if (!barOrder.empty()) {
|
||||
barOrder += ", ";
|
||||
}
|
||||
barOrder += bar.name;
|
||||
}
|
||||
barOrder += bar.name;
|
||||
}
|
||||
kLog.info("{} bar(s) defined", m_config.bars.size());
|
||||
kLog.info("bar order: {}", barOrder);
|
||||
kLog.info("idle behaviors={}", m_config.idle.behaviors.size());
|
||||
std::size_t hookKindsUsed = 0;
|
||||
for (const auto& cmds : m_config.hooks.commands) {
|
||||
if (!cmds.empty()) {
|
||||
++hookKindsUsed;
|
||||
kLog.info("{} bar(s) defined", config.bars.size());
|
||||
kLog.info("bar order: {}", barOrder);
|
||||
kLog.info("idle behaviors={}", config.idle.behaviors.size());
|
||||
std::size_t hookKindsUsed = 0;
|
||||
for (const auto& cmds : config.hooks.commands) {
|
||||
if (!cmds.empty()) {
|
||||
++hookKindsUsed;
|
||||
}
|
||||
}
|
||||
kLog.info("hooks kinds with commands={} battery_low_threshold={}%", hookKindsUsed,
|
||||
config.hooks.batteryLowPercentThreshold);
|
||||
}
|
||||
kLog.info("hooks kinds with commands={} battery_low_threshold={}%", hookKindsUsed,
|
||||
m_config.hooks.batteryLowPercentThreshold);
|
||||
}
|
||||
|
||||
bool ConfigService::matchesKeybind(KeybindAction action, std::uint32_t sym, std::uint32_t modifiers) const {
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
void setDockEnabled(bool enabled);
|
||||
bool markSetupWizardCompleted();
|
||||
[[nodiscard]] bool hasOverride(const std::vector<std::string>& path) const;
|
||||
[[nodiscard]] bool hasEffectiveOverride(const std::vector<std::string>& path) const;
|
||||
[[nodiscard]] bool isOverrideOnlyBar(std::string_view name) const;
|
||||
[[nodiscard]] bool canMoveBarOverride(std::string_view name, int direction) const;
|
||||
[[nodiscard]] bool canDeleteBarOverride(std::string_view name) const;
|
||||
@@ -88,6 +89,11 @@ private:
|
||||
static void deepMerge(toml::table& base, const toml::table& overlay);
|
||||
void loadAll();
|
||||
void parseTable(const toml::table& tbl);
|
||||
void parseTableInto(const toml::table& tbl, Config& config, bool logSummary) const;
|
||||
[[nodiscard]] std::optional<Config> configForOverrides(const toml::table& overrides) const;
|
||||
[[nodiscard]] bool overridePathEffectiveInTable(const std::vector<std::string>& path, const toml::table& overrides,
|
||||
const Config* parsedWith = nullptr) const;
|
||||
[[nodiscard]] std::size_t overridePreserveDepthForPath(const std::vector<std::string>& path) const;
|
||||
void setupWatch();
|
||||
void fireReloadCallbacks();
|
||||
void loadOverridesFromFile();
|
||||
@@ -108,6 +114,7 @@ private:
|
||||
std::string m_defaultWallpaperPath;
|
||||
std::unordered_map<std::string, std::string> m_monitorWallpaperPaths;
|
||||
bool m_setupWizardCompleted = false;
|
||||
mutable std::unordered_map<std::string, bool> m_effectiveOverrideCache;
|
||||
|
||||
std::string m_pendingError; // parse error from initial load, sent as notification once manager is wired up
|
||||
uint32_t m_configErrorNotificationId = 0; // ID of the active config-error notification, 0 if none
|
||||
|
||||
@@ -691,7 +691,7 @@ namespace settings {
|
||||
continue;
|
||||
}
|
||||
const auto path = widgetSettingPath(std::string(widgetName), key);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasOverride(path);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(path);
|
||||
if (ctx.showOverriddenOnly && !overridden) {
|
||||
continue;
|
||||
}
|
||||
@@ -722,7 +722,7 @@ namespace settings {
|
||||
}
|
||||
const auto path = widgetSettingPath(std::string(widgetName), key);
|
||||
const std::string deleteKey = pathKey(path);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasOverride(path);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(path);
|
||||
const bool pendingDelete = ctx.pendingDeleteWidgetSettingPath == deleteKey;
|
||||
|
||||
auto row = std::make_unique<Flex>();
|
||||
@@ -781,7 +781,7 @@ namespace settings {
|
||||
}
|
||||
|
||||
auto path = widgetSettingPath(std::string(widgetName), "type");
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasOverride(path);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(path);
|
||||
if (ctx.showOverriddenOnly && !overridden) {
|
||||
return;
|
||||
}
|
||||
@@ -856,7 +856,7 @@ namespace settings {
|
||||
continue;
|
||||
}
|
||||
const auto path = widgetSettingPath(widgetName, spec.key);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasOverride(path);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(path);
|
||||
if (ctx.showOverriddenOnly && !overridden) {
|
||||
continue;
|
||||
}
|
||||
@@ -1065,8 +1065,14 @@ namespace settings {
|
||||
currentLaneKey = std::string(laneKey);
|
||||
currentLanePath = std::move(p);
|
||||
currentLaneItems = std::move(items);
|
||||
currentLaneInherited = isMonitorWidgetListPath(currentLanePath) &&
|
||||
!monitorWidgetListHasExplicitValue(ctx.config, currentLanePath);
|
||||
const bool currentLaneOverridden =
|
||||
ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(currentLanePath);
|
||||
const bool currentLaneRedundantGuiOverride = ctx.configService != nullptr &&
|
||||
ctx.configService->hasOverride(currentLanePath) &&
|
||||
!currentLaneOverridden;
|
||||
currentLaneInherited =
|
||||
isMonitorWidgetListPath(currentLanePath) &&
|
||||
(!monitorWidgetListHasExplicitValue(ctx.config, currentLanePath) || currentLaneRedundantGuiOverride);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1580,9 +1586,11 @@ namespace settings {
|
||||
for (const auto laneKey : kLaneKeys) {
|
||||
auto lanePath = pathWithLastSegment(entry.path, std::string(laneKey));
|
||||
const auto laneItems = barWidgetItemsForPath(ctx.config, lanePath);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasOverride(lanePath);
|
||||
const bool inherited =
|
||||
isMonitorWidgetListPath(lanePath) && !monitorWidgetListHasExplicitValue(ctx.config, lanePath);
|
||||
const bool overridden = ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(lanePath);
|
||||
const bool redundantGuiOverride =
|
||||
ctx.configService != nullptr && ctx.configService->hasOverride(lanePath) && !overridden;
|
||||
const bool inherited = isMonitorWidgetListPath(lanePath) &&
|
||||
(!monitorWidgetListHasExplicitValue(ctx.config, lanePath) || redundantGuiOverride);
|
||||
|
||||
auto lane = std::make_unique<Flex>();
|
||||
lane->setDirection(FlexDirection::Vertical);
|
||||
|
||||
@@ -426,9 +426,11 @@ namespace settings {
|
||||
};
|
||||
|
||||
const auto makeRow = [&](Flex& section, const SettingEntry& entry, std::unique_ptr<Node> control) {
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasOverride(entry.path));
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(entry.path));
|
||||
const bool redundantGuiOverride =
|
||||
ctx.configService != nullptr && ctx.configService->hasOverride(entry.path) && !overridden;
|
||||
const bool monitorSetting = isMonitorOverrideSettingPath(entry.path);
|
||||
const bool monitorExplicit = monitorOverrideHasExplicitValue(cfg, entry.path);
|
||||
const bool monitorExplicit = monitorOverrideHasExplicitValue(cfg, entry.path) && !redundantGuiOverride;
|
||||
const bool monitorInherited = monitorSetting && !monitorExplicit;
|
||||
|
||||
auto row = std::make_unique<Flex>();
|
||||
@@ -792,7 +794,7 @@ namespace settings {
|
||||
|
||||
const auto makeSearchPickerBlock = [&](Flex& section, const SettingEntry& entry,
|
||||
const SearchPickerSetting& setting) {
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasOverride(entry.path));
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(entry.path));
|
||||
const std::string pickerPath = pathKey(entry.path);
|
||||
|
||||
auto block = std::make_unique<Flex>();
|
||||
@@ -912,7 +914,7 @@ namespace settings {
|
||||
};
|
||||
|
||||
const auto makeMultiSelectBlock = [&](Flex& section, const SettingEntry& entry, const MultiSelectSetting& setting) {
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasOverride(entry.path));
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(entry.path));
|
||||
|
||||
auto block = std::make_unique<Flex>();
|
||||
block->setDirection(FlexDirection::Vertical);
|
||||
@@ -1004,7 +1006,7 @@ namespace settings {
|
||||
};
|
||||
|
||||
const auto makeListBlock = [&](Flex& section, const SettingEntry& entry, const ListSetting& list) {
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasOverride(entry.path));
|
||||
const bool overridden = (ctx.configService != nullptr && ctx.configService->hasEffectiveOverride(entry.path));
|
||||
|
||||
auto block = std::make_unique<Flex>();
|
||||
block->setDirection(FlexDirection::Vertical);
|
||||
@@ -1176,7 +1178,8 @@ namespace settings {
|
||||
if (!ctx.showAdvanced && entry.advanced) {
|
||||
continue;
|
||||
}
|
||||
if (ctx.showOverriddenOnly && ctx.configService != nullptr && !ctx.configService->hasOverride(entry.path)) {
|
||||
if (ctx.showOverriddenOnly && ctx.configService != nullptr &&
|
||||
!ctx.configService->hasEffectiveOverride(entry.path)) {
|
||||
continue;
|
||||
}
|
||||
if (!matchesNormalizedSettingQuery(entry, normalizedSearchQuery)) {
|
||||
|
||||
@@ -1014,7 +1014,7 @@ void SettingsWindow::buildScene(std::uint32_t width, std::uint32_t height) {
|
||||
if (m_config != nullptr) {
|
||||
for (const auto& entry : m_settingsRegistry) {
|
||||
if (settingEntryBelongsToPage(entry, m_selectedSection, m_selectedBarName, m_selectedMonitorOverride) &&
|
||||
m_config->hasOverride(entry.path) && !containsPath(resetPagePaths, entry.path)) {
|
||||
m_config->hasEffectiveOverride(entry.path) && !containsPath(resetPagePaths, entry.path)) {
|
||||
resetPagePaths.push_back(entry.path);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user