Skip to content

Commit dbf83bd

Browse files
authored
Merge pull request #6 from naokiri/candidate_list_view
Show candidates in list. fix #4
2 parents bb9a65f + f51d1fa commit dbf83bd

8 files changed

+342
-51
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.0)
2-
project(fcitx5-cskk VERSION 0.2.0-dev)
2+
project(fcitx5-cskk VERSION 0.2.0)
33
set(CMAKE_CXX_STANDARD 20)
44

55
find_package(ECM 1.0.0 REQUIRED)

development.org

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ On last resort, hook into imconfig's run im script /usr/share/im-config/data/??_
1111
As a desktop app following the standard, data should better be loaded from subdir for this component only under XDG_DATA_HOME for personal dictionary and XDG_DATA_DIRS for static common dictionary.
1212
Although, emacs + SKK users tend to share dictionary file among ddskk and IME so this might have to be fixed to be configurable in the future.
1313

14+
** Some class name
15+
Putting Fcitx in class name may seem redundant, but it helps distinguish this fcitx5-cskk addon from cskk the library.
16+
Put 'FcitxCskk' as prefix for class names.
17+
1418
* Q
1519
** How config works?
1620
Create Option class.

src/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
set(CSKK_SOURCES
22
cskk.cpp
3-
)
3+
cskkcandidatelist.cpp)
44
add_library(fcitx5-cskk MODULE ${CSKK_SOURCES})
55
target_link_libraries(fcitx5-cskk
66
Fcitx5::Core

src/cskk.cpp

+107-40
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
* SPDX-FileCopyrightText: Copyright (c) 2021 Naoaki Iwakiri
1313
*/
1414
#include "cskk.h"
15+
#include "cskkcandidatelist.h"
16+
#include "log.h"
1517
#include <cstdlib>
18+
#include <cstring>
1619
#include <fcitx-config/iniparser.h>
17-
#include <fcitx-utils/log.h>
1820
#include <fcitx/addonmanager.h>
1921
#include <fcitx/inputpanel.h>
2022
#include <filesystem>
@@ -24,17 +26,20 @@
2426
using std::getenv;
2527
using std::string;
2628

27-
namespace fcitx {
2829
FCITX_DEFINE_LOG_CATEGORY(cskk_log, "cskk");
2930

30-
#define CSKK_DEBUG() FCITX_LOGC(cskk_log, Debug) << "\t**CSKK** "
31-
#define CSKK_WARN() FCITX_LOGC(cskk_log, Warn) << "\t**CSKK** "
31+
namespace fcitx {
3232

3333
/*******************************************************************************
34-
* CskkEngine
34+
* FcitxCskkEngine
3535
******************************************************************************/
36-
const string CskkEngine::config_file_path = "conf/fcitx5-cskk";
37-
CskkEngine::CskkEngine(Instance *instance)
36+
const string FcitxCskkEngine::config_file_path = string{"conf/fcitx5-cskk"};
37+
38+
const uint FcitxCskkEngine::pageStartIdx = 3;
39+
const CandidateLayoutHint FcitxCskkEngine::layoutHint =
40+
CandidateLayoutHint::Horizontal;
41+
42+
FcitxCskkEngine::FcitxCskkEngine(Instance *instance)
3843
: instance_{instance}, factory_([this](InputContext &ic) {
3944
auto newCskkContext = new FcitxCskkContext(this, &ic);
4045
newCskkContext->applyConfig();
@@ -43,39 +48,39 @@ CskkEngine::CskkEngine(Instance *instance)
4348
reloadConfig();
4449
instance_->inputContextManager().registerProperty("cskkcontext", &factory_);
4550
}
46-
CskkEngine::~CskkEngine() = default;
47-
void CskkEngine::keyEvent(const InputMethodEntry &, KeyEvent &keyEvent) {
51+
FcitxCskkEngine::~FcitxCskkEngine() = default;
52+
void FcitxCskkEngine::keyEvent(const InputMethodEntry &, KeyEvent &keyEvent) {
4853
CSKK_DEBUG() << "Engine keyEvent start: " << keyEvent.rawKey();
4954
// delegate to context
5055
auto ic = keyEvent.inputContext();
5156
auto context = ic->propertyFor(&factory_);
5257
context->keyEvent(keyEvent);
5358
CSKK_DEBUG() << "Engine keyEvent end";
5459
}
55-
void CskkEngine::save() {}
56-
void CskkEngine::activate(const InputMethodEntry &, InputContextEvent &) {}
57-
void CskkEngine::deactivate(const InputMethodEntry &entry,
58-
InputContextEvent &event) {
60+
void FcitxCskkEngine::save() {}
61+
void FcitxCskkEngine::activate(const InputMethodEntry &, InputContextEvent &) {}
62+
void FcitxCskkEngine::deactivate(const InputMethodEntry &entry,
63+
InputContextEvent &event) {
5964
FCITX_UNUSED(entry);
6065
reset(entry, event);
6166
}
62-
void CskkEngine::reset(const InputMethodEntry &entry,
63-
InputContextEvent &event) {
67+
void FcitxCskkEngine::reset(const InputMethodEntry &entry,
68+
InputContextEvent &event) {
6469
FCITX_UNUSED(entry);
6570
CSKK_DEBUG() << "Reset";
6671
auto ic = event.inputContext();
6772
auto context = ic->propertyFor(&factory_);
6873
context->reset();
6974
}
70-
void CskkEngine::setConfig(const RawConfig &config) {
75+
void FcitxCskkEngine::setConfig(const RawConfig &config) {
7176
CSKK_DEBUG() << "Cskk setconfig";
7277
config_.load(config, true);
73-
safeSaveAsIni(config_, CskkEngine::config_file_path);
78+
safeSaveAsIni(config_, FcitxCskkEngine::config_file_path);
7479
reloadConfig();
7580
}
76-
void CskkEngine::reloadConfig() {
81+
void FcitxCskkEngine::reloadConfig() {
7782
CSKK_DEBUG() << "Cskkengine reload config";
78-
readAsIni(config_, CskkEngine::config_file_path);
83+
readAsIni(config_, FcitxCskkEngine::config_file_path);
7984

8085
loadDictionary();
8186
if (factory_.registered()) {
@@ -86,7 +91,7 @@ void CskkEngine::reloadConfig() {
8691
});
8792
}
8893
}
89-
void CskkEngine::loadDictionary() {
94+
void FcitxCskkEngine::loadDictionary() {
9095
freeDictionaries();
9196

9297
const std::filesystem::directory_options directoryOptions =
@@ -128,7 +133,7 @@ void CskkEngine::loadDictionary() {
128133
}
129134
}
130135
}
131-
std::string CskkEngine::getXDGDataHome() {
136+
std::string FcitxCskkEngine::getXDGDataHome() {
132137
const char *xdgDataHomeEnv = getenv("XDG_DATA_HOME");
133138
const char *homeEnv = getenv("$HOME");
134139
if (xdgDataHomeEnv) {
@@ -139,7 +144,7 @@ std::string CskkEngine::getXDGDataHome() {
139144
return "";
140145
}
141146

142-
std::vector<std::string> CskkEngine::getXDGDataDirs() {
147+
std::vector<std::string> FcitxCskkEngine::getXDGDataDirs() {
143148
const char *xdgDataDirEnv = getenv("XDG_DATA_DIRS");
144149
string rawDirs;
145150

@@ -164,7 +169,7 @@ std::vector<std::string> CskkEngine::getXDGDataDirs() {
164169
CSKK_DEBUG() << xdgDataDirs;
165170
return xdgDataDirs;
166171
}
167-
void CskkEngine::freeDictionaries() {
172+
void FcitxCskkEngine::freeDictionaries() {
168173
CSKK_DEBUG() << "Cskk free dict";
169174
for (auto dictionary : dictionaries_) {
170175
skk_free_dictionary(dictionary);
@@ -176,57 +181,119 @@ void CskkEngine::freeDictionaries() {
176181
* CskkContext
177182
******************************************************************************/
178183

179-
FcitxCskkContext::FcitxCskkContext(CskkEngine *engine, InputContext *ic)
184+
FcitxCskkContext::FcitxCskkContext(FcitxCskkEngine *engine, InputContext *ic)
180185
: context_(skk_context_new(nullptr, 0)), ic_(ic), engine_(engine) {
181186
CSKK_DEBUG() << "Cskk context new";
182187
skk_context_set_input_mode(context_, *engine_->config().inputMode);
183188
}
184189
FcitxCskkContext::~FcitxCskkContext() = default;
185190
void FcitxCskkContext::keyEvent(KeyEvent &keyEvent) {
186-
// TODO: handleCandidate to utilize fcitx's paged candidate list
191+
auto candidateList = std::dynamic_pointer_cast<FcitxCskkCandidateList>(
192+
ic_->inputPanel().candidateList());
193+
if (candidateList != nullptr && !candidateList->empty()) {
194+
if (handleCandidateSelection(candidateList, keyEvent)) {
195+
updateUI();
196+
return;
197+
}
198+
}
199+
200+
if (skk_context_get_composition_mode(context_) !=
201+
CompositionMode::CompositionSelection) {
202+
CSKK_DEBUG() << "not composition selection. destroy candidate list.";
203+
ic_->inputPanel().setCandidateList(nullptr);
204+
}
187205

188206
uint32_t modifiers =
189207
static_cast<uint32_t>(keyEvent.rawKey().states() & KeyState::SimpleMask);
190-
191208
CskkKeyEvent *cskkKeyEvent = skk_key_event_new_from_fcitx_keyevent(
192209
keyEvent.rawKey().sym(), modifiers, keyEvent.isRelease());
193210

194211
if (skk_context_process_key_event(context_, cskkKeyEvent)) {
195212
CSKK_DEBUG() << "Key processed in context.";
196213
keyEvent.filterAndAccept();
197214
}
215+
198216
if (keyEvent.filtered()) {
199217
updateUI();
200218
}
201219
}
202-
void FcitxCskkContext::commitPreedit() {
203-
auto str = skk_context_get_preedit(context_);
204-
ic_->commitString(str);
205-
skk_free_string(str);
220+
bool FcitxCskkContext::handleCandidateSelection(
221+
const std::shared_ptr<FcitxCskkCandidateList> &candidateList,
222+
KeyEvent &keyEvent) {
223+
if (keyEvent.isRelease()) {
224+
return false;
225+
}
226+
CSKK_DEBUG() << "handleCandidateSelection";
227+
228+
if (keyEvent.key().checkKeyList(std::vector{Key(FcitxKey_Up)})) {
229+
candidateList->prevCandidate();
230+
keyEvent.filterAndAccept();
231+
} else if (keyEvent.key().checkKeyList(
232+
std::vector{Key(FcitxKey_Down), Key(FcitxKey_space)})) {
233+
CSKK_DEBUG() << "space key caught in handle candidate";
234+
235+
candidateList->nextCandidate();
236+
keyEvent.filterAndAccept();
237+
} else if (keyEvent.key().check(Key(FcitxKey_Page_Down))) {
238+
keyEvent.filterAndAccept();
239+
} else if (keyEvent.key().check(Key(FcitxKey_Page_Up))) {
240+
keyEvent.filterAndAccept();
241+
} else if (keyEvent.key().check(Key(FcitxKey_Return))) {
242+
CSKK_DEBUG() << "return key caught in handle candidate";
243+
candidateList->candidate(candidateList->cursorIndex()).select(ic_);
244+
keyEvent.filterAndAccept();
245+
}
246+
247+
return keyEvent.filtered();
248+
}
249+
250+
void FcitxCskkContext::reset() {
251+
skk_context_reset(context_);
252+
updateUI();
206253
}
207-
void FcitxCskkContext::reset() { skk_context_reset(context_); }
208254
void FcitxCskkContext::updateUI() {
209255
auto &inputPanel = ic_->inputPanel();
210256

211257
// Output
212258
if (auto output = skk_context_poll_output(context_)) {
213-
FCITX_DEBUG() << "**** CSKK output " << output;
259+
CSKK_DEBUG() << "output: " << output;
214260
if (strlen(output) > 0) {
215261
ic_->commitString(output);
216262
}
217263
skk_free_string(output);
218264
}
219265

220266
// Preedit
221-
// TODO: candidate in preedit?
222-
inputPanel.reset();
223267
auto preedit = skk_context_get_preedit(context_);
224268
Text preeditText;
225269
// FIXME: Pretty text format someday.
226270
preeditText.append(std::string(preedit));
227-
// FIXME: Narrowing conversion without check
228271
preeditText.setCursor(static_cast<int>(strlen(preedit)));
229272

273+
// CandidateList
274+
int current_idx = skk_context_get_current_canddiate_selection_at(context_);
275+
if (current_idx >= static_cast<int>(FcitxCskkEngine::pageStartIdx)) {
276+
char *current_to_composite = skk_context_get_current_to_composite(context_);
277+
auto candidateList = std::dynamic_pointer_cast<FcitxCskkCandidateList>(
278+
ic_->inputPanel().candidateList());
279+
if (candidateList == nullptr || candidateList->empty() ||
280+
(strcmp(candidateList->to_composite().c_str(), current_to_composite) !=
281+
0)) {
282+
// update whole candidateList only if needed
283+
CSKK_DEBUG() << "Set new candidate list on UI update";
284+
285+
inputPanel.setCandidateList(
286+
std::make_unique<FcitxCskkCandidateList>(engine_, ic_));
287+
} else {
288+
// Sync in case idx has changed out of input panel
289+
candidateList->setCursorIndex(
290+
static_cast<int>(current_idx - FcitxCskkEngine::pageStartIdx));
291+
}
292+
293+
} else {
294+
inputPanel.setCandidateList(nullptr);
295+
}
296+
230297
if (ic_->capabilityFlags().test(CapabilityFlag::Preedit)) {
231298
inputPanel.setClientPreedit(preeditText);
232299
ic_->updatePreedit();
@@ -247,17 +314,17 @@ void FcitxCskkContext::copyTo(InputContextProperty *context) {
247314
}
248315

249316
/*******************************************************************************
250-
* CskkFactory
317+
* FcitxCskkFactory
251318
******************************************************************************/
252319

253-
AddonInstance *CskkFactory::create(AddonManager *manager) {
320+
AddonInstance *FcitxCskkFactory::create(AddonManager *manager) {
254321
{
255-
CSKK_DEBUG() << "**** CSKK CskkFactory Create ****";
322+
CSKK_DEBUG() << "**** CSKK FcitxCskkFactory Create ****";
256323
registerDomain("fcitx5-cskk", FCITX_INSTALL_LOCALEDIR);
257-
auto engine = new CskkEngine(manager->instance());
324+
auto engine = new FcitxCskkEngine(manager->instance());
258325
return engine;
259326
}
260327
}
261328
} // namespace fcitx
262329

263-
FCITX_ADDON_FACTORY(fcitx::CskkFactory);
330+
FCITX_ADDON_FACTORY(fcitx::FcitxCskkFactory);

0 commit comments

Comments
 (0)