Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

input-field: new color features #201

Merged
merged 10 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions nix/hm-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,12 @@ in {
type = bool;
default = false;
};

swap_font_color = mkOption {
description = "Whether to swap font color with inner color on some events";
type = bool;
default = false;
};
}
// shadow;
});
Expand Down Expand Up @@ -557,6 +563,7 @@ in {
numlock_color = ${input-field.numlock_color}
bothlock_color = ${input-field.bothlock_color}
invert_numlock = ${boolToString input-field.invert_numlock}
swap_font_color = ${boolToString input-field.swap_font_color}

position = ${toString input-field.position.x}, ${toString input-field.position.y}
halign = ${input-field.halign}
Expand Down
2 changes: 2 additions & 0 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ void CConfigManager::init() {
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});
m_config.addSpecialConfigValue("input-field", "swap_font_color", Hyprlang::INT{0});
SHADOWABLE("input-field");

m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
Expand Down Expand Up @@ -218,6 +219,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"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())},
{"swap_font_color", m_config.getSpecialConfigValue("input-field", "swap_font_color", k.c_str())},
SHADOWABLE("input-field"),
}
});
Expand Down
243 changes: 148 additions & 95 deletions src/renderer/widgets/PasswordInputField.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,27 @@

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"));
inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
outThick = std::any_cast<Hyprlang::INT>(props.at("outline_thickness"));
dots.size = std::any_cast<Hyprlang::FLOAT>(props.at("dots_size"));
dots.spacing = std::any_cast<Hyprlang::FLOAT>(props.at("dots_spacing"));
dots.center = std::any_cast<Hyprlang::INT>(props.at("dots_center"));
dots.rounding = std::any_cast<Hyprlang::INT>(props.at("dots_rounding"));
fadeOnEmpty = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty"));
fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout"));
font = std::any_cast<Hyprlang::INT>(props.at("font_color"));
hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input"));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text"));
outerColor.transitionMs = std::any_cast<Hyprlang::INT>(props.at("fail_transition"));
outerColor.main = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
outerColor.fail = std::any_cast<Hyprlang::INT>(props.at("fail_color"));
outerColor.check = std::any_cast<Hyprlang::INT>(props.at("check_color"));
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"));
col.transitionMs = std::any_cast<Hyprlang::INT>(props.at("fail_transition"));
col.outer = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
col.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
col.font = std::any_cast<Hyprlang::INT>(props.at("font_color"));
col.fail = std::any_cast<Hyprlang::INT>(props.at("fail_color"));
col.check = std::any_cast<Hyprlang::INT>(props.at("check_color"));
col.both = std::any_cast<Hyprlang::INT>(props.at("bothlock_color"));
col.caps = std::any_cast<Hyprlang::INT>(props.at("capslock_color"));
col.num = std::any_cast<Hyprlang::INT>(props.at("numlock_color"));
col.invertNum = std::any_cast<Hyprlang::INT>(props.at("invert_numlock"));
col.swapFont = std::any_cast<Hyprlang::INT>(props.at("swap_font_color"));
viewport = viewport_;

auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position"));
Expand All @@ -35,16 +36,16 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(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);
outerColor.transitionMs = std::clamp(outerColor.transitionMs, 0, 1000);
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);
col.transitionMs = std::clamp(col.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;
col.both = col.both == -1 ? col.outer : col.both;
col.caps = col.caps == -1 ? col.outer : col.caps;
col.num = col.num == -1 ? col.outer : col.num;

g_pHyprlock->m_bNumLock = outerColor.invertNum;
g_pHyprlock->m_bNumLock = col.invertNum;

std::string placeholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));

Expand All @@ -57,7 +58,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
request.asset = placeholderText;
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
request.props["font_family"] = std::string{"Sans"};
request.props["color"] = CColor{1.0 - font.r, 1.0 - font.g, 1.0 - font.b, 0.5};
request.props["color"] = CColor{1.0 - col.font.r, 1.0 - col.font.g, 1.0 - col.font.b, 0.5};
request.props["font_size"] = (int)size.y / 4;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}
Expand Down Expand Up @@ -171,13 +172,17 @@ bool CPasswordInputField::draw(const SRenderData& data) {

bool forceReload = false;

if (passwordLength == 0 && g_pHyprlock->getPasswordFailedAttempts() > failedAttempts)
bvr-yr marked this conversation as resolved.
Show resolved Hide resolved
forceReload = true;

failedAttempts = g_pHyprlock->getPasswordFailedAttempts();
passwordLength = g_pHyprlock->getPasswordBufferDisplayLen();
checkWaiting = g_pHyprlock->passwordCheckWaiting();

updateFade();
updateDots();
updateFailTex();
updateOuter();
updateColors();
updateHiddenInputState();

static auto TIMER = std::chrono::system_clock::now();
Expand Down Expand Up @@ -208,14 +213,12 @@ bool CPasswordInputField::draw(const SRenderData& data) {
shadowData.opacity *= fade.a;
shadow.draw(shadowData);

float passAlpha = checkWaiting ? 0.5 : 1.0;

CColor outerCol = outerColor.main;
CColor outerCol = col.outer;
outerCol.a *= fade.a * data.opacity;
CColor innerCol = inner;
CColor innerCol = col.inner;
innerCol.a *= fade.a * data.opacity;
CColor fontCol = font;
fontCol.a *= fade.a * data.opacity * passAlpha;
CColor fontCol = col.font;
fontCol.a *= fade.a * data.opacity;

if (outThick > 0) {
g_pRenderer->renderRect(outerBox, outerCol, rounding == -1 ? outerBox.h / 2.0 : rounding);
Expand Down Expand Up @@ -304,7 +307,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
forceReload = true;
}

return dots.currentAmount != passwordLength || fade.animated || outerColor.animated || redrawShadow || data.opacity < 1.0 || forceReload;
return dots.currentAmount != passwordLength || fade.animated || col.animated || redrawShadow || data.opacity < 1.0 || forceReload;
}

void CPasswordInputField::updateFailTex() {
Expand All @@ -328,7 +331,7 @@ void CPasswordInputField::updateFailTex() {

placeholder.failText = configFailText;
replaceAllFail(placeholder.failText, "$FAIL", FAIL.value());
replaceAllFail(placeholder.failText, "$ATTEMPTS", std::to_string(g_pHyprlock->getPasswordFailedAttempts()));
replaceAllFail(placeholder.failText, "$ATTEMPTS", std::to_string(failedAttempts));

// query
CAsyncResourceGatherer::SPreloadRequest request;
Expand All @@ -337,7 +340,7 @@ void CPasswordInputField::updateFailTex() {
request.asset = placeholder.failText;
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
request.props["font_family"] = std::string{"Sans"};
request.props["color"] = outerColor.fail;
request.props["color"] = col.fail;
request.props["font_size"] = (int)size.y / 4;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);

Expand Down Expand Up @@ -371,91 +374,141 @@ void CPasswordInputField::updateHiddenInputState() {
hiddenInputState.lastQuadrant = (hiddenInputState.lastQuadrant + rand() % 3 + 1) % 4;
}

void CPasswordInputField::updateOuter() {
if (outThick == 0)
return;
static void changeColor(const CColor& source, const CColor& target, CColor& subject, const double& multi, bool& animated) {

static auto OUTER = outerColor.main, TARGET = OUTER, SOURCE = OUTER;
static auto TIMER = std::chrono::system_clock::now();
const auto DELTA = target - source;

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 (subject.r != target.r) {
subject.r += DELTA.r * multi;
animated = true;

outerColor.animated = false;
outerColor.stateNum = outerColor.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock;
outerColor.stateCaps = g_pHyprlock->m_bCapsLock;
if ((source.r < target.r && subject.r > target.r) || (source.r > target.r && subject.r < target.r))
subject.r = target.r;
}

if (placeholder.failID.empty()) {
if (g_pHyprlock->m_bFadeStarted) {
if (TARGET == outerColor.check)
SOURCE = outerColor.main;
outerColor.transitionMs = 100;
TARGET = OUTER;
} else if (checkWaiting)
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 (subject.g != target.g) {
subject.g += DELTA.g * multi;
animated = true;

if (fade.animated || fade.a < 1.0) {
TARGET = OUTER;
SOURCE = outerColor.fail;
}
if ((source.g < target.g && subject.g > target.g) || (source.g > target.g && subject.g < target.g))
subject.g = target.g;
}

if (outerColor.main == TARGET)
return;

if (outerColor.main == SOURCE && !fade.animated)
TIMER = std::chrono::system_clock::now();
if (subject.b != target.b) {
subject.b += DELTA.b * multi;
animated = true;

const auto MULTI = outerColor.transitionMs == 0 ?
1.0 :
std::clamp(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - TIMER).count() / (double)outerColor.transitionMs, 0.02, 0.5);
const auto DELTA = TARGET - SOURCE;
if ((source.b < target.b && subject.b > target.b) || (source.b > target.b && subject.b < target.b))
subject.b = target.b;
}

if (outerColor.main.r != TARGET.r) {
outerColor.main.r += DELTA.r * MULTI;
outerColor.animated = true;
if (subject.a != target.a) {
subject.a += DELTA.a * multi;
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 ((source.a < target.a && subject.a > target.a) || (source.a > target.a && subject.a < target.a))
subject.a = target.a;
}
}

void CPasswordInputField::updateColors() {
static auto OUTER = col.outer, TARGET = OUTER, SOURCE = OUTER;
static auto INNER = col.inner, ITARGET = INNER, ISOURCE = INNER;
static auto FONT = col.font, FTARGET = FONT, FSOURCE = FONT;

if (outerColor.main.g != TARGET.g) {
outerColor.main.g += DELTA.g * MULTI;
outerColor.animated = true;
const bool BORDERLESS = outThick == 0;

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 (col.animated) {
// some cases when events happen too quick (within transitionMs)
// TODO: find more?
const bool LOCKCHANGED = col.stateNum != (col.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock) || col.stateCaps != g_pHyprlock->m_bCapsLock;
const bool ANIMONCHECK = checkWaiting && (TARGET == (BORDERLESS ? INNER : OUTER) || TARGET == col.fail);

if (LOCKCHANGED || ANIMONCHECK) {
SOURCE = BORDERLESS ? col.inner : col.outer;
// to avoid an edge case when check_color set to the same as outer.
FSOURCE = ANIMONCHECK && OUTER == col.check ? FONT : col.font;
ISOURCE = ANIMONCHECK && OUTER == col.check ? INNER : col.inner;
}
} else {
SOURCE = BORDERLESS ? col.inner : col.outer;
FSOURCE = col.font;
ISOURCE = col.inner;
}

if (outerColor.main.b != TARGET.b) {
outerColor.main.b += DELTA.b * MULTI;
outerColor.animated = true;
col.stateNum = col.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock;
col.stateCaps = g_pHyprlock->m_bCapsLock;

if (placeholder.failID.empty()) {
if (g_pHyprlock->m_bFadeStarted) {
if (TARGET == col.check)
SOURCE = BORDERLESS ? col.inner : col.outer;
col.transitionMs = 100;
TARGET = BORDERLESS ? INNER : OUTER;
} else if (checkWaiting) {
FTARGET = col.swapFont ? INNER : FONT;
const float PASSALPHA = FTARGET.a * 0.5;
FTARGET.a = PASSALPHA;

TARGET = col.check;
ITARGET = col.swapFont ? FONT : INNER;
} else if (col.both != OUTER && col.stateCaps && col.stateNum) {
TARGET = col.both;
FTARGET = col.swapFont && BORDERLESS ? INNER : FONT;
} else if (col.caps != OUTER && col.stateCaps) {
TARGET = col.caps;
FTARGET = col.swapFont && BORDERLESS ? INNER : FONT;
} else if (col.num != OUTER && col.stateNum) {
TARGET = col.num;
FTARGET = col.swapFont && BORDERLESS ? INNER : FONT;
} else {
// if quickly pressed after failure
if (col.animated && TARGET == col.fail)
SOURCE = BORDERLESS ? col.inner : col.outer;

TARGET = BORDERLESS ? INNER : OUTER;
FTARGET = FONT;
ITARGET = INNER;
}
} else {
FSOURCE = col.swapFont ? INNER : FONT;
const float PASSALPHA = FSOURCE.a * 0.5;
FSOURCE.a = PASSALPHA;
FTARGET = FONT;

SOURCE = col.check;
TARGET = col.fail;
ISOURCE = FONT;
ITARGET = FONT;

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 (fade.animated || fade.a < 1.0) {
TARGET = BORDERLESS ? INNER : OUTER;
SOURCE = col.fail;
}
}

if (outerColor.main.a != TARGET.a) {
outerColor.main.a += DELTA.a * MULTI;
outerColor.animated = true;
col.animated = false;

const bool SWAPDONE = !BORDERLESS && col.swapFont ? col.inner == ITARGET : true;

if ((SOURCE.a < TARGET.a && outerColor.main.a > TARGET.a) || (SOURCE.a > TARGET.a && outerColor.main.a < TARGET.a))
outerColor.main.a = TARGET.a;
if ((BORDERLESS ? col.inner : col.outer) == TARGET && col.font == FTARGET && SWAPDONE) {
col.shouldStart = true;
return;
}

TIMER = std::chrono::system_clock::now();
if (col.shouldStart) {
col.lastFrame = std::chrono::system_clock::now();
col.shouldStart = false;
}

const auto MULTI = col.transitionMs == 0 ?
1.0 :
std::clamp(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - col.lastFrame).count() / (double)col.transitionMs, 0.016, 0.5);

changeColor(SOURCE, TARGET, (BORDERLESS ? col.inner : col.outer), MULTI, col.animated);
changeColor(FSOURCE, FTARGET, col.font, MULTI, col.animated);
if (col.swapFont && !BORDERLESS)
changeColor(ISOURCE, ITARGET, col.inner, MULTI, col.animated);

col.lastFrame = std::chrono::system_clock::now();
}
Loading
Loading