add eye to toggle visibility

This commit is contained in:
Felix Salcher 2025-06-27 17:29:03 +02:00
parent 4d6b766e3a
commit b1dc5df422
No known key found for this signature in database
4 changed files with 77 additions and 9 deletions

View file

@ -850,6 +850,10 @@ bool CHyprlock::getPasswordShow() {
return m_sPasswordState.show;
}
void CHyprlock::togglePasswordShow() {
m_sPasswordState.show = !m_sPasswordState.show;
}
size_t CHyprlock::getPasswordBufferDisplayLen() {
// Counts utf-8 codepoints in the buffer. A byte is counted if it does not match 0b10xxxxxx.
return std::count_if(m_sPasswordState.passBuffer.begin(), m_sPasswordState.passBuffer.end(), [](char c) { return (c & 0xc0) != 0x80; });

View file

@ -67,6 +67,8 @@ class CHyprlock {
std::string getPasswordBuffer();
bool getPasswordShow();
void togglePasswordShow();
SP<CCExtSessionLockManagerV1> getSessionLockMgr();
SP<CCExtSessionLockV1> getSessionLock();
SP<CCWlCompositor> getCompositor();

View file

@ -103,6 +103,7 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a
// request the inital placeholder asset
updatePlaceholder();
updateEye();
}
void CPasswordInputField::reset() {
@ -224,7 +225,8 @@ void CPasswordInputField::updatePassword() {
assetSize = g_pRenderer->asyncResourceGatherer->getTextAssetSize(request);
offset = (inputFieldBox.h - assetSize.y) / 2.0;
if (assetSize.x <= (inputFieldBox.w - offset * 2)) {
// It can be safely assumed that the eye asset is always available
if (!eye.asset || assetSize.x <= (inputFieldBox.w - offset * 2 - eye.margin - eye.asset->texture.m_vSize.x)) {
break;
}
@ -252,6 +254,22 @@ void CPasswordInputField::renderPasswordUpdate() {
g_pHyprlock->renderOutput(outputStringPort);
}
void CPasswordInputField::updateEye() {
CAsyncResourceGatherer::SPreloadRequest request;
std::string textResourceID = std::format("eye:{}", (uintptr_t)this);
request.id = textResourceID;
request.asset = "👁";
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
request.props["font_family"] = fontFamily;
request.props["color"] = colorConfig.font;
request.props["font_size"] = eye.size;
eye.resourceID = textResourceID;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}
bool CPasswordInputField::draw(const SRenderData& data) {
if (firstRender || redrawShadow) {
firstRender = false;
@ -315,7 +333,10 @@ bool CPasswordInputField::draw(const SRenderData& data) {
const int ROUND = roundingForBox(inputFieldBox, rounding);
g_pRenderer->renderRect(inputFieldBox, innerCol, ROUND);
if (!hiddenInputState.enabled) {
eye.asset = g_pRenderer->asyncResourceGatherer->getAssetByID(eye.resourceID);
if (!hiddenInputState.enabled && eye.asset) {
Vector2D eyeSize = eye.asset->texture.m_vSize;
if (!showPassword) {
const int RECTPASSSIZE = std::nearbyint(inputFieldBox.h * dots.size * 0.5f) * 2.f;
Vector2D passSize{RECTPASSSIZE, RECTPASSSIZE};
@ -335,7 +356,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
const auto CURRDOTS = dots.currentAmount->value();
const double DOTPAD = (inputFieldBox.h - passSize.y) / 2.0;
const double DOTAREAWIDTH = inputFieldBox.w - (DOTPAD * 2);
const double DOTAREAWIDTH = inputFieldBox.w - (DOTPAD * 2) - eye.margin - eyeSize.x;
const int MAXDOTS = std::round(DOTAREAWIDTH * 1.0 / (passSize.x + passSpacing));
const int DOTFLOORED = std::floor(CURRDOTS);
const auto DOTALPHA = fontCol.a;
@ -344,7 +365,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
const double CURRWIDTH = ((passSize.x + passSpacing) * CURRDOTS) - passSpacing;
// Calculate starting x-position to ensure dots stay centered within the input field
double xstart = dots.center ? ((DOTAREAWIDTH - CURRWIDTH) / 2.0) + DOTPAD : DOTPAD;
double xstart = dots.center ? ((DOTAREAWIDTH - CURRWIDTH) / 2.0) + DOTPAD - (eye.margin - eyeSize.x) / 2 : DOTPAD;
if (CURRDOTS > MAXDOTS)
xstart = (inputFieldBox.w + MAXDOTS * (passSize.x + passSpacing) - passSpacing - 2 * CURRWIDTH) / 2.0;
@ -379,20 +400,32 @@ bool CPasswordInputField::draw(const SRenderData& data) {
g_pRenderer->renderRect(box, fontCol, dots.rounding);
fontCol.a = DOTALPHA;
auto eyeSize = eye.asset->texture.m_vSize;
auto eyePosition = inputFieldBox.pos() + Vector2D{inputFieldBox.w - eyeSize.x - DOTPAD, DOTPAD};
box = {eyePosition, eyeSize};
g_pRenderer->renderTexture(box, eye.asset->texture, fontCol.a);
}
} else if (password.content.length() > 0) {
password.asset = g_pRenderer->asyncResourceGatherer->getAssetByID(password.resourceID);
if (password.asset) {
auto size = password.asset->texture.m_vSize;
double offset = (inputFieldBox.h - size.y) / 2.0;
auto passSize = password.asset->texture.m_vSize;
auto eyeSize = eye.asset->texture.m_vSize;
double padding = (inputFieldBox.h - passSize.y) / 2.0;
double xstart = password.center ? inputFieldBox.w / 2.0 - size.x / 2.0 : offset;
double xstart = password.center ? (inputFieldBox.w - passSize.x - eyeSize.x + eye.margin) / 2.0 : padding;
Vector2D dotPosition = inputFieldBox.pos() + Vector2D{xstart, offset};
CBox box{dotPosition, Vector2D(size.x, size.y)};
Vector2D passwordPosition = inputFieldBox.pos() + Vector2D{xstart, padding};
CBox box{passwordPosition, passSize};
g_pRenderer->renderTexture(box, password.asset->texture, fontCol.a);
passwordPosition = inputFieldBox.pos() + Vector2D{inputFieldBox.w - eyeSize.x - padding, padding};
box = {passwordPosition, eyeSize};
g_pRenderer->renderTexture(box, eye.asset->texture, fontCol.a);
} else {
forceReload = true;
}
}
}
@ -580,3 +613,21 @@ CBox CPasswordInputField::getBoundingBoxWl() const {
void CPasswordInputField::onHover(const Vector2D& pos) {
g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT);
}
void CPasswordInputField::onClick(uint32_t button, bool down, const Vector2D& pos) {
if (!password.asset || !eye.asset || !down)
return;
CBox inputFieldBox = getBoundingBoxWl();
auto eyeSize = eye.asset->texture.m_vSize;
auto passwordSize = password.asset->texture.m_vSize;
double offset = (inputFieldBox.h - passwordSize.y) / 2.0;
Vector2D dotPosition = inputFieldBox.pos() + Vector2D{inputFieldBox.w - offset - eye.margin - eyeSize.x, offset};
inputFieldBox = {dotPosition, eyeSize};
if (inputFieldBox.containsPoint(pos)) {
g_pHyprlock->togglePasswordShow();
}
}

View file

@ -24,6 +24,7 @@ class CPasswordInputField : public IWidget {
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
virtual bool draw(const SRenderData& data);
virtual void onHover(const Vector2D& pos);
virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
virtual CBox getBoundingBoxWl() const;
void reset();
@ -35,6 +36,7 @@ class CPasswordInputField : public IWidget {
WP<CPasswordInputField> m_self;
void updatePassword();
void updateEye();
void updateDots();
void updateFade();
void updatePlaceholder();
@ -83,6 +85,15 @@ class CPasswordInputField : public IWidget {
int trim = 0;
} password;
struct {
int size = 16;
std::string resourceID = "";
std::string placement = "right";
SPreloadedAsset* asset = nullptr;
const int margin = 4;
} eye;
struct {
PHLANIMVAR<float> a;
bool appearing = true;