diff --git a/src/rime/engine.cc b/src/rime/engine.cc index 95a9f56865..3e59da46dc 100644 --- a/src/rime/engine.cc +++ b/src/rime/engine.cc @@ -387,17 +387,17 @@ void ConcreteEngine::InitializeOptions() { // reset custom switches Config* config = schema_->config(); Switches switches(config); - switches.ForEachOption([this](Switches::SwitchOption option) { - if (option.reset_value < 0) // unspecified - return; - if (option.type == Switches::kToggleOption) { - context_->set_option(option.option_name, (option.reset_value != 0)); - } - else if (option.type == Switches::kRadioGroup) { - context_->set_option( - option.option_name, - static_cast(option.option_index) == option.reset_value); + switches.FindOption([this](Switches::SwitchOption option) { + if (option.reset_value >= 0) { + if (option.type == Switches::kToggleOption) { + context_->set_option(option.option_name, (option.reset_value != 0)); + } else if (option.type == Switches::kRadioGroup) { + context_->set_option( + option.option_name, + static_cast(option.option_index) == option.reset_value); + } } + return Switches::kContinue; }); } diff --git a/src/rime/gear/key_binder.cc b/src/rime/gear/key_binder.cc index b7c37d109c..93ebc18fa4 100644 --- a/src/rime/gear/key_binder.cc +++ b/src/rime/gear/key_binder.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include using namespace std::placeholders; @@ -57,29 +58,83 @@ struct KeyBinding { }; class KeyBindings : public map> { + vector> { public: void LoadBindings(const an& bindings); void Bind(const KeyEvent& key, const KeyBinding& binding); }; +static void radio_select_option(Context* ctx, + const Switches::SwitchOption& the_option) { + Switches::FindRadioGroupOption( + the_option.the_switch, + [ctx, &the_option](Switches::SwitchOption option) { + bool value = (option.option_index == the_option.option_index); + if (ctx->get_option(option.option_name) != value) { + ctx->set_option(option.option_name, value); + } + return Switches::kContinue; + }); +} + static void toggle_option(Engine* engine, const string& option) { if (!engine) return; Context* ctx = engine->context(); - ctx->set_option(option, !ctx->get_option(option)); + Switches switches(engine->schema()->config()); + auto the_option = switches.OptionByName(option); + if (the_option.found() && the_option.type == Switches::kRadioGroup) { + auto selected_option = switches.FindRadioGroupOption( + the_option.the_switch, + [ctx](Switches::SwitchOption option) { + return ctx->get_option(option.option_name) + ? Switches::kFound + : Switches::kContinue; + }); + if (!selected_option.found()) { + // invalid state: none is selected. select the given option. + radio_select_option(ctx, the_option); + return; + } + // cycle through the ratio group and select the next option. + auto next_option = Switches::Cycle(selected_option); + if (next_option.found()) { + radio_select_option(ctx, next_option); + } + } else { // toggle + ctx->set_option(option, !ctx->get_option(option)); + } } + static void set_option(Engine* engine, const string& option) { if (!engine) return; Context* ctx = engine->context(); - ctx->set_option(option, 1); + Switches switches(engine->schema()->config()); + auto the_option = switches.OptionByName(option); + if (the_option.found() && the_option.type == Switches::kRadioGroup) { + radio_select_option(ctx, the_option); + } else { + ctx->set_option(option, 1); + } } + static void unset_option(Engine* engine, const string& option) { if (!engine) return; Context* ctx = engine->context(); - ctx->set_option(option, 0); + Switches switches(engine->schema()->config()); + auto the_option = switches.OptionByName(option); + if (the_option.found() && the_option.type == Switches::kRadioGroup) { + if (ctx->get_option(option)) { + auto default_option = Switches::Reset(the_option); + if (default_option.found()) { + radio_select_option(ctx, default_option); + } + } + } else { + ctx->set_option(option, 0); + } } static void select_schema(Engine* engine, const string& schema) { diff --git a/src/rime/switches.cc b/src/rime/switches.cc index b2dfa17d55..b023c81050 100644 --- a/src/rime/switches.cc +++ b/src/rime/switches.cc @@ -3,51 +3,54 @@ namespace rime { -void Switches::ForEachOption(function callback) { +Switches::SwitchOption Switches::FindOption( + function callback) { auto switches = (*config_)["switches"]; if (!switches.IsList()) - return; + return {}; for (size_t i = 0; i < switches.size(); ++i) { auto item = switches[i]; if (!item.IsMap()) continue; + auto the_switch = As(*item); auto reset = item["reset"]; int reset_value = reset.IsValue() ? reset.ToInt() : -1; auto name = item["name"]; if (name.IsValue()) { - callback( - SwitchOption{ - As(*item), - kToggleOption, - name.ToString(), - reset_value, - i - }); + SwitchOption option{ + the_switch, + kToggleOption, + name.ToString(), + reset_value, + i, + }; + if (callback(option) == kFound) + return option; continue; } auto options = item["options"]; if (options.IsList()) { for (size_t j = 0; j < options.size(); ++j) { - callback( - SwitchOption{ - As(*item), - kRadioGroup, - options[j].ToString(), - reset_value, - i, - j - }); + SwitchOption option{ + the_switch, + kRadioGroup, + options[j].ToString(), + reset_value, + i, + j, + }; + if (callback(option) == kFound) + return option; } } } + return {}; } Switches::SwitchOption Switches::OptionByName(const string& option_name) { - ForEachOption([&option_name](SwitchOption option) { - if (option.option_name == option_name) - return option; + return FindOption([&option_name](SwitchOption option) { + return option.option_name == option_name ? kFound : kContinue; }); - return {}; } an Switches::ByIndex(size_t switch_index) { @@ -60,6 +63,60 @@ an Switches::ByIndex(size_t switch_index) { return As(*item); } +Switches::SwitchOption Switches::Cycle(const SwitchOption& current) { + if (auto options = As(current.the_switch->Get("options"))) { + size_t next_option_index = (current.option_index + 1) % options->size(); + if (next_option_index != current.option_index) { + return { + current.the_switch, + current.type, + options->GetValueAt(next_option_index)->str(), + current.reset_value, + current.switch_index, + next_option_index, + }; + } + } + return {}; +} + +Switches::SwitchOption Switches::Reset(const SwitchOption& current) { + size_t default_state = (current.reset_value >= 0) ? current.reset_value : 0; + if (auto options = As(current.the_switch->Get("options"))) { + if (default_state >= options->size() || + default_state == current.option_index) + return {}; + return { + current.the_switch, + current.type, + options->GetValueAt(default_state)->str(), + current.reset_value, + current.switch_index, + default_state, + }; + } +} + +Switches::SwitchOption Switches::FindRadioGroupOption( + an the_switch, + function callback) { + if (auto options = As(the_switch->Get("options"))) { + for (size_t j = 0; j < options->size(); ++j) { + SwitchOption option{ + the_switch, + kRadioGroup, + options->GetValueAt(j)->str(), + 0, // unknown + 0, // unknown + j, + }; + if (callback(option) == kFound) + return option; + } + } + return {}; +} + an Switches::GetStateLabel(an the_switch, size_t state_index) { if (!the_switch) diff --git a/src/rime/switches.h b/src/rime/switches.h index 63a5cbb2d7..a17dd3e74e 100644 --- a/src/rime/switches.h +++ b/src/rime/switches.h @@ -34,14 +34,28 @@ class Switches { } }; - void ForEachOption(function callback); + enum FindResult { + kContinue, + kFound, + }; + + SwitchOption FindOption(function callback); SwitchOption OptionByName(const string& option_name); an ByIndex(size_t switch_index); + static SwitchOption Cycle(const SwitchOption& option); + + static SwitchOption Reset(const SwitchOption& option); + + static SwitchOption FindRadioGroupOption( + an the_switch, + function callback); + static an GetStateLabel( an the_switch, size_t state_index); + an GetStateLabel(const string& option_name, int state); private: