Keep the user's shift; trust the text guess less
- A tapped shift (on or off) survives the field re-reporting its context: it lasts until a character is typed, the hint changes or a field is focused - An input-method reset forgets the guessed text and pending dead key, so double-space can't delete a character the keyboard never saw - Fields that state typing preferences only get capitals if they ask - Trackpad moves in fields without surrounding text make the guess unknown; Backspace removes a whole grapheme cluster from it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,9 @@ static_assert(int(KeyboardController::ShiftMode::Locked) == int(ShiftState::Mode
|
||||
namespace {
|
||||
// Enough history for the text rules; fields may report megabytes.
|
||||
constexpr qsizetype MaxTextBefore = 256;
|
||||
// Stands for text we can't see: neither a sentence start nor a word, so no
|
||||
// rule fires on it. For fields that don't report text, after cursor moves.
|
||||
constexpr QChar UnknownText = u'\uFFFC';
|
||||
// Like a hardware key: no word mode, a faster rate than Backspace.
|
||||
constexpr KeyRepeater::Timing KeysymRepeat{.intervalMs = 50, .wordModeAfterMs = std::numeric_limits<int>::max()};
|
||||
}
|
||||
@@ -168,20 +171,30 @@ void KeyboardController::moveCursor(QPoint steps)
|
||||
repeat(qMax(0, -steps.x()), XKB_KEY_Left);
|
||||
repeat(qMax(0, steps.y()), XKB_KEY_Down);
|
||||
repeat(qMax(0, -steps.y()), XKB_KEY_Up);
|
||||
if (!steps.isNull() && !m_context.hasSurroundingText) {
|
||||
m_textBefore = UnknownText;
|
||||
autoCapitalize();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardController::resetContext(const TextContext &context)
|
||||
{
|
||||
m_deadKeys.cancel();
|
||||
m_textBefore.clear();
|
||||
m_shift.forgetUserChoice();
|
||||
setContext(context);
|
||||
}
|
||||
|
||||
void KeyboardController::setContext(const TextContext &context)
|
||||
{
|
||||
m_context = context;
|
||||
if (context.hasSurroundingText)
|
||||
if (context.hasSurroundingText) {
|
||||
m_textBefore = context.textBeforeCursor.right(MaxTextBefore);
|
||||
} else if (m_context.hasSurroundingText) {
|
||||
// The field stopped reporting (an input-method reset): what we knew is stale.
|
||||
m_deadKeys.cancel();
|
||||
m_textBefore.clear();
|
||||
}
|
||||
m_context = context;
|
||||
autoCapitalize();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user