#pragma once #include #include #include #include "../config/ConfigValue.hpp" enum eOverridePriority : uint8_t { PRIORITY_LAYOUT = 0, PRIORITY_WORKSPACE_RULE, PRIORITY_WINDOW_RULE, PRIORITY_SET_PROP, }; template T clampOptional(T const& value, std::optional const& min, std::optional const& max) { return std::clamp(value, min.value_or(std::numeric_limits::min()), max.value_or(std::numeric_limits::max())); } template || std::is_same_v || std::is_same_v> class CWindowOverridableVar { public: CWindowOverridableVar(T const& value, eOverridePriority priority) { m_values[priority] = value; } CWindowOverridableVar(T const& value) : m_defaultValue{value} {} CWindowOverridableVar(T const& value, std::optional const& min, std::optional const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {} CWindowOverridableVar(std::string const& value) requires(Extended && !std::is_same_v) : m_configValue(SP>(new CConfigValue(value))) {} CWindowOverridableVar(std::string const& value, std::optional const& min, std::optional const& max = std::nullopt) requires(Extended && !std::is_same_v) : m_minValue(min), m_maxValue(max), m_configValue(SP>(new CConfigValue(value))) {} CWindowOverridableVar() = default; ~CWindowOverridableVar() = default; CWindowOverridableVar& operator=(CWindowOverridableVar const& other) { // Self-assignment check if (this == &other) return *this; for (auto const& value : other.m_values) { if constexpr (Extended && !std::is_same_v) m_values[value.first] = clampOptional(value.second, m_minValue, m_maxValue); else m_values[value.first] = value.second; } return *this; } void unset(eOverridePriority priority) { m_values.erase(priority); } bool hasValue() { return !m_values.empty(); } T value() { if (!m_values.empty()) return std::prev(m_values.end())->second; else throw std::bad_optional_access(); } T valueOr(T const& other) { if (hasValue()) return value(); else return other; } T valueOrDefault() requires(Extended && !std::is_same_v) { if (hasValue()) return value(); else if (m_defaultValue.has_value()) return m_defaultValue.value(); else return **std::any_cast>>(m_configValue); } T valueOrDefault() requires(!Extended || std::is_same_v) { if (hasValue()) return value(); else if (!m_defaultValue.has_value()) throw std::bad_optional_access(); else return m_defaultValue.value(); } eOverridePriority getPriority() { if (!m_values.empty()) return std::prev(m_values.end())->first; else throw std::bad_optional_access(); } void increment(T const& other, eOverridePriority priority) { if constexpr (std::is_same_v) m_values[priority] = valueOr(false) ^ other; else m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue); } void matchOptional(std::optional const& optValue, eOverridePriority priority) { if (optValue.has_value()) m_values[priority] = optValue.value(); else unset(priority); } operator std::optional() { if (hasValue()) return value(); else return std::nullopt; } private: std::map m_values; std::optional m_defaultValue; // used for toggling, so required for bool std::optional m_minValue; std::optional m_maxValue; std::any m_configValue; // only there for select variables };