diff --git a/nix/hm-module.nix b/nix/hm-module.nix index b8b053a..06bf45b 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -90,68 +90,67 @@ in { backgrounds = mkOption { description = "Background configurations"; type = listOf (submodule { - options = - { - monitor = mkOption { - description = "The monitor to apply the given wallpaper to"; - type = str; - default = ""; - }; - - path = mkOption { - description = "The path to the wallpaper"; - type = str; - default = "echo '/home/me/someImage.png'"; # only png supported for now - }; - - color = mkOption { - description = "Background color"; - type = str; - default = "rgba(25, 20, 20, 1.0)"; - }; - - blur_size = mkOption { - description = "Blur size"; - type = int; - default = 8; - }; - - blur_passes = mkOption { - description = "Blur passes"; - type = int; - default = 0; - }; - - noise = mkOption { - description = "Noise applied to blur"; - type = float; - default = 0.0117; - }; - - contrast = mkOption { - description = "Contrast applied to blur"; - type = float; - default = 0.8917; - }; - - brightness = mkOption { - description = "Brightness applied to blur"; - type = float; - default = 0.8172; - }; - - vibrancy = mkOption { - description = "Vibrancy applied to blur"; - type = float; - default = 0.1686; - }; - - vibrancy_darkness = mkOption { - description = "Vibrancy darkness applied to blur"; - type = float; - default = 0.05; - }; + options = { + monitor = mkOption { + description = "The monitor to apply the given wallpaper to"; + type = str; + default = ""; }; + + path = mkOption { + description = "The path to the wallpaper"; + type = str; + default = "echo '/home/me/someImage.png'"; # only png supported for now + }; + + color = mkOption { + description = "Background color"; + type = str; + default = "rgba(25, 20, 20, 1.0)"; + }; + + blur_size = mkOption { + description = "Blur size"; + type = int; + default = 8; + }; + + blur_passes = mkOption { + description = "Blur passes"; + type = int; + default = 0; + }; + + noise = mkOption { + description = "Noise applied to blur"; + type = float; + default = 0.0117; + }; + + contrast = mkOption { + description = "Contrast applied to blur"; + type = float; + default = 0.8917; + }; + + brightness = mkOption { + description = "Brightness applied to blur"; + type = float; + default = 0.8172; + }; + + vibrancy = mkOption { + description = "Vibrancy applied to blur"; + type = float; + default = 0.1686; + }; + + vibrancy_darkness = mkOption { + description = "Vibrancy darkness applied to blur"; + type = float; + default = 0.05; + }; + }; }); default = [ {} @@ -308,6 +307,30 @@ in { type = str; default = "center"; }; + + capslock_color = mkOption { + description = "Color of the input field when Caps Lock is active"; + type = str; + default = "-1"; + }; + + numlock_color = mkOption { + description = "Color of the input field when NumLock is active"; + type = str; + default = "-1"; + }; + + bothlock_color = mkOption { + description = "Color of the input field when both Caps Lock and NumLock are active"; + type = str; + default = "-1"; + }; + + invert_numlock = mkOption { + description = "Whether to change the color when NumLock is not active"; + type = bool; + default = false; + }; } // shadow; }); @@ -437,6 +460,10 @@ in { fail_color = ${input-field.fail_color} fail_text = ${input-field.fail_text} fail_transition = ${toString input-field.fail_transition} + capslock_color = ${input-field.capslock_color} + numlock_color = ${input-field.numlock_color} + bothlock_color = ${input-field.bothlock_color} + invert_numlock = ${boolToString input-field.invert_numlock} position = ${toString input-field.position.x}, ${toString input-field.position.y} halign = ${input-field.halign} diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 9e7d8ab..9e126ad 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -82,6 +82,10 @@ void CConfigManager::init() { m_config.addSpecialConfigValue("input-field", "fail_color", Hyprlang::INT{0xFFCC2222}); m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"$FAIL"}); m_config.addSpecialConfigValue("input-field", "fail_transition", Hyprlang::INT{300}); + m_config.addSpecialConfigValue("input-field", "capslock_color", Hyprlang::INT{-1}); + m_config.addSpecialConfigValue("input-field", "numlock_color", Hyprlang::INT{-1}); + m_config.addSpecialConfigValue("input-field", "bothlock_color", Hyprlang::INT{-1}); + m_config.addSpecialConfigValue("input-field", "invert_numlock", Hyprlang::INT{0}); SHADOWABLE("input-field"); m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); @@ -174,6 +178,10 @@ std::vector CConfigManager::getWidgetConfigs() { {"fail_color", m_config.getSpecialConfigValue("input-field", "fail_color", k.c_str())}, {"fail_text", m_config.getSpecialConfigValue("input-field", "fail_text", k.c_str())}, {"fail_transition", m_config.getSpecialConfigValue("input-field", "fail_transition", k.c_str())}, + {"capslock_color", m_config.getSpecialConfigValue("input-field", "capslock_color", k.c_str())}, + {"numlock_color", m_config.getSpecialConfigValue("input-field", "numlock_color", k.c_str())}, + {"bothlock_color", m_config.getSpecialConfigValue("input-field", "bothlock_color", k.c_str())}, + {"invert_numlock", m_config.getSpecialConfigValue("input-field", "invert_numlock", k.c_str())}, SHADOWABLE("input-field"), } }); diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp index bbc2d66..00e2fb1 100644 --- a/src/core/hyprlock.cpp +++ b/src/core/hyprlock.cpp @@ -755,6 +755,9 @@ void CHyprlock::onKey(uint32_t key, bool down) { return; } + m_bCapsLock = xkb_state_mod_name_is_active(g_pHyprlock->m_pXKBState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED); + m_bNumLock = xkb_state_mod_name_is_active(g_pHyprlock->m_pXKBState, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LOCKED); + if (SYM == XKB_KEY_BackSpace) { if (m_sPasswordState.passBuffer.length() > 0) m_sPasswordState.passBuffer = m_sPasswordState.passBuffer.substr(0, m_sPasswordState.passBuffer.length() - 1); @@ -766,6 +769,10 @@ void CHyprlock::onKey(uint32_t key, bool down) { Debug::log(LOG, "Clearing password buffer"); m_sPasswordState.passBuffer = ""; + } else if (SYM == XKB_KEY_Caps_Lock) { + m_bCapsLock = !m_bCapsLock; + } else if (SYM == XKB_KEY_Num_Lock) { + m_bNumLock = !m_bNumLock; } else { char buf[16] = {0}; int len = xkb_keysym_to_utf8(SYM, buf, 16); diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp index 9841a5c..9030e42 100644 --- a/src/core/hyprlock.hpp +++ b/src/core/hyprlock.hpp @@ -79,6 +79,9 @@ class CHyprlock { bool m_bLocked = false; + bool m_bCapsLock = false; + bool m_bNumLock = false; + // std::chrono::system_clock::time_point m_tGraceEnds; Vector2D m_vLastEnterCoords = {}; diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index 658249f..9502175 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -4,37 +4,47 @@ #include CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map& props) : shadow(this, props, viewport_) { - size = std::any_cast(props.at("size")); - inner = std::any_cast(props.at("inner_color")); - outer = std::any_cast(props.at("outer_color")); - outThick = std::any_cast(props.at("outline_thickness")); - dots.size = std::any_cast(props.at("dots_size")); - dots.spacing = std::any_cast(props.at("dots_spacing")); - dots.center = std::any_cast(props.at("dots_center")); - dots.rounding = std::any_cast(props.at("dots_rounding")); - fadeOnEmpty = std::any_cast(props.at("fade_on_empty")); - fadeTimeoutMs = std::any_cast(props.at("fade_timeout")); - font = std::any_cast(props.at("font_color")); - hiddenInputState.enabled = std::any_cast(props.at("hide_input")); - rounding = std::any_cast(props.at("rounding")); - placeholder.failColor = std::any_cast(props.at("fail_color")); - placeholder.failTransitionMs = std::any_cast(props.at("fail_transition")); - configFailText = std::any_cast(props.at("fail_text")); - checkColor = std::any_cast(props.at("check_color")); - viewport = viewport_; + size = std::any_cast(props.at("size")); + inner = std::any_cast(props.at("inner_color")); + outThick = std::any_cast(props.at("outline_thickness")); + dots.size = std::any_cast(props.at("dots_size")); + dots.spacing = std::any_cast(props.at("dots_spacing")); + dots.center = std::any_cast(props.at("dots_center")); + dots.rounding = std::any_cast(props.at("dots_rounding")); + fadeOnEmpty = std::any_cast(props.at("fade_on_empty")); + fadeTimeoutMs = std::any_cast(props.at("fade_timeout")); + font = std::any_cast(props.at("font_color")); + hiddenInputState.enabled = std::any_cast(props.at("hide_input")); + rounding = std::any_cast(props.at("rounding")); + configFailText = std::any_cast(props.at("fail_text")); + outerColor.transitionMs = std::any_cast(props.at("fail_transition")); + outerColor.main = std::any_cast(props.at("outer_color")); + outerColor.fail = std::any_cast(props.at("fail_color")); + outerColor.check = std::any_cast(props.at("check_color")); + outerColor.both = std::any_cast(props.at("bothlock_color")); + outerColor.caps = std::any_cast(props.at("capslock_color")); + outerColor.num = std::any_cast(props.at("numlock_color")); + outerColor.invertNum = std::any_cast(props.at("invert_numlock")); + viewport = viewport_; - auto POS__ = std::any_cast(props.at("position")); - pos = {POS__.x, POS__.y}; - configPos = pos; - configSize = size; + auto POS__ = std::any_cast(props.at("position")); + pos = {POS__.x, POS__.y}; + configPos = pos; + configSize = size; halign = std::any_cast(props.at("halign")); valign = std::any_cast(props.at("valign")); - pos = posFromHVAlign(viewport, size, pos, halign, valign); - dots.size = std::clamp(dots.size, 0.2f, 0.8f); - dots.spacing = std::clamp(dots.spacing, 0.f, 1.f); - placeholder.failTransitionMs = std::clamp(placeholder.failTransitionMs, 1, 5000); + pos = posFromHVAlign(viewport, size, pos, halign, valign); + dots.size = std::clamp(dots.size, 0.2f, 0.8f); + dots.spacing = std::clamp(dots.spacing, 0.f, 1.f); + outerColor.transitionMs = std::clamp(outerColor.transitionMs, 0, 1000); + + outerColor.both = outerColor.both == -1 ? outerColor.main : outerColor.both; + outerColor.caps = outerColor.caps == -1 ? outerColor.main : outerColor.caps; + outerColor.num = outerColor.num == -1 ? outerColor.main : outerColor.num; + + g_pHyprlock->m_bNumLock = outerColor.invertNum; std::string placeholderText = std::any_cast(props.at("placeholder_text")); if (!placeholderText.empty()) { @@ -198,7 +208,7 @@ bool CPasswordInputField::draw(const SRenderData& data) { float passAlpha = g_pHyprlock->passwordCheckWaiting() ? 0.5 : 1.0; - CColor outerCol = outer; + CColor outerCol = outerColor.main; outerCol.a *= fade.a * data.opacity; CColor innerCol = inner; innerCol.a *= fade.a * data.opacity; @@ -291,7 +301,7 @@ bool CPasswordInputField::draw(const SRenderData& data) { forceReload = true; } - return dots.currentAmount != PASSLEN || fade.animated || outerAnimated || redrawShadow || data.opacity < 1.0 || forceReload; + return dots.currentAmount != PASSLEN || fade.animated || outerColor.animated || redrawShadow || data.opacity < 1.0 || forceReload; } void CPasswordInputField::updateFailTex() { @@ -326,7 +336,7 @@ void CPasswordInputField::updateFailTex() { request.asset = placeholder.failText; request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT; request.props["font_family"] = std::string{"Sans"}; - request.props["color"] = placeholder.failColor; + request.props["color"] = outerColor.fail; request.props["font_size"] = (int)size.y / 4; g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); @@ -364,67 +374,81 @@ void CPasswordInputField::updateOuter() { if (outThick == 0) return; - static auto OUTERCOL = outer, CHANGETO = OUTERCOL; - static auto TIMER = std::chrono::system_clock::now(); - const auto WAITING = g_pHyprlock->passwordCheckWaiting(); - bool emptyFail = placeholder.failID.empty(); + static auto OUTER = outerColor.main, TARGET = OUTER, SOURCE = OUTER; + static auto TIMER = std::chrono::system_clock::now(); - outerAnimated = false; + if (outerColor.animated) { + if (outerColor.stateNum != outerColor.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock || outerColor.stateCaps != g_pHyprlock->m_bCapsLock) + SOURCE = outerColor.main; + } else + SOURCE = outerColor.main; - if (emptyFail) { - CHANGETO = WAITING ? checkColor : OUTERCOL; + outerColor.animated = false; + outerColor.stateNum = outerColor.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock; + outerColor.stateCaps = g_pHyprlock->m_bCapsLock; - if (outer == CHANGETO) - return; + if (placeholder.failID.empty()) { + if (g_pHyprlock->passwordCheckWaiting()) + TARGET = outerColor.check; + else if (outerColor.both != OUTER && outerColor.stateCaps && outerColor.stateNum) + TARGET = outerColor.both; + else if (outerColor.caps != OUTER && outerColor.stateCaps) + TARGET = outerColor.caps; + else if (outerColor.num != OUTER && outerColor.stateNum) + TARGET = outerColor.num; + else + TARGET = OUTER; + } else { + SOURCE = outerColor.check; + TARGET = outerColor.fail; - if (outer == placeholder.failColor || (outer == OUTERCOL && WAITING)) - TIMER = std::chrono::system_clock::now(); - } else if (!emptyFail) { if (fade.animated || fade.a < 1.0) { - emptyFail = true; - CHANGETO = OUTERCOL; + TARGET = OUTER; + SOURCE = outerColor.fail; } - - if (outer == CHANGETO) - TIMER = std::chrono::system_clock::now(); } - const auto MULTI = std::clamp( - std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - TIMER).count() / (double)placeholder.failTransitionMs, 0.001, 0.5); - const auto DELTA = emptyFail ? CHANGETO - (WAITING ? OUTERCOL : placeholder.failColor) : placeholder.failColor - CHANGETO; - const auto TARGET = emptyFail ? CHANGETO : placeholder.failColor; - const auto SOURCE = emptyFail ? (WAITING ? OUTERCOL : placeholder.failColor) : CHANGETO; + if (outerColor.main == TARGET) + return; - if (outer.r != TARGET.r) { - outer.r += DELTA.r * MULTI; - outerAnimated = true; + if (outerColor.main == SOURCE && !fade.animated) + TIMER = std::chrono::system_clock::now(); - if ((SOURCE.r < TARGET.r && outer.r > TARGET.r) || (SOURCE.r > TARGET.r && outer.r < TARGET.r)) - outer.r = TARGET.r; + const auto MULTI = outerColor.transitionMs == 0 ? + 1.0 : + std::clamp(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - TIMER).count() / (double)outerColor.transitionMs, 0.02, 0.5); + const auto DELTA = TARGET - SOURCE; + + if (outerColor.main.r != TARGET.r) { + outerColor.main.r += DELTA.r * MULTI; + outerColor.animated = true; + + if ((SOURCE.r < TARGET.r && outerColor.main.r > TARGET.r) || (SOURCE.r > TARGET.r && outerColor.main.r < TARGET.r)) + outerColor.main.r = TARGET.r; } - if (outer.g != TARGET.g) { - outer.g += DELTA.g * MULTI; - outerAnimated = true; + if (outerColor.main.g != TARGET.g) { + outerColor.main.g += DELTA.g * MULTI; + outerColor.animated = true; - if ((SOURCE.g < TARGET.g && outer.g > TARGET.g) || (SOURCE.g > TARGET.g && outer.g < TARGET.g)) - outer.g = TARGET.g; + if ((SOURCE.g < TARGET.g && outerColor.main.g > TARGET.g) || (SOURCE.g > TARGET.g && outerColor.main.g < TARGET.g)) + outerColor.main.g = TARGET.g; } - if (outer.b != TARGET.b) { - outer.b += DELTA.b * MULTI; - outerAnimated = true; + if (outerColor.main.b != TARGET.b) { + outerColor.main.b += DELTA.b * MULTI; + outerColor.animated = true; - if ((SOURCE.b < TARGET.b && outer.b > TARGET.b) || (SOURCE.b > TARGET.b && outer.b < TARGET.b)) - outer.b = TARGET.b; + if ((SOURCE.b < TARGET.b && outerColor.main.b > TARGET.b) || (SOURCE.b > TARGET.b && outerColor.main.b < TARGET.b)) + outerColor.main.b = TARGET.b; } - if (outer.a != TARGET.a) { - outer.a += DELTA.a * MULTI; - outerAnimated = true; + if (outerColor.main.a != TARGET.a) { + outerColor.main.a += DELTA.a * MULTI; + outerColor.animated = true; - if ((SOURCE.a < TARGET.a && outer.a > TARGET.a) || (SOURCE.a > TARGET.a && outer.a < TARGET.a)) - outer.a = TARGET.a; + if ((SOURCE.a < TARGET.a && outerColor.main.a > TARGET.a) || (SOURCE.a > TARGET.a && outerColor.main.a < TARGET.a)) + outerColor.main.a = TARGET.a; } TIMER = std::chrono::system_clock::now(); diff --git a/src/renderer/widgets/PasswordInputField.hpp b/src/renderer/widgets/PasswordInputField.hpp index 45d636a..f3675af 100644 --- a/src/renderer/widgets/PasswordInputField.hpp +++ b/src/renderer/widgets/PasswordInputField.hpp @@ -26,9 +26,8 @@ class CPasswordInputField : public IWidget { void updateHiddenInputState(); void updateOuter(); - bool firstRender = true; - bool redrawShadow = false; - bool outerAnimated = false; + bool firstRender = true; + bool redrawShadow = false; Vector2D size; Vector2D pos; @@ -40,7 +39,7 @@ class CPasswordInputField : public IWidget { int outThick, rounding; - CColor inner, outer, font, checkColor; + CColor inner, font; struct { float currentAmount = 0; @@ -68,9 +67,7 @@ class CPasswordInputField : public IWidget { std::string failID = ""; SPreloadedAsset* failAsset = nullptr; bool canGetNewFail = true; - CColor failColor; - int failTransitionMs = 0; - std::string failText = ""; + std::string failText = ""; } placeholder; struct { @@ -80,6 +77,20 @@ class CPasswordInputField : public IWidget { bool enabled = false; } hiddenInputState; + struct { + CColor main; + CColor fail; + CColor check; + CColor caps; + CColor num; + CColor both; + int transitionMs = 0; + bool invertNum = false; + bool animated = false; + bool stateNum = false; + bool stateCaps = false; + } outerColor; + bool fadeOnEmpty; uint64_t fadeTimeoutMs;