14 #include "fcitx-utils/environ.h" 16 #include "focusgroup.h" 17 #include "inputcontext_p.h" 18 #include "inputcontextmanager.h" 27 bool shouldDisablePreeditByDefault(
const std::string &program) {
28 static const std::vector<std::regex> matchers = []() {
29 std::vector<std::regex> matchers;
30 auto apps = getEnvironment(
"FCITX_NO_PREEDIT_APPS");
32 apps = NO_PREEDIT_APPS;
35 for (
const auto &matcherString : matcherStrings) {
37 matchers.emplace_back(matcherString, std::regex::icase |
38 std::regex::extended |
46 return std::any_of(matchers.begin(), matchers.end(),
47 [&program](
const std::regex ®ex) {
48 return std::regex_match(program, regex);
53 InputContextPrivate::InputContextPrivate(InputContext *q,
54 InputContextManager &manager,
55 const std::string &program)
56 : QPtrHolder(q), manager_(manager), group_(nullptr), inputPanel_(q),
57 statusArea_(q), program_(program),
58 isPreeditEnabled_(manager.isPreeditEnabledByDefault() &&
59 !shouldDisablePreeditByDefault(program)) {}
61 #define RETURN_IF_HAS_NO_FOCUS(...) \ 68 InputContext::InputContext(InputContextManager &manager,
69 const std::string &program)
70 : d_ptr(
std::make_unique<InputContextPrivate>(this, manager, program)) {
71 manager.registerInputContext(*
this);
74 InputContext::~InputContext() { assert(d_ptr->destroyed_); }
85 assert(!d->destroyed_);
87 d->group_->removeInputContext(
this);
90 d->manager_.unregisterInputContext(*
this);
106 return d->group_ ? d->group_->display() :
"";
111 return d->cursorRect_;
121 auto *factory = d->manager_.factoryForName(name);
125 return d->manager_.property(*
this, factory);
131 return d->manager_.property(*
this, factory);
136 auto *factory = d->manager_.factoryForName(name);
140 updateProperty(factory);
145 auto *
property = d->manager_.property(*
this, factory);
146 if (!property->needCopy()) {
149 d->manager_.propagateProperty(*
this, factory);
152 bool InputContext::isVirtualKeyboardVisible()
const {
154 if (
auto *instance = d->manager_.instance()) {
155 return instance->userInterfaceManager().isVirtualKeyboardVisible();
160 void InputContext::showVirtualKeyboard()
const {
162 if (
auto *instance = d->manager_.instance()) {
163 return instance->userInterfaceManager().showVirtualKeyboard();
167 void InputContext::hideVirtualKeyboard()
const {
169 if (
auto *instance = d->manager_.instance()) {
170 return instance->userInterfaceManager().hideVirtualKeyboard();
174 bool InputContext::clientControlVirtualkeyboardShow()
const {
176 return d->clientControlVirtualkeyboardShow_;
179 void InputContext::setClientControlVirtualkeyboardShow(
bool show) {
181 d->clientControlVirtualkeyboardShow_ = show;
184 bool InputContext::clientControlVirtualkeyboardHide()
const {
186 return d->clientControlVirtualkeyboardHide_;
189 void InputContext::setClientControlVirtualkeyboardHide(
bool hide) {
191 d->clientControlVirtualkeyboardHide_ = hide;
195 if (!isPreeditEnabled) {
196 flag = flag.unset(CapabilityFlag::Preedit)
197 .unset(CapabilityFlag::FormattedPreedit);
204 if (d->capabilityFlags_ == flags) {
207 const auto oldFlags = capabilityFlags();
208 auto newFlags = calculateFlags(flags, d->isPreeditEnabled_);
209 if (oldFlags != newFlags) {
212 d->capabilityFlags_ = flags;
213 if (oldFlags != newFlags) {
220 return calculateFlags(d->capabilityFlags_, d->isPreeditEnabled_);
225 if (enable == d->isPreeditEnabled_) {
228 const auto oldFlags = capabilityFlags();
229 auto newFlags = calculateFlags(d->capabilityFlags_, enable);
230 if (oldFlags != newFlags) {
233 d->isPreeditEnabled_ = enable;
234 if (oldFlags != newFlags) {
241 return d->isPreeditEnabled_;
248 if (d->cursorRect_ == rect && d->scale_ == scale) {
251 d->cursorRect_ = rect;
260 d->group_->removeInputContext(
this);
264 d->group_->addInputContext(
this);
276 d->group_->setFocusedInputContext(
this);
285 if (d->group_->focusedInputContext() ==
this) {
286 d->group_->setFocusedInputContext(
nullptr);
298 void InputContext::setHasFocus(
bool hasFocus) {
300 if (hasFocus == d->hasFocus_) {
303 d->hasFocus_ = hasFocus;
304 d->manager_.notifyFocus(*
this, d->hasFocus_);
315 RETURN_IF_HAS_NO_FOCUS(
false);
316 decltype(std::chrono::steady_clock::now()) start;
318 if (::keyTrace().checkLogLevel(LogLevel::Debug)) {
319 start = std::chrono::steady_clock::now();
321 auto result = d->postEvent(event);
322 FCITX_KEYTRACE() <<
"KeyEvent handling time: " 323 << std::chrono::duration_cast<std::chrono::milliseconds>(
324 std::chrono::steady_clock::now() - start)
326 <<
"ms result:" << result;
332 RETURN_IF_HAS_NO_FOCUS(
false);
333 decltype(std::chrono::steady_clock::now()) start;
335 if (::keyTrace().checkLogLevel(LogLevel::Debug)) {
336 start = std::chrono::steady_clock::now();
338 auto result = d->postEvent(event);
339 FCITX_KEYTRACE() <<
"VirtualKeyboardEvent handling time: " 340 << std::chrono::duration_cast<std::chrono::milliseconds>(
341 std::chrono::steady_clock::now() - start)
343 <<
"ms result:" << result;
349 RETURN_IF_HAS_NO_FOCUS();
357 RETURN_IF_HAS_NO_FOCUS();
363 return d->surroundingText_;
368 return d->surroundingText_;
378 if (d->blockEventToClient_ == block) {
379 throw std::invalid_argument(
380 "setBlockEventToClient has invalid argument. Probably a bug in the " 383 d->blockEventToClient_ = block;
385 d->deliverBlockedEvents();
389 bool InputContext::hasPendingEvents()
const {
391 return !d->blockedEvents_.empty();
396 if (d->blockedEvents_.empty()) {
401 if (std::any_of(d->blockedEvents_.begin(), d->blockedEvents_.end(),
402 [](
const auto &event) {
403 return event->type() !=
404 EventType::InputContextUpdatePreedit;
412 return !inputPanel().clientPreedit().toString().empty();
417 if (
auto *instance = d->manager_.instance()) {
418 auto newString = instance->commitFilter(
this, text);
429 throw std::invalid_argument(text);
432 if (
auto *instance = d->manager_.instance()) {
433 auto newString = instance->commitFilter(
this, text);
442 deleteSurroundingTextImpl(offset, size);
452 if (!capabilityFlags().test(CapabilityFlag::Preedit)) {
456 const bool preeditIsEmpty = inputPanel().clientPreedit().empty();
457 if (preeditIsEmpty && d->lastPreeditUpdateIsEmpty_) {
460 d->lastPreeditUpdateIsEmpty_ = preeditIsEmpty;
472 return d->inputPanel_;
477 return d->inputPanel_;
482 return d->statusArea_;
487 return d->statusArea_;
492 InputContextV2::~InputContextV2() =
default;
494 InputContextEventBlocker::InputContextEventBlocker(
InputContext *inputContext)
495 : inputContext_(inputContext->watch()) {
499 InputContextEventBlocker::~InputContextEventBlocker() {
500 if (
auto *ic = inputContext_.get()) {
501 ic->setBlockEventToClient(
false);
void focusOut()
Called when input context losts the input focus.
CapabilityFlags capabilityFlags() const
Returns the current capability flags.
void deleteSurroundingText(int offset, unsigned int size)
Ask client to delete a range of surrounding text.
double scaleFactor() const
Return the client scale factor.
virtual void updateClientSideUIImpl()
Send the UI update to client.
void created()
Notifies the creation of input context.
const ICUUID & uuid() const
Returns the uuid of this input context.
size_t length(Iter start, Iter end)
Return the number UTF-8 characters in the string iterator range.
C++ Utility functions for handling utf8 strings.
void setEnablePreedit(bool enable)
Override the preedit hint from client.
std::vector< std::string > split(std::string_view str, std::string_view delim, SplitBehavior behavior)
Split the string by delim.
InputContextProperty * property(const std::string &name)
Returns the input context property by name.
bool hasFocus() const
Returns whether the input context holds the input focus.
void reset()
Called when input context state need to be reset.
Manager class for user interface.
void updateProperty(const std::string &name)
Notifies the change of a given input context property.
const std::string & program() const
Returns the program name of input context.
FocusGroup * focusGroup() const
Returns the current focus group of input context.
void setCapabilityFlags(CapabilityFlags flags)
Update the capability flags of the input context.
void updatePreedit()
Notifies client about changes in clientPreedit.
void destroy()
Notifies the destruction of the input context.
Class represents the current state of surrounding text of an input context.
void setCursorRect(Rect rect)
Update the current cursor rect of the input context.
void focusIn()
Called When input context gains the input focus.
std::string display() const
Returns the display server of the client.
Status area represent a list of actions and action may have sub actions.
Factory class for input context property.
bool keyEvent(KeyEvent &event)
Send a key event to current input context.
void setFocusGroup(FocusGroup *group)
Set the focus group of this input context.
InputPanel & inputPanel()
Returns the associated input panel.
void commitStringWithCursor(const std::string &text, size_t cursor)
Commit a string to application with a cursor location after commit.
std::string_view frontendName() const
Return the frontend name.
bool hasPendingEventsStrictOrder() const
Has pending event that need to use key order fix.
Class provides bit flag support for Enum.
Event for commit string with cursor.
StatusArea & statusArea()
Returns the associated StatusArea.
bool virtualKeyboardEvent(VirtualKeyboardEvent &event)
Send a virtual keyboard event to current input context.
SurroundingText & surroundingText()
Returns the mutable surrounding text of the input context.
An input context represents a client of Fcitx.
void updateUserInterface(UserInterfaceComponent component, bool immediate=false)
Notifies UI about changes in user interface.
void invokeAction(InvokeActionEvent &event)
Invoke an action on the preedit.
void updateSurroundingText()
Notifies the surrounding text modification from the client.
This is a class that designed to store state that is specific to certain input context.
void commitString(const std::string &text)
Commit a string to the client.
void forwardKey(const Key &rawKey, bool isRelease=false, int time=0)
Send a key event to client.
const Rect & cursorRect() const
Returns the cursor position of the client.
void setBlockEventToClient(bool block)
Prevent event deliver to input context, and re-send the event later.
bool isPreeditEnabled() const
Check if preedit is manually disabled.