summaryrefslogtreecommitdiff
path: root/src/desktop/WindowOverridableVar.hpp
blob: ea113d3e6585eedcda5f8c7ee2a289fe8f121e54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#pragma once

#include <cstdint>
#include <type_traits>
#include <any>
#include "../config/ConfigValue.hpp"

enum eOverridePriority : uint8_t {
    PRIORITY_LAYOUT = 0,
    PRIORITY_WORKSPACE_RULE,
    PRIORITY_WINDOW_RULE,
    PRIORITY_SET_PROP,
};

template <typename T>
T clampOptional(T const& value, std::optional<T> const& min, std::optional<T> const& max) {
    return std::clamp(value, min.value_or(std::numeric_limits<T>::min()), max.value_or(std::numeric_limits<T>::max()));
}

template <typename T, bool Extended = std::is_same_v<T, bool> || std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, Hyprlang::FLOAT>>
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<T> const& min, std::optional<T> const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {}
    CWindowOverridableVar(std::string const& value)
        requires(Extended && !std::is_same_v<T, bool>)
        : m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
    CWindowOverridableVar(std::string const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt)
        requires(Extended && !std::is_same_v<T, bool>)
        : m_minValue(min), m_maxValue(max), m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}

    CWindowOverridableVar()  = default;
    ~CWindowOverridableVar() = default;

    CWindowOverridableVar& operator=(CWindowOverridableVar<T> const& other) {
        // Self-assignment check
        if (this == &other)
            return *this;

        for (auto const& value : other.m_values) {
            if constexpr (Extended && !std::is_same_v<T, bool>)
                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<T, bool>)
    {
        if (hasValue())
            return value();
        else if (m_defaultValue.has_value())
            return m_defaultValue.value();
        else
            return **std::any_cast<SP<CConfigValue<T>>>(m_configValue);
    }

    T valueOrDefault()
        requires(!Extended || std::is_same_v<T, bool>)
    {
        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<T, bool>)
            m_values[priority] = valueOr(false) ^ other;
        else
            m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue);
    }

    void matchOptional(std::optional<T> const& optValue, eOverridePriority priority) {
        if (optValue.has_value())
            m_values[priority] = optValue.value();
        else
            unset(priority);
    }

    operator std::optional<T>() {
        if (hasValue())
            return value();
        else
            return std::nullopt;
    }

  private:
    std::map<eOverridePriority, T> m_values;
    std::optional<T>               m_defaultValue; // used for toggling, so required for bool
    std::optional<T>               m_minValue;
    std::optional<T>               m_maxValue;
    std::any                       m_configValue; // only there for select variables
};