Skip to content

Commit 27ec516

Browse files
input-field: add auto_submit_after option
Adds a new optional config key auto_submit_after to input-field. When set to N > 0, the input field automatically submits after N characters are typed without requiring Enter, similar to a phone PIN screen. Submission is deferred by one frame so the final dot animates before PAM begins checking. A pending timer is cancelled if the buffer changes before it fires. Fixes: #1012
1 parent 98fb021 commit 27ec516

5 files changed

Lines changed: 61 additions & 0 deletions

File tree

src/config/ConfigManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ void CConfigManager::init() {
319319
m_config.addSpecialConfigValue("input-field", "dots_text_format", Hyprlang::STRING{""});
320320
m_config.addSpecialConfigValue("input-field", "fade_on_empty", Hyprlang::INT{1});
321321
m_config.addSpecialConfigValue("input-field", "fade_timeout", Hyprlang::INT{2000});
322+
m_config.addSpecialConfigValue("input-field", "auto_submit_after", Hyprlang::INT{0});
322323
m_config.addSpecialConfigValue("input-field", "font_color", Hyprlang::INT{0xFF000000});
323324
m_config.addSpecialConfigValue("input-field", "font_family", Hyprlang::STRING{"Sans"});
324325
m_config.addSpecialConfigValue("input-field", "halign", Hyprlang::STRING{"center"});
@@ -501,6 +502,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
501502
{"dots_text_format", m_config.getSpecialConfigValue("input-field", "dots_text_format", k.c_str())},
502503
{"fade_on_empty", m_config.getSpecialConfigValue("input-field", "fade_on_empty", k.c_str())},
503504
{"fade_timeout", m_config.getSpecialConfigValue("input-field", "fade_timeout", k.c_str())},
505+
{"auto_submit_after", m_config.getSpecialConfigValue("input-field", "auto_submit_after", k.c_str())},
504506
{"font_color", m_config.getSpecialConfigValue("input-field", "font_color", k.c_str())},
505507
{"font_family", m_config.getSpecialConfigValue("input-field", "font_family", k.c_str())},
506508
{"halign", m_config.getSpecialConfigValue("input-field", "halign", k.c_str())},

src/core/hyprlock.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,10 @@ size_t CHyprlock::getPasswordBufferLen() {
871871
return m_sPasswordState.passBuffer.length();
872872
}
873873

874+
const std::string& CHyprlock::getPasswordBuffer() {
875+
return m_sPasswordState.passBuffer;
876+
}
877+
874878
size_t CHyprlock::getPasswordBufferDisplayLen() {
875879
// Counts utf-8 codepoints in the buffer. A byte is counted if it does not match 0b10xxxxxx.
876880
return std::count_if(m_sPasswordState.passBuffer.begin(), m_sPasswordState.passBuffer.end(), [](char c) { return (c & 0xc0) != 0x80; });

src/core/hyprlock.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class CHyprlock {
6565

6666
size_t getPasswordBufferLen();
6767
size_t getPasswordBufferDisplayLen();
68+
const std::string& getPasswordBuffer();
6869

6970
SP<CCExtSessionLockManagerV1> getSessionLockMgr();
7071
SP<CCExtSessionLockV1> getSessionLock();

src/renderer/widgets/PasswordInputField.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a
4646
dots.textFormat = std::any_cast<Hyprlang::STRING>(props.at("dots_text_format"));
4747
fadeOnEmpty = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty"));
4848
fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout"));
49+
autoSubmitAfter = std::any_cast<Hyprlang::INT>(props.at("auto_submit_after"));
4950
hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input"));
5051
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
5152
configPlaceholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));
@@ -107,6 +108,12 @@ void CPasswordInputField::reset() {
107108
fade.fadeOutTimer.reset();
108109
}
109110

111+
if (autoSubmitTimer) {
112+
autoSubmitTimer->cancel();
113+
autoSubmitTimer.reset();
114+
pendingAutoSubmitBuffer.clear();
115+
}
116+
110117
if (g_pHyprlock->isTerminating())
111118
return;
112119

@@ -130,6 +137,29 @@ void CPasswordInputField::onFadeOutTimer() {
130137
g_pHyprlock->renderOutput(outputStringPort);
131138
}
132139

140+
static void autoSubmitCallback(AWP<CPasswordInputField> ref) {
141+
if (const auto PP = ref.lock(); PP)
142+
PP->onAutoSubmitTimer();
143+
}
144+
145+
void CPasswordInputField::onAutoSubmitTimer() {
146+
autoSubmitTimer.reset();
147+
148+
if (g_pAuth->checkWaiting()) {
149+
pendingAutoSubmitBuffer.clear();
150+
return;
151+
}
152+
153+
if (g_pHyprlock->getPasswordBuffer() != pendingAutoSubmitBuffer) {
154+
pendingAutoSubmitBuffer.clear();
155+
return;
156+
}
157+
158+
pendingAutoSubmitBuffer.clear();
159+
g_pAuth->submitInput(g_pHyprlock->getPasswordBuffer());
160+
g_pHyprlock->renderAllOutputs();
161+
}
162+
133163
void CPasswordInputField::updateFade() {
134164
if (!fadeOnEmpty) {
135165
fade.a->setValueAndWarp(1.0);
@@ -173,6 +203,24 @@ void CPasswordInputField::updateDots() {
173203
*dots.currentAmount = passwordLength;
174204
}
175205

206+
void CPasswordInputField::updateAutoSubmit() {
207+
if (autoSubmitAfter <= 0)
208+
return;
209+
210+
const auto& passwordBuffer = g_pHyprlock->getPasswordBuffer();
211+
212+
if (autoSubmitTimer && (checkWaiting || passwordLength != (size_t)autoSubmitAfter || passwordBuffer != pendingAutoSubmitBuffer)) {
213+
autoSubmitTimer->cancel();
214+
autoSubmitTimer.reset();
215+
pendingAutoSubmitBuffer.clear();
216+
}
217+
218+
if (passwordLength == (size_t)autoSubmitAfter && !checkWaiting && !autoSubmitTimer) {
219+
pendingAutoSubmitBuffer = passwordBuffer;
220+
autoSubmitTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(1), [REF = m_self](auto, auto) { autoSubmitCallback(REF); }, nullptr);
221+
}
222+
}
223+
176224
bool CPasswordInputField::draw(const SRenderData& data) {
177225
if (firstRender || redrawShadow) {
178226
firstRender = false;
@@ -186,6 +234,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
186234
checkWaiting = g_pAuth->checkWaiting();
187235
displayFail = g_pAuth->m_bDisplayFailText;
188236

237+
updateAutoSubmit();
189238
updateFade();
190239
updateDots();
191240
updateColors();

src/renderer/widgets/PasswordInputField.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ class CPasswordInputField : public IWidget {
3131

3232
void reset();
3333
void onFadeOutTimer();
34+
void onAutoSubmitTimer();
3435

3536
private:
3637
AWP<CPasswordInputField> m_self;
3738

3839
void updateDots();
40+
void updateAutoSubmit();
3941
void updateFade();
4042
void updatePlaceholder();
4143
void updateWidth();
@@ -121,6 +123,9 @@ class CPasswordInputField : public IWidget {
121123

122124
bool fadeOnEmpty;
123125
uint64_t fadeTimeoutMs;
126+
int autoSubmitAfter = 0;
127+
ASP<CTimer> autoSubmitTimer = nullptr;
128+
std::string pendingAutoSubmitBuffer;
124129

125130
CShadowable shadow;
126131
};

0 commit comments

Comments
 (0)