Skip to content

Commit

Permalink
fix page_down clearing preedit and click committing nothing (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj authored Apr 12, 2024
1 parent 5b539c6 commit a45d99c
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 43 deletions.
16 changes: 14 additions & 2 deletions macosfrontend/macosfrontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ std::string MacosFrontend::keyEvent(ICUUID uuid, const Key &key,
if (!ic) {
return "{}";
}
ic->resetState();
ic->focusIn();
KeyEvent keyEvent(ic, key, isRelease);
ic->isSyncEvent = true;
ic->keyEvent(keyEvent);
ic->isSyncEvent = false;

if (simulateKeyRelease_ && !isRelease && !key.isModifier() &&
keyEvent.accepted()) {
Expand All @@ -69,7 +70,9 @@ std::string MacosFrontend::keyEvent(ICUUID uuid, const Key &key,
auto timeEventPtr = timeEvent.release();
}

return ic->getState(keyEvent.accepted());
auto state = ic->getState(keyEvent.accepted());
ic->resetState();
return state;
}

MacosInputContext *MacosFrontend::findIC(ICUUID uuid) {
Expand Down Expand Up @@ -168,6 +171,15 @@ std::string MacosInputContext::getState(bool accepted) {
return j.dump();
}

void MacosInputContext::commitAndSetPreeditAsync() {
auto state = state_;
resetState();
dispatch_async(dispatch_get_main_queue(), ^void() {
SwiftFrontend::commitAndSetPreedit(client_, state.commit, state.preedit,
state.cursorPos, state.dummyPreedit);
});
}

std::pair<double, double>
MacosInputContext::getCursorCoordinates(bool followCursor) {
double x = 0, y = 0;
Expand Down
9 changes: 7 additions & 2 deletions macosfrontend/macosfrontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,19 @@ class MacosInputContext : public InputContext {

void resetState() {
state_.commit.clear();
state_.preedit.clear();
state_.cursorPos = 0;
// Don't clear preedit as fcitx may not update it when it's not changed.
state_.dummyPreedit = false;
}
void setDummyPreedit(bool dummyPreedit) {
state_.dummyPreedit = dummyPreedit;
}
std::string getState(bool accepted);
// Shows whether we are processing a sync event (mainly key down) that needs
// to return a bool to indicate if it's handled. In this case, commit and
// preedit need to be set in batch synchronously before returning. Otherwise
// set them in batch asynchronously.
bool isSyncEvent = false;
void commitAndSetPreeditAsync();

private:
MacosFrontend *frontend_;
Expand Down
71 changes: 46 additions & 25 deletions macosfrontend/macosfrontend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,58 @@ import InputMethodKit
private var u16pos = 0
private var currentPreedit = ""

public func commit(_ client: Any!, _ string: String) {
if let client = client as? IMKTextInput {
client.insertText(string, replacementRange: NSRange(location: NSNotFound, length: NSNotFound))
}
private func commitString(_ client: IMKTextInput, _ string: String) {
client.insertText(string, replacementRange: NSRange(location: NSNotFound, length: NSNotFound))
}

// Executed in fcitx thread, so before process_key returns, no UI update
// will happen. That means we can't get coordinates in this function.
public func setPreedit(_ client: Any!, _ preedit: String, _ caretPosUtf8: Int) {
if let client = client as? IMKTextInput {
currentPreedit = preedit
// The caretPos argument is specified in UTF-8 bytes.
// Convert it to UTF-16.
var u8pos = 0
u16pos = 0
for ch in preedit {
if u8pos == caretPosUtf8 {
break
}
u8pos += ch.utf8.count
u16pos += 1
private func setPreedit(_ client: IMKTextInput, _ preedit: String, _ caretPosUtf8: Int) {
currentPreedit = preedit
// The caretPos argument is specified in UTF-8 bytes.
// Convert it to UTF-16.
var u8pos = 0
u16pos = 0
for ch in preedit {
if u8pos == caretPosUtf8 {
break
}
client.setMarkedText(
NSMutableAttributedString(string: preedit),
selectionRange: NSRange(location: u16pos, length: 0),
replacementRange: NSRange(location: NSNotFound, length: 0)
)
u8pos += ch.utf8.count
u16pos += 1
}
client.setMarkedText(
NSMutableAttributedString(string: preedit),
selectionRange: NSRange(location: u16pos, length: 0),
replacementRange: NSRange(location: NSNotFound, length: 0)
)
}

public func commitAndSetPreeditImpl(
_ client: IMKTextInput, _ commit: String, _ preedit: String, _ cursorPos: Int,
_ dummyPreedit: Bool
) {
if !commit.isEmpty {
commitString(client, commit)
}
// Without client preedit, Backspace bypasses IM in Terminal, every key
// is both processed by IM and passed to client in iTerm, so we force a
// dummy client preedit here.
if preedit.isEmpty && dummyPreedit {
setPreedit(client, " ", 0)
} else {
setPreedit(client, preedit, cursorPos)
}
}

public func commitAndSetPreedit(
_ clientPtr: UnsafeMutableRawPointer, _ commit: String, _ preedit: String, _ cursorPos: Int,
_ dummyPreedit: Bool
) {
let client: AnyObject = Unmanaged.fromOpaque(clientPtr).takeUnretainedValue()
guard let client = client as? IMKTextInput else {
return
}
commitAndSetPreeditImpl(client, commit, preedit, cursorPos, dummyPreedit)
}

// Must be executed after actual preedit UI update, i.e. not simply setPreedit.
public func getCursorCoordinates(
_ clientPtr: UnsafeMutableRawPointer,
_ followCursor: Bool,
Expand Down
19 changes: 5 additions & 14 deletions src/controller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class FcitxInputController: IMKInputController {
}

func processKey(_ unicode: UInt32, _ modsVal: UInt32, _ code: UInt16, _ isRelease: Bool) -> Bool {
guard let client = client as? IMKTextInput else {
return false
}
let res = String(process_key(uuid, unicode, modsVal, code, isRelease))
do {
if let data = res.data(using: .utf8) {
Expand All @@ -54,24 +57,12 @@ class FcitxInputController: IMKInputController {
let cursorPos = try Int?.decode(json: json["cursorPos"]) ?? -1
let dummyPreedit = (try Int?.decode(json: json["dummyPreedit"]) ?? 0) == 1
let accepted = (try Int?.decode(json: json["accepted"]) ?? 0) == 1
if !commit.isEmpty {
SwiftFrontend.commit(client, commit)
}
// Without client preedit, Backspace bypasses IM in Terminal, every key
// is both processed by IM and passed to client in iTerm, so we force a
// dummy client preedit here.
if preedit.isEmpty && dummyPreedit {
SwiftFrontend.setPreedit(client, " ", 0)
} else {
SwiftFrontend.setPreedit(client, preedit, cursorPos)
}
commitAndSetPreeditImpl(client, commit, preedit, cursorPos, dummyPreedit)
return accepted
} else {
return false
}
} catch {
return false
}
return false
}

// Default behavior is to recognize keyDown only
Expand Down
3 changes: 3 additions & 0 deletions webpanel/webpanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ void WebPanel::update(UserInterfaceComponent component,
macosIC->setDummyPreedit(
(panelShow_ & PanelShowFlag::HasPreedit) |
(panelShow_ & PanelShowFlag::HasCandidates));
if (!macosIC->isSyncEvent) {
macosIC->commitAndSetPreeditAsync();
}
}
showAsync(panelShow_);
break;
Expand Down

0 comments on commit a45d99c

Please sign in to comment.