17 #include <string_view> 21 #include "fcitx-utils/environ.h" 24 #include "fcitx-utils/macros.h" 30 #include "focusgroup.h" 31 #include "inputcontext_p.h" 32 #include "inputcontextmanager.h" 44 bool shouldDisablePreeditByDefault(
const std::string &program) {
45 static const std::vector<std::regex> matchers = []() {
46 std::vector<std::regex> matchers;
47 auto apps = getEnvironment(
"FCITX_NO_PREEDIT_APPS");
49 apps = NO_PREEDIT_APPS;
52 for (
const auto &matcherString : matcherStrings) {
54 matchers.emplace_back(matcherString, std::regex::icase |
55 std::regex::extended |
58 FCITX_ERROR() <<
"Invalid regex pattern: " << matcherString;
64 return std::ranges::any_of(matchers, [&program](
const std::regex ®ex) {
65 return std::regex_match(program, regex);
69 CapabilityFlags calculateFlags(CapabilityFlags flag,
bool isPreeditEnabled) {
70 if (!isPreeditEnabled) {
71 flag = flag.unset(CapabilityFlag::Preedit)
72 .unset(CapabilityFlag::FormattedPreedit);
79 InputContextPrivate::InputContextPrivate(InputContext *q,
80 InputContextManager &manager,
81 const std::string &program)
82 : QPtrHolder(q), manager_(manager), group_(nullptr), inputPanel_(q),
83 statusArea_(q), program_(program),
84 isPreeditEnabled_(manager.isPreeditEnabledByDefault() &&
85 !shouldDisablePreeditByDefault(program)) {}
87 #define RETURN_IF_HAS_NO_FOCUS(...) \ 94 InputContext::InputContext(InputContextManager &manager,
95 const std::string &program)
96 : d_ptr(
std::make_unique<InputContextPrivate>(this, manager, program)) {
97 manager.registerInputContext(*
this);
100 InputContext::~InputContext() { assert(d_ptr->destroyed_); }
111 assert(!d->destroyed_);
113 d->group_->removeInputContext(
this);
116 d->manager_.unregisterInputContext(*
this);
117 d->destroyed_ =
true;
132 return d->group_ ? d->group_->display() :
"";
137 return d->cursorRect_;
147 auto *factory = d->manager_.factoryForName(name);
151 return d->manager_.property(*
this, factory);
157 return d->manager_.property(*
this, factory);
162 auto *factory = d->manager_.factoryForName(name);
166 updateProperty(factory);
171 auto *
property = d->manager_.property(*
this, factory);
172 if (!property->needCopy()) {
175 d->manager_.propagateProperty(*
this, factory);
178 bool InputContext::isVirtualKeyboardVisible()
const {
180 if (
auto *instance = d->manager_.instance()) {
181 return instance->userInterfaceManager().isVirtualKeyboardVisible();
186 void InputContext::showVirtualKeyboard()
const {
188 if (
auto *instance = d->manager_.instance()) {
189 instance->userInterfaceManager().showVirtualKeyboard();
194 void InputContext::hideVirtualKeyboard()
const {
196 if (
auto *instance = d->manager_.instance()) {
197 instance->userInterfaceManager().hideVirtualKeyboard();
202 bool InputContext::clientControlVirtualkeyboardShow()
const {
204 return d->clientControlVirtualkeyboardShow_;
207 void InputContext::setClientControlVirtualkeyboardShow(
bool show) {
209 d->clientControlVirtualkeyboardShow_ = show;
212 bool InputContext::clientControlVirtualkeyboardHide()
const {
214 return d->clientControlVirtualkeyboardHide_;
217 void InputContext::setClientControlVirtualkeyboardHide(
bool hide) {
219 d->clientControlVirtualkeyboardHide_ = hide;
224 if (d->capabilityFlags_ == flags) {
227 const auto oldFlags = capabilityFlags();
228 auto newFlags = calculateFlags(flags, d->isPreeditEnabled_);
229 if (oldFlags != newFlags) {
232 d->capabilityFlags_ = flags;
233 if (oldFlags != newFlags) {
240 return calculateFlags(d->capabilityFlags_, d->isPreeditEnabled_);
245 if (enable == d->isPreeditEnabled_) {
248 const auto oldFlags = capabilityFlags();
249 auto newFlags = calculateFlags(d->capabilityFlags_, enable);
250 if (oldFlags != newFlags) {
253 d->isPreeditEnabled_ = enable;
254 if (oldFlags != newFlags) {
261 return d->isPreeditEnabled_;
268 if (d->cursorRect_ == rect && d->scale_ == scale) {
271 d->cursorRect_ = rect;
280 d->group_->removeInputContext(
this);
284 d->group_->addInputContext(
this);
296 d->group_->setFocusedInputContext(
this);
305 if (d->group_->focusedInputContext() ==
this) {
306 d->group_->setFocusedInputContext(
nullptr);
318 void InputContext::setHasFocus(
bool hasFocus) {
320 if (hasFocus == d->hasFocus_) {
323 d->hasFocus_ = hasFocus;
324 d->manager_.notifyFocus(*
this, d->hasFocus_);
335 RETURN_IF_HAS_NO_FOCUS(
false);
336 decltype(std::chrono::steady_clock::now()) start;
338 if (::keyTrace().checkLogLevel(LogLevel::Debug)) {
339 start = std::chrono::steady_clock::now();
341 auto result = d->postEvent(event);
342 FCITX_KEYTRACE() <<
"KeyEvent handling time: " 343 << std::chrono::duration_cast<std::chrono::milliseconds>(
344 std::chrono::steady_clock::now() - start)
346 <<
"ms result:" << result;
352 RETURN_IF_HAS_NO_FOCUS(
false);
353 decltype(std::chrono::steady_clock::now()) start;
355 if (::keyTrace().checkLogLevel(LogLevel::Debug)) {
356 start = std::chrono::steady_clock::now();
358 auto result = d->postEvent(event);
359 FCITX_KEYTRACE() <<
"VirtualKeyboardEvent handling time: " 360 << std::chrono::duration_cast<std::chrono::milliseconds>(
361 std::chrono::steady_clock::now() - start)
363 <<
"ms result:" << result;
369 RETURN_IF_HAS_NO_FOCUS();
377 RETURN_IF_HAS_NO_FOCUS();
383 return d->surroundingText_;
388 return d->surroundingText_;
398 if (d->blockEventToClient_ == block) {
399 throw std::invalid_argument(
400 "setBlockEventToClient has invalid argument. Probably a bug in the " 403 d->blockEventToClient_ = block;
405 d->deliverBlockedEvents();
409 bool InputContext::hasPendingEvents()
const {
411 return !d->blockedEvents_.empty();
416 if (d->blockedEvents_.empty()) {
421 if (std::ranges::any_of(d->blockedEvents_, [](
const auto &event) {
422 return event->type() != EventType::InputContextUpdatePreedit;
430 return !inputPanel().clientPreedit().toString().empty();
436 if (
auto *instance = d->manager_.instance()) {
437 auto newString = instance->commitFilter(
this, sanitizedText);
450 throw std::invalid_argument(sanitizedText);
453 if (
auto *instance = d->manager_.instance()) {
454 auto newString = instance->commitFilter(
this, sanitizedText);
465 deleteSurroundingTextImpl(offset, size);
475 if (!capabilityFlags().test(CapabilityFlag::Preedit)) {
479 const bool preeditIsEmpty = inputPanel().clientPreedit().empty();
480 if (preeditIsEmpty && d->lastPreeditUpdateIsEmpty_) {
483 d->lastPreeditUpdateIsEmpty_ = preeditIsEmpty;
495 return d->inputPanel_;
500 return d->inputPanel_;
505 return d->statusArea_;
510 return d->statusArea_;
515 InputContextV2::~InputContextV2() =
default;
517 InputContextEventBlocker::InputContextEventBlocker(
InputContext *inputContext)
518 : inputContext_(inputContext->watch()) {
522 InputContextEventBlocker::~InputContextEventBlocker() {
523 if (
auto *ic = inputContext_.get()) {
524 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.
Local cache for surrounding text.
Provide utility to handle rectangle.
Manager class for user interface.
void updateProperty(const std::string &name)
Notifies the change of a given input context property.
Enum type for input context capability.
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.
Base class for User Interface addon.
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.
bool replaceInvalidInplace(std::string &str, char replacement)
Replace invalid UTF-8 sequences in-place with given byte.
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.
Input Context Property for Fcitx.
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.
Class to represent a key.
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.
std::string replaceInvalid(std::string_view str, char replacement)
Replace invalid UTF-8 sequences with given byte.
bool isPreeditEnabled() const
Check if preedit is manually disabled.