Skip to content

Commit

Permalink
feat(navigator,selector): vertical navigation
Browse files Browse the repository at this point in the history
refactor `KeyBindingProcessor`:
make `KeyBindingProcessor::Handler` return bool; return false to fall
back to the next processor;
include mutiple `Keymap`s for defining key bindings for each
`(Horizontal, Vertical) x (Stacked, Linear)` combination.

closes #543
  • Loading branch information
lotem committed Jan 24, 2023
1 parent 946e852 commit d79f6b3
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 200 deletions.
76 changes: 45 additions & 31 deletions src/rime/gear/editor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,49 +88,57 @@ void Editor::LoadConfig() {
}
}

void Editor::Confirm(Context* ctx) {
bool Editor::Confirm(Context* ctx) {
ctx->ConfirmCurrentSelection() || ctx->Commit();
return true;
}

void Editor::ToggleSelection(Context* ctx) {
bool Editor::ToggleSelection(Context* ctx) {
ctx->ReopenPreviousSegment() ||
ctx->ConfirmCurrentSelection();
return true;
}

void Editor::CommitComment(Context* ctx) {
bool Editor::CommitComment(Context* ctx) {
if (auto cand = ctx->GetSelectedCandidate()) {
if (!cand->comment().empty()) {
engine_->sink()(cand->comment());
ctx->Clear();
}
}
return true;
}

void Editor::CommitScriptText(Context* ctx) {
bool Editor::CommitScriptText(Context* ctx) {
engine_->sink()(ctx->GetScriptText());
ctx->Clear();
return true;
}

void Editor::CommitRawInput(Context* ctx) {
bool Editor::CommitRawInput(Context* ctx) {
ctx->ClearNonConfirmedComposition();
ctx->Commit();
return true;
}

void Editor::CommitComposition(Context* ctx) {
bool Editor::CommitComposition(Context* ctx) {
if (!ctx->ConfirmCurrentSelection() || !ctx->HasMenu())
ctx->Commit();
return true;
}

void Editor::RevertLastEdit(Context* ctx) {
bool Editor::RevertLastEdit(Context* ctx) {
// different behavior in regard to previous operation type
ctx->ReopenPreviousSelection() ||
(ctx->PopInput() && ctx->ReopenPreviousSegment());
return true;
}

void Editor::BackToPreviousInput(Context* ctx) {
bool Editor::BackToPreviousInput(Context* ctx) {
ctx->ReopenPreviousSegment() ||
ctx->ReopenPreviousSelection() ||
ctx->PopInput();
return true;
}

static bool pop_input_by_syllable(Context* ctx) {
Expand All @@ -149,23 +157,27 @@ static bool pop_input_by_syllable(Context* ctx) {
return false;
}

void Editor::BackToPreviousSyllable(Context* ctx) {
bool Editor::BackToPreviousSyllable(Context* ctx) {
ctx->ReopenPreviousSelection() ||
((pop_input_by_syllable(ctx) || ctx->PopInput()) &&
ctx->ReopenPreviousSegment());
return true;
}

void Editor::DeleteCandidate(Context* ctx) {
bool Editor::DeleteCandidate(Context* ctx) {
ctx->DeleteCurrentSelection();
return true;
}

void Editor::DeleteChar(Context* ctx) {
bool Editor::DeleteChar(Context* ctx) {
ctx->DeleteInput();
return true;
}

void Editor::CancelComposition(Context* ctx) {
bool Editor::CancelComposition(Context* ctx) {
if (!ctx->ClearPreviousSegment())
ctx->Clear();
return true;
}

ProcessResult Editor::DirectCommit(Context* ctx, int ch) {
Expand All @@ -180,30 +192,32 @@ ProcessResult Editor::AddToInput(Context* ctx, int ch) {
}

FluidEditor::FluidEditor(const Ticket& ticket) : Editor(ticket, false) {
Bind({XK_space, 0}, &Editor::Confirm);
Bind({XK_BackSpace, 0}, &Editor::BackToPreviousInput); //
Bind({XK_BackSpace, kControlMask}, &Editor::BackToPreviousSyllable);
Bind({XK_Return, 0}, &Editor::CommitComposition); //
Bind({XK_Return, kControlMask}, &Editor::CommitRawInput); //
Bind({XK_Return, kShiftMask}, &Editor::CommitScriptText); //
Bind({XK_Return, kControlMask | kShiftMask}, &Editor::CommitComment);
Bind({XK_Delete, 0}, &Editor::DeleteChar);
Bind({XK_Delete, kControlMask}, &Editor::DeleteCandidate);
Bind({XK_Escape, 0}, &Editor::CancelComposition);
auto& keymap = get_keymap();
keymap.Bind({XK_space, 0}, &Editor::Confirm);
keymap.Bind({XK_BackSpace, 0}, &Editor::BackToPreviousInput); //
keymap.Bind({XK_BackSpace, kControlMask}, &Editor::BackToPreviousSyllable);
keymap.Bind({XK_Return, 0}, &Editor::CommitComposition); //
keymap.Bind({XK_Return, kControlMask}, &Editor::CommitRawInput); //
keymap.Bind({XK_Return, kShiftMask}, &Editor::CommitScriptText); //
keymap.Bind({XK_Return, kControlMask | kShiftMask}, &Editor::CommitComment);
keymap.Bind({XK_Delete, 0}, &Editor::DeleteChar);
keymap.Bind({XK_Delete, kControlMask}, &Editor::DeleteCandidate);
keymap.Bind({XK_Escape, 0}, &Editor::CancelComposition);
char_handler_ = &Editor::AddToInput; //
LoadConfig();
}

ExpressEditor::ExpressEditor(const Ticket& ticket) : Editor(ticket, true) {
Bind({XK_space, 0}, &Editor::Confirm);
Bind({XK_BackSpace, 0}, &Editor::RevertLastEdit); //
Bind({XK_BackSpace, kControlMask}, &Editor::BackToPreviousSyllable);
Bind({XK_Return, 0}, &Editor::CommitRawInput); //
Bind({XK_Return, kControlMask}, &Editor::CommitScriptText); //
Bind({XK_Return, kControlMask | kShiftMask}, &Editor::CommitComment);
Bind({XK_Delete, 0}, &Editor::DeleteChar);
Bind({XK_Delete, kControlMask}, &Editor::DeleteCandidate);
Bind({XK_Escape, 0}, &Editor::CancelComposition);
auto& keymap = get_keymap();
keymap.Bind({XK_space, 0}, &Editor::Confirm);
keymap.Bind({XK_BackSpace, 0}, &Editor::RevertLastEdit); //
keymap.Bind({XK_BackSpace, kControlMask}, &Editor::BackToPreviousSyllable);
keymap.Bind({XK_Return, 0}, &Editor::CommitRawInput); //
keymap.Bind({XK_Return, kControlMask}, &Editor::CommitScriptText); //
keymap.Bind({XK_Return, kControlMask | kShiftMask}, &Editor::CommitComment);
keymap.Bind({XK_Delete, 0}, &Editor::DeleteChar);
keymap.Bind({XK_Delete, kControlMask}, &Editor::DeleteCandidate);
keymap.Bind({XK_Escape, 0}, &Editor::CancelComposition);
char_handler_ = &Editor::DirectCommit; //
LoadConfig();
}
Expand Down
33 changes: 22 additions & 11 deletions src/rime/gear/key_binding_processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,41 @@

namespace rime {

template <class T>
template <class T, int N = 1>
class KeyBindingProcessor {
public:
typedef void Handler(Context* ctx);
using HandlerPtr = void (T::*)(Context* ctx);
typedef bool Handler(Context* ctx);
using HandlerPtr = bool (T::*)(Context* ctx);

struct ActionDef {
const char* name;
HandlerPtr action;
};

static const ActionDef kActionNoop;

KeyBindingProcessor(ActionDef* action_definitions)
explicit KeyBindingProcessor(ActionDef* action_definitions)
: action_definitions_(action_definitions) {}
ProcessResult ProcessKeyEvent(const KeyEvent& key_event, Context* ctx);
bool Accept(const KeyEvent& key_event, Context* ctx);
void Bind(KeyEvent key_event, HandlerPtr action);
void LoadConfig(Config* config, const string& section);

ProcessResult ProcessKeyEvent(const KeyEvent& key_event,
Context* ctx,
int keymap_selector = 0);
void LoadConfig(Config* config,
const string& section,
int kemap_selector = 0);

protected:
struct Keymap : map<KeyEvent, HandlerPtr> {
void Bind(KeyEvent key_event, HandlerPtr action);
};

Keymap& get_keymap(int keymap_selector = 0);

bool Accept(const KeyEvent& key_event, Context* ctx, Keymap& keymap);

private:
ActionDef* action_definitions_;

using KeyBindingMap = map<KeyEvent, HandlerPtr>;
KeyBindingMap key_bindings_;
Keymap keymaps_[N];
};

} // namespace rime
Expand Down
61 changes: 38 additions & 23 deletions src/rime/gear/key_binding_processor_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@

namespace rime {

template <class T>
const typename KeyBindingProcessor<T>::ActionDef
KeyBindingProcessor<T>::kActionNoop = { "noop", nullptr };
template <class T, int N>
const typename KeyBindingProcessor<T, N>::ActionDef
KeyBindingProcessor<T, N>::kActionNoop = { "noop", nullptr };

template <class T>
ProcessResult KeyBindingProcessor<T>::ProcessKeyEvent(
const KeyEvent& key_event, Context* ctx) {
template <class T, int N>
ProcessResult KeyBindingProcessor<T, N>::ProcessKeyEvent(
const KeyEvent& key_event, Context* ctx, int keymap_selector) {
auto& keymap = get_keymap(keymap_selector);
// exact match
if (Accept(key_event, ctx)) {
if (Accept(key_event, ctx, keymap)) {
return kAccepted;
}
// fallback: compatible modifiers
Expand All @@ -25,45 +26,59 @@ ProcessResult KeyBindingProcessor<T>::ProcessKeyEvent(
key_event.keycode(),
(key_event.modifier() & ~kShiftMask) | kControlMask
};
if (Accept(shift_as_ctrl, ctx)) {
if (Accept(shift_as_ctrl, ctx, keymap)) {
return kAccepted;
}
KeyEvent ignore_shift{
key_event.keycode(),
key_event.modifier() & ~kShiftMask
};
if (Accept(ignore_shift, ctx)) {
if (Accept(ignore_shift, ctx, keymap)) {
return kAccepted;
}
}
// not handled
return kNoop;
}

template <class T>
bool KeyBindingProcessor<T>::Accept(const KeyEvent& key_event, Context* ctx) {
auto binding = key_bindings_.find(key_event);
if (binding != key_bindings_.end()) {
template <class T, int N>
typename KeyBindingProcessor<T, N>::Keymap&
KeyBindingProcessor<T, N>::get_keymap(int keymap_selector) {
DCHECK_LT(keymap_selector, N);
return keymaps_[keymap_selector];
}

template <class T, int N>
bool KeyBindingProcessor<T, N>::Accept(const KeyEvent& key_event,
Context* ctx,
Keymap& keymap) {
auto binding = keymap.find(key_event);
if (binding != keymap.end()) {
auto action = binding->second;
RIME_THIS_CALL_AS(T, action)(ctx);
DLOG(INFO) << "action key accepted: " << key_event.repr();
return true;
if (RIME_THIS_CALL_AS(T, action)(ctx)) {
DLOG(INFO) << "action key accepted: " << key_event.repr();
return true;
}
}
return false;
}

template <class T>
void KeyBindingProcessor<T>::Bind(KeyEvent key_event, HandlerPtr action) {
template <class T, int N>
void KeyBindingProcessor<T, N>::Keymap::Bind(KeyEvent key_event,
HandlerPtr action) {
if (action) {
key_bindings_[key_event] = action;
(*this)[key_event] = action;
}
else {
key_bindings_.erase(key_event);
this->erase(key_event);
}
}

template <class T>
void KeyBindingProcessor<T>::LoadConfig(Config* config, const string& section) {
template <class T, int N>
void KeyBindingProcessor<T, N>::LoadConfig(Config* config,
const string& section,
int keymap_selector) {
auto& keymap = get_keymap(keymap_selector);
if (auto bindings = config->GetMap(section + "/bindings")) {
for (auto it = bindings->begin(); it != bindings->end(); ++it) {
auto value = As<ConfigValue>(it->second);
Expand All @@ -83,7 +98,7 @@ void KeyBindingProcessor<T>::LoadConfig(Config* config, const string& section) {
LOG(WARNING) << "[" << section << "] invalid key: " << it->first;
continue;
}
Bind(ke, p->action);
keymap.Bind(ke, p->action);
}
}
}
Expand Down
Loading

0 comments on commit d79f6b3

Please sign in to comment.