Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用快捷键切换单选状态组 #555

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 12 additions & 23 deletions src/rime/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <rime/segmentation.h>
#include <rime/segmentor.h>
#include <rime/switcher.h>
#include <rime/switches.h>
#include <rime/ticket.h>
#include <rime/translation.h>
#include <rime/translator.h>
Expand Down Expand Up @@ -385,31 +386,19 @@ void ConcreteEngine::InitializeComponents() {
void ConcreteEngine::InitializeOptions() {
// reset custom switches
Config* config = schema_->config();
if (auto switches = config->GetList("switches")) {
for (size_t i = 0; i < switches->size(); ++i) {
auto item = As<ConfigMap>(switches->GetAt(i));
if (!item)
continue;
auto reset_value = item->GetValue("reset");
if (!reset_value)
continue;
int value = 0;
reset_value->GetInt(&value);
if (auto option_name = item->GetValue("name")) {
// toggle
context_->set_option(option_name->str(), (value != 0));
}
else if (auto options = As<ConfigList>(item->Get("options"))) {
// radio
for (size_t i = 0; i < options->size(); ++i) {
if (auto option_name = options->GetValueAt(i)) {
context_->set_option(option_name->str(),
static_cast<int>(i) == value);
}
}
Switches switches(config);
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<int>(option.option_index) == option.reset_value);
}
}
}
return Switches::kContinue;
});
}

} // namespace rime
6 changes: 3 additions & 3 deletions src/rime/gear/chord_composer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ ProcessResult ChordComposer::ProcessChordingKey(const KeyEvent& key_event) {
if (key_event.ctrl() || key_event.alt()) {
raw_sequence_.clear();
}
if (key_event.ctrl() && !use_control_ ||
key_event.alt() && !use_alt_ ||
key_event.shift() && !use_shift_) {
if ((key_event.ctrl() && !use_control_) ||
(key_event.alt() && !use_alt_) ||
(key_event.shift() && !use_shift_)) {
ClearChord();
return kNoop;
}
Expand Down
63 changes: 59 additions & 4 deletions src/rime/gear/key_binder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <rime/key_table.h>
#include <rime/schema.h>
#include <rime/switcher.h>
#include <rime/switches.h>
#include <rime/gear/key_binder.h>

using namespace std::placeholders;
Expand Down Expand Up @@ -57,29 +58,83 @@ struct KeyBinding {
};

class KeyBindings : public map<KeyEvent,
vector<KeyBinding>> {
vector<KeyBinding>> {
public:
void LoadBindings(const an<ConfigList>& 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) {
Expand Down
148 changes: 148 additions & 0 deletions src/rime/switches.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <rime/config.h>
#include <rime/switches.h>

namespace rime {

Switches::SwitchOption Switches::FindOption(
function<FindResult (SwitchOption option)> callback) {
auto switches = (*config_)["switches"];
if (!switches.IsList())
return {};
for (size_t i = 0; i < switches.size(); ++i) {
auto item = switches[i];
if (!item.IsMap())
continue;
auto the_switch = As<ConfigMap>(*item);
auto reset = item["reset"];
int reset_value = reset.IsValue() ? reset.ToInt() : -1;
auto name = item["name"];
if (name.IsValue()) {
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) {
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) {
return FindOption([&option_name](SwitchOption option) {
return option.option_name == option_name ? kFound : kContinue;
});
}

an<ConfigMap> Switches::ByIndex(size_t switch_index) {
auto switches = (*config_)["switches"];
if (!switches.IsList())
return nullptr;
if (switches.size() <= switch_index)
return nullptr;
auto item = switches[switch_index];
return As<ConfigMap>(*item);
}

Switches::SwitchOption Switches::Cycle(const SwitchOption& current) {
if (auto options = As<ConfigList>(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<ConfigList>(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<ConfigMap> the_switch,
function<FindResult (SwitchOption option)> callback) {
if (auto options = As<ConfigList>(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<ConfigValue> Switches::GetStateLabel(an<ConfigMap> the_switch,
size_t state_index) {
if (!the_switch)
return nullptr;
auto states = As<ConfigList>(the_switch->Get("states"));
if (!states)
return nullptr;
return states->GetValueAt(state_index);
}

an<ConfigValue> Switches::GetStateLabel(const string& option_name, int state) {
auto the_option = OptionByName(option_name);
if (!the_option.found())
return nullptr;
if (the_option.type == kToggleOption) {
size_t state_index = static_cast<size_t>(state);
return GetStateLabel(the_option.the_switch, state_index);
}
if (the_option.type == kRadioGroup) {
// if the query is a deselected option among the radio group, do not
// display its state label; only show the selected option.
return state
? GetStateLabel(the_option.the_switch, the_option.option_index)
: nullptr;
}
return nullptr;
}

} // namespace rime
67 changes: 67 additions & 0 deletions src/rime/switches.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#ifndef RIME_SWITCHES_H_
#define RIME_SWITCHES_H_

#include <rime/common.h>

namespace rime {

class Config;
class ConfigMap;
class ConfigValue;

class Switches {
public:
explicit Switches(Config* config) : config_(config) {}

enum SwitchType {
kToggleOption,
kRadioGroup,
};

struct SwitchOption {
an<ConfigMap> the_switch = nullptr;
SwitchType type = kToggleOption;
string option_name;
// reset state value on initialize. -1 if unspecified.
int reset_value = -1;
// index of the switch configuration.
size_t switch_index = 0;
// the index of the option in the radio group.
size_t option_index = 0;

bool found() const {
return bool(the_switch);
}
};

enum FindResult {
kContinue,
kFound,
};

SwitchOption FindOption(function<FindResult (SwitchOption option)> callback);

SwitchOption OptionByName(const string& option_name);

an<ConfigMap> ByIndex(size_t switch_index);

static SwitchOption Cycle(const SwitchOption& option);

static SwitchOption Reset(const SwitchOption& option);

static SwitchOption FindRadioGroupOption(
an<ConfigMap> the_switch,
function<FindResult (SwitchOption option)> callback);

static an<ConfigValue> GetStateLabel(
an<ConfigMap> the_switch, size_t state_index);

an<ConfigValue> GetStateLabel(const string& option_name, int state);

private:
Config* config_;
};

} // namespace rime

#endif // RIME_SWITCHES_H_
Loading