input-field: add capslock and numlock options (#182)

* input-field: add capslock and numlock options

* fixups

* Nix/HM module: add {caps,num}lock options

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
This commit is contained in:
bvr-yr 2024-03-13 04:10:42 +03:00 committed by GitHub
parent 5c91621ad2
commit f3a41161ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 219 additions and 139 deletions

View file

@ -90,68 +90,67 @@ in {
backgrounds = mkOption { backgrounds = mkOption {
description = "Background configurations"; description = "Background configurations";
type = listOf (submodule { type = listOf (submodule {
options = options = {
{ monitor = mkOption {
monitor = mkOption { description = "The monitor to apply the given wallpaper to";
description = "The monitor to apply the given wallpaper to"; type = str;
type = str; default = "";
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;
};
}; };
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 = [ default = [
{} {}
@ -308,6 +307,30 @@ in {
type = str; type = str;
default = "center"; 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; // shadow;
}); });
@ -437,6 +460,10 @@ in {
fail_color = ${input-field.fail_color} fail_color = ${input-field.fail_color}
fail_text = ${input-field.fail_text} fail_text = ${input-field.fail_text}
fail_transition = ${toString input-field.fail_transition} 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} position = ${toString input-field.position.x}, ${toString input-field.position.y}
halign = ${input-field.halign} halign = ${input-field.halign}

View file

@ -82,6 +82,10 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("input-field", "fail_color", Hyprlang::INT{0xFFCC2222}); m_config.addSpecialConfigValue("input-field", "fail_color", Hyprlang::INT{0xFFCC2222});
m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"<i>$FAIL</i>"}); m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"<i>$FAIL</i>"});
m_config.addSpecialConfigValue("input-field", "fail_transition", Hyprlang::INT{300}); 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"); SHADOWABLE("input-field");
m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
@ -174,6 +178,10 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"fail_color", m_config.getSpecialConfigValue("input-field", "fail_color", k.c_str())}, {"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_text", m_config.getSpecialConfigValue("input-field", "fail_text", k.c_str())},
{"fail_transition", m_config.getSpecialConfigValue("input-field", "fail_transition", 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"), SHADOWABLE("input-field"),
} }
}); });

View file

@ -755,6 +755,9 @@ void CHyprlock::onKey(uint32_t key, bool down) {
return; 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 (SYM == XKB_KEY_BackSpace) {
if (m_sPasswordState.passBuffer.length() > 0) if (m_sPasswordState.passBuffer.length() > 0)
m_sPasswordState.passBuffer = m_sPasswordState.passBuffer.substr(0, m_sPasswordState.passBuffer.length() - 1); 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"); Debug::log(LOG, "Clearing password buffer");
m_sPasswordState.passBuffer = ""; 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 { } else {
char buf[16] = {0}; char buf[16] = {0};
int len = xkb_keysym_to_utf8(SYM, buf, 16); int len = xkb_keysym_to_utf8(SYM, buf, 16);

View file

@ -79,6 +79,9 @@ class CHyprlock {
bool m_bLocked = false; bool m_bLocked = false;
bool m_bCapsLock = false;
bool m_bNumLock = false;
// //
std::chrono::system_clock::time_point m_tGraceEnds; std::chrono::system_clock::time_point m_tGraceEnds;
Vector2D m_vLastEnterCoords = {}; Vector2D m_vLastEnterCoords = {};

View file

@ -4,37 +4,47 @@
#include <algorithm> #include <algorithm>
CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) { CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) {
size = std::any_cast<Hyprlang::VEC2>(props.at("size")); size = std::any_cast<Hyprlang::VEC2>(props.at("size"));
inner = std::any_cast<Hyprlang::INT>(props.at("inner_color")); inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
outer = std::any_cast<Hyprlang::INT>(props.at("outer_color")); outThick = std::any_cast<Hyprlang::INT>(props.at("outline_thickness"));
outThick = std::any_cast<Hyprlang::INT>(props.at("outline_thickness")); dots.size = std::any_cast<Hyprlang::FLOAT>(props.at("dots_size"));
dots.size = std::any_cast<Hyprlang::FLOAT>(props.at("dots_size")); dots.spacing = std::any_cast<Hyprlang::FLOAT>(props.at("dots_spacing"));
dots.spacing = std::any_cast<Hyprlang::FLOAT>(props.at("dots_spacing")); dots.center = std::any_cast<Hyprlang::INT>(props.at("dots_center"));
dots.center = std::any_cast<Hyprlang::INT>(props.at("dots_center")); dots.rounding = std::any_cast<Hyprlang::INT>(props.at("dots_rounding"));
dots.rounding = std::any_cast<Hyprlang::INT>(props.at("dots_rounding")); fadeOnEmpty = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty"));
fadeOnEmpty = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty")); fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout"));
fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout")); font = std::any_cast<Hyprlang::INT>(props.at("font_color"));
font = std::any_cast<Hyprlang::INT>(props.at("font_color")); hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input"));
hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input")); rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding")); configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text"));
placeholder.failColor = std::any_cast<Hyprlang::INT>(props.at("fail_color")); outerColor.transitionMs = std::any_cast<Hyprlang::INT>(props.at("fail_transition"));
placeholder.failTransitionMs = std::any_cast<Hyprlang::INT>(props.at("fail_transition")); outerColor.main = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text")); outerColor.fail = std::any_cast<Hyprlang::INT>(props.at("fail_color"));
checkColor = std::any_cast<Hyprlang::INT>(props.at("check_color")); outerColor.check = std::any_cast<Hyprlang::INT>(props.at("check_color"));
viewport = viewport_; outerColor.both = std::any_cast<Hyprlang::INT>(props.at("bothlock_color"));
outerColor.caps = std::any_cast<Hyprlang::INT>(props.at("capslock_color"));
outerColor.num = std::any_cast<Hyprlang::INT>(props.at("numlock_color"));
outerColor.invertNum = std::any_cast<Hyprlang::INT>(props.at("invert_numlock"));
viewport = viewport_;
auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position")); auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position"));
pos = {POS__.x, POS__.y}; pos = {POS__.x, POS__.y};
configPos = pos; configPos = pos;
configSize = size; configSize = size;
halign = std::any_cast<Hyprlang::STRING>(props.at("halign")); halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign")); valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
pos = posFromHVAlign(viewport, size, pos, halign, valign); pos = posFromHVAlign(viewport, size, pos, halign, valign);
dots.size = std::clamp(dots.size, 0.2f, 0.8f); dots.size = std::clamp(dots.size, 0.2f, 0.8f);
dots.spacing = std::clamp(dots.spacing, 0.f, 1.f); dots.spacing = std::clamp(dots.spacing, 0.f, 1.f);
placeholder.failTransitionMs = std::clamp(placeholder.failTransitionMs, 1, 5000); 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<Hyprlang::STRING>(props.at("placeholder_text")); std::string placeholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));
if (!placeholderText.empty()) { if (!placeholderText.empty()) {
@ -198,7 +208,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
float passAlpha = g_pHyprlock->passwordCheckWaiting() ? 0.5 : 1.0; float passAlpha = g_pHyprlock->passwordCheckWaiting() ? 0.5 : 1.0;
CColor outerCol = outer; CColor outerCol = outerColor.main;
outerCol.a *= fade.a * data.opacity; outerCol.a *= fade.a * data.opacity;
CColor innerCol = inner; CColor innerCol = inner;
innerCol.a *= fade.a * data.opacity; innerCol.a *= fade.a * data.opacity;
@ -291,7 +301,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
forceReload = true; 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() { void CPasswordInputField::updateFailTex() {
@ -326,7 +336,7 @@ void CPasswordInputField::updateFailTex() {
request.asset = placeholder.failText; request.asset = placeholder.failText;
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT; request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
request.props["font_family"] = std::string{"Sans"}; 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; request.props["font_size"] = (int)size.y / 4;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
@ -364,67 +374,81 @@ void CPasswordInputField::updateOuter() {
if (outThick == 0) if (outThick == 0)
return; return;
static auto OUTERCOL = outer, CHANGETO = OUTERCOL; static auto OUTER = outerColor.main, TARGET = OUTER, SOURCE = OUTER;
static auto TIMER = std::chrono::system_clock::now(); static auto TIMER = std::chrono::system_clock::now();
const auto WAITING = g_pHyprlock->passwordCheckWaiting();
bool emptyFail = placeholder.failID.empty();
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) { outerColor.animated = false;
CHANGETO = WAITING ? checkColor : OUTERCOL; outerColor.stateNum = outerColor.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock;
outerColor.stateCaps = g_pHyprlock->m_bCapsLock;
if (outer == CHANGETO) if (placeholder.failID.empty()) {
return; 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) { if (fade.animated || fade.a < 1.0) {
emptyFail = true; TARGET = OUTER;
CHANGETO = OUTERCOL; SOURCE = outerColor.fail;
} }
if (outer == CHANGETO)
TIMER = std::chrono::system_clock::now();
} }
const auto MULTI = std::clamp( if (outerColor.main == TARGET)
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - TIMER).count() / (double)placeholder.failTransitionMs, 0.001, 0.5); return;
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 (outer.r != TARGET.r) { if (outerColor.main == SOURCE && !fade.animated)
outer.r += DELTA.r * MULTI; TIMER = std::chrono::system_clock::now();
outerAnimated = true;
if ((SOURCE.r < TARGET.r && outer.r > TARGET.r) || (SOURCE.r > TARGET.r && outer.r < TARGET.r)) const auto MULTI = outerColor.transitionMs == 0 ?
outer.r = TARGET.r; 1.0 :
std::clamp(std::chrono::duration_cast<std::chrono::milliseconds>(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) { if (outerColor.main.g != TARGET.g) {
outer.g += DELTA.g * MULTI; outerColor.main.g += DELTA.g * MULTI;
outerAnimated = true; outerColor.animated = true;
if ((SOURCE.g < TARGET.g && outer.g > TARGET.g) || (SOURCE.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))
outer.g = TARGET.g; outerColor.main.g = TARGET.g;
} }
if (outer.b != TARGET.b) { if (outerColor.main.b != TARGET.b) {
outer.b += DELTA.b * MULTI; outerColor.main.b += DELTA.b * MULTI;
outerAnimated = true; outerColor.animated = true;
if ((SOURCE.b < TARGET.b && outer.b > TARGET.b) || (SOURCE.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))
outer.b = TARGET.b; outerColor.main.b = TARGET.b;
} }
if (outer.a != TARGET.a) { if (outerColor.main.a != TARGET.a) {
outer.a += DELTA.a * MULTI; outerColor.main.a += DELTA.a * MULTI;
outerAnimated = true; outerColor.animated = true;
if ((SOURCE.a < TARGET.a && outer.a > TARGET.a) || (SOURCE.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))
outer.a = TARGET.a; outerColor.main.a = TARGET.a;
} }
TIMER = std::chrono::system_clock::now(); TIMER = std::chrono::system_clock::now();

View file

@ -26,9 +26,8 @@ class CPasswordInputField : public IWidget {
void updateHiddenInputState(); void updateHiddenInputState();
void updateOuter(); void updateOuter();
bool firstRender = true; bool firstRender = true;
bool redrawShadow = false; bool redrawShadow = false;
bool outerAnimated = false;
Vector2D size; Vector2D size;
Vector2D pos; Vector2D pos;
@ -40,7 +39,7 @@ class CPasswordInputField : public IWidget {
int outThick, rounding; int outThick, rounding;
CColor inner, outer, font, checkColor; CColor inner, font;
struct { struct {
float currentAmount = 0; float currentAmount = 0;
@ -68,9 +67,7 @@ class CPasswordInputField : public IWidget {
std::string failID = ""; std::string failID = "";
SPreloadedAsset* failAsset = nullptr; SPreloadedAsset* failAsset = nullptr;
bool canGetNewFail = true; bool canGetNewFail = true;
CColor failColor; std::string failText = "";
int failTransitionMs = 0;
std::string failText = "";
} placeholder; } placeholder;
struct { struct {
@ -80,6 +77,20 @@ class CPasswordInputField : public IWidget {
bool enabled = false; bool enabled = false;
} hiddenInputState; } 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; bool fadeOnEmpty;
uint64_t fadeTimeoutMs; uint64_t fadeTimeoutMs;