Add keys for entering the current suggestions (#1176)

This adds the new "Stateful" kind of keys, for which the symbol is
dynamically computed from the app's state. This is implemented with a
hook directly in KeyValue.

3 keys are added for accessing the 3 words on the candidates view.
This commit is contained in:
Jules Aguillon
2026-06-30 16:57:27 +02:00
committed by GitHub
parent 44dcf6e753
commit 4ab921f112
4 changed files with 89 additions and 4 deletions
+9
View File
@@ -192,6 +192,15 @@ Value | Meaning
`shareText` | Emit a share Intent for the selected text. **Oddity:** This is in CamelCase.
`hide_self` | Hide the keyboard.
## Suggestions
Value | Meaning
:---- | :------
`complete_first` | Enter the best suggestion (the one in the middle)
`complete_second` | Enter the second best suggestion (the right one)
`complete_third` | Enter the third best suggestion (the left one)
`complete_emoji` | Enter the emoji suggestion
## Unused
These keys are known to do nothing.
+16 -2
View File
@@ -36,14 +36,14 @@ public final class KeyEventHandler
LastAction _last_action = null;
LastAction _next_last_action = null;
public KeyEventHandler(IReceiver recv, Config config)
public KeyEventHandler(IReceiver recv, Suggestions sg)
{
_recv = recv;
Handler handler = recv.getHandler();
_autocap = new Autocapitalisation(handler,
this.new Autocapitalisation_callback());
_mods = Pointers.Modifiers.EMPTY;
_suggestions = new Suggestions(recv, config);
_suggestions = sg;
_typedword = new CurrentlyTypedWord(handler, this);
}
@@ -119,6 +119,7 @@ public final class KeyEventHandler
case Compose_pending: _recv.set_compose_pending(true); break;
case Slider: handle_slider(key.getSlider(), key.getSliderRepeat(), false); break;
case Macro: evaluate_macro(key.getMacro()); break;
case Stateful: handle_stateful(key.getStateful()); break;
}
update_meta_state(old_mods);
_last_action = _next_last_action;
@@ -334,6 +335,19 @@ public final class KeyEventHandler
}
}
void handle_stateful(KeyValue.Stateful st)
{
switch (st)
{
case Complete_first:
case Complete_second:
case Complete_third:
case Complete_emoji:
suggestion_entered(st.toString());
break;
}
}
/** Move the cursor right or left, if possible without sending key events.
Unlike arrow keys, the selection is not removed even if shift is not on.
Falls back to sending arrow keys events if the editor do not support
+45
View File
@@ -105,6 +105,7 @@ public final class KeyValue implements Comparable<KeyValue>
String, // [_payload] is also the string to output, value is unused.
Slider, // [_payload] is a [KeyValue.Slider], value is slider repeatition.
Macro, // [_payload] is a [KeyValue.Macro], value is unused.
Stateful, // [_payload] is a [KeyValue.Stateful], value is the query.
}
private static final int FLAGS_OFFSET = 20;
@@ -239,6 +240,12 @@ public final class KeyValue implements Comparable<KeyValue>
return ((Macro)_payload).keys;
}
/** Defined only when [getKind() == Kind.Stateful]. */
public Stateful getStateful()
{
return (Stateful)_payload;
}
/* Update the char and the symbol. */
public KeyValue withChar(char c)
{
@@ -423,6 +430,11 @@ public final class KeyValue implements Comparable<KeyValue>
id.ordinal(), flags | FLAG_KEY_FONT);
}
private static KeyValue statefulKey(Stateful st)
{
return new KeyValue(st, Kind.Stateful, 0, FLAG_SMALLER_FONT);
}
public static KeyValue makeStringKey(String str)
{
return makeStringKey(str, 0);
@@ -644,6 +656,10 @@ public final class KeyValue implements Comparable<KeyValue>
case "capslock": return eventKey(0xE012, Event.CAPS_LOCK, 0);
case "voice_typing": return eventKey(0xE015, Event.SWITCH_VOICE_TYPING, FLAG_SMALLER_FONT);
case "voice_typing_chooser": return VOICE_TYPING_CHOOSER;
case "complete_first": return statefulKey(Stateful.Complete_first);
case "complete_second": return statefulKey(Stateful.Complete_second);
case "complete_third": return statefulKey(Stateful.Complete_third);
case "complete_emoji": return statefulKey(Stateful.Complete_emoji);
case "hide_self": return eventKey("⊻", Event.HIDE_SELF, FLAG_SMALLER_FONT);
/* Key events */
@@ -914,4 +930,33 @@ public final class KeyValue implements Comparable<KeyValue>
return _symbol.compareTo(snd._symbol);
}
};
/** Stateful keys are keys whose symbol must be constantly computed from the
app's state. */
public static enum Stateful implements Describe
{
Complete_first,
Complete_second,
Complete_third,
Complete_emoji;
@Override
public String toString()
{
if (_handler == null)
return "";
String s = _handler.provide_stateful_key_symbol(this);
return (s == null) ? "" : s;
}
@Override
public String describe() { return name(); }
public static Symbol_provider _handler = null;
public static interface Symbol_provider
{
public String provide_stateful_key_symbol(Stateful q);
}
}
}
+19 -2
View File
@@ -39,6 +39,7 @@ public class Keyboard2 extends InputMethodService
private ViewGroup _keyboard_container_view;
private Keyboard2View _keyboard_layout_view;
private CandidatesView _candidates_view;
private Suggestions _suggestions;
private KeyEventHandler _keyeventhandler;
/** If not 'null', the layout to use instead of [_config.current_layout]. */
private KeyboardData _currentSpecialLayout;
@@ -126,7 +127,10 @@ public class Keyboard2 extends InputMethodService
Config.initGlobalConfig(prefs, getResources(),
_foldStateTracker.isUnfolded(), _dictionaries);
_config = Config.globalConfig();
_keyeventhandler = new KeyEventHandler(this.new Receiver(), _config);
Receiver recvr = this.new Receiver();
_suggestions = new Suggestions(recvr, _config);
_keyeventhandler = new KeyEventHandler(recvr, _suggestions);
KeyValue.Stateful._handler = recvr;
_config.handler = _keyeventhandler;
prefs.registerOnSharedPreferenceChangeListener(this);
Logs.set_debug_logs(getResources().getBoolean(R.bool.debug_logs));
@@ -391,7 +395,8 @@ public class Keyboard2 extends InputMethodService
}
/** Not static */
public class Receiver implements KeyEventHandler.IReceiver
public class Receiver implements KeyEventHandler.IReceiver,
KeyValue.Stateful.Symbol_provider
{
public void handle_event_key(KeyValue.Event ev)
{
@@ -512,6 +517,18 @@ public class Keyboard2 extends InputMethodService
{
_candidates_view.set_candidates(suggestions);
}
public String provide_stateful_key_symbol(KeyValue.Stateful q)
{
switch (q)
{
case Complete_first: return _suggestions.suggestions[0];
case Complete_second: return _suggestions.suggestions[1];
case Complete_third: return _suggestions.suggestions[2];
case Complete_emoji: return _suggestions.emoji_suggestion;
}
return "";
}
}
private IBinder getConnectionToken()