17 #include <type_traits> 18 #include <unordered_map> 19 #include <unordered_set> 25 #include "fcitx-utils/macros.h" 26 #include "fcitx-utils/misc.h" 29 #include "fcitx/addoninfo.h" 40 struct UserInterfaceComponentHash {
42 std::underlying_type_t<T> operator()(T t)
const {
43 return static_cast<std::underlying_type_t<T>
>(t);
50 if (freeList_.empty()) {
54 auto value = *freeList_.begin();
55 freeList_.erase(freeList_.begin());
58 void returnId(
int id) {
59 assert(
id <= maxId_ && !freeList_.contains(
id));
63 std::set<int> freeList_;
67 bool isUserInterfaceValid(UserInterface *userInterface,
68 InputMethodMode inputMethodMode) {
69 if (userInterface ==
nullptr || !userInterface->available() ||
70 userInterface->addonInfo() ==
nullptr) {
74 return (userInterface->addonInfo()->uiType() == UIType::OnScreenKeyboard &&
75 inputMethodMode == InputMethodMode::OnScreenKeyboard) ||
76 (userInterface->addonInfo()->uiType() == UIType::PhyscialKeyboard &&
77 inputMethodMode == InputMethodMode::PhysicalKeyboard);
85 : addonManager_(addonManager) {}
87 void registerAction(
const std::string &name,
int id,
Action *action) {
89 [
this, action](
void *) { unregisterAction(action); });
90 action->setName(name);
92 actions_.emplace(name, std::make_pair(action, std::move(conn)));
93 idToAction_.emplace(action->
id(), action);
96 void unregisterAction(
Action *action) {
97 auto iter = actions_.find(action->
name());
98 if (iter == actions_.end()) {
101 if (std::get<0>(iter->second) != action) {
104 actions_.erase(iter);
105 idToAction_.erase(action->
id());
106 ids_.returnId(action->
id());
107 action->setName(std::string());
113 template <UserInterfaceComponent component>
118 std::vector<std::string> uis_;
120 std::unordered_map<std::string, std::pair<Action *, ScopedConnection>>
122 std::unordered_map<int, Action *> idToAction_;
124 using UIUpdateList = std::list<std::pair<
125 InputContext *, std::unordered_set<UserInterfaceComponent, EnumHash>>>;
126 UIUpdateList updateList_;
127 std::unordered_map<InputContext *, UIUpdateList::iterator> updateIndex_;
131 bool isVirtualKeyboardVisible_ =
false;
135 void UserInterfaceManagerPrivate::updateSingleComponent<
137 if (
const auto &virtualKeyboardCallback =
138 ic->inputPanel().customVirtualKeyboardCallback()) {
139 virtualKeyboardCallback(ic);
140 }
else if (ui_ !=
nullptr && ui_->addonInfo() !=
nullptr &&
141 ui_->addonInfo()->uiType() == UIType::OnScreenKeyboard) {
143 }
else if (
const auto &callback =
144 ic->inputPanel().customInputPanelCallback()) {
146 }
else if (ic->capabilityFlags().test(
148 ic->updateClientSideUIImpl();
155 void UserInterfaceManagerPrivate::updateSingleComponent<
164 #define _UI_CASE(COMP) \ 166 updateSingleComponent<COMP>(ic); \ 176 UserInterfaceManager::UserInterfaceManager(
AddonManager *addonManager)
177 : d_ptr(std::make_unique<UserInterfaceManagerPrivate>(addonManager)) {}
179 UserInterfaceManager::~UserInterfaceManager() =
default;
183 auto names = d->addonManager_->addonNames(AddonCategory::UI);
186 if (names.contains(uiName)) {
187 auto *ui = d->addonManager_->addon(uiName,
true);
189 d->uis_.push_back(uiName);
193 if (d->uis_.empty()) {
194 d->uis_.insert(d->uis_.end(), names.begin(), names.end());
196 d->uis_, [d](
const std::string &lhs,
const std::string &rhs) {
197 const auto *linfo = d->addonManager_->addonInfo(lhs);
198 const auto *rinfo = d->addonManager_->addonInfo(rhs);
205 auto lp = linfo->uiPriority();
206 auto rp = rinfo->uiPriority();
213 updateAvailability();
218 auto id = d->ids_.allocId();
219 auto name = stringutils::concat(
"$",
id);
220 auto iter = d->actions_.find(name);
222 if (iter != d->actions_.end()) {
223 FCITX_ERROR() <<
"Reserved id is used, how can this be possible?";
224 d->ids_.returnId(
id);
227 d->registerAction(name,
id, action);
234 if (!action->
name().empty() || name.empty()) {
237 if (name.starts_with(
"$")) {
238 FCITX_ERROR() <<
"Action name starts with $ is reserved.";
241 auto iter = d->actions_.find(name);
242 if (iter != d->actions_.end()) {
246 d->registerAction(name, d->ids_.allocId(), action);
252 d->unregisterAction(action);
257 auto iter = d->actions_.find(name);
258 if (iter == d->actions_.end()) {
261 return std::get<0>(iter->second);
266 auto iter = d->idToAction_.find(
id);
267 if (iter == d->idToAction_.end()) {
276 auto iter = d->updateIndex_.find(inputContext);
277 decltype(d->updateList_)::iterator listIter;
278 if (d->updateIndex_.end() == iter) {
279 d->updateList_.emplace_back(std::piecewise_construct,
280 std::forward_as_tuple(inputContext),
281 std::forward_as_tuple());
282 d->updateIndex_[inputContext] = listIter =
283 std::prev(d->updateList_.end());
285 listIter = iter->second;
287 listIter->second.insert(component);
292 auto iter = d->updateIndex_.find(inputContext);
293 if (d->updateIndex_.end() != iter) {
294 d->updateList_.erase(iter->second);
295 d->updateIndex_.erase(iter);
299 void UserInterfaceManager::flush() {
301 auto *instance = d->addonManager_->instance();
302 for (
auto &p : d->updateList_) {
303 for (
auto comp : p.second) {
305 d->updateDispatch(comp, p.first);
308 d->updateIndex_.clear();
309 d->updateList_.clear();
314 auto *instance = d->addonManager_->instance();
315 auto *oldUI = d->ui_;
317 std::string newUIName;
318 for (
auto &name : d->uis_) {
320 static_cast<UserInterface *
>(d->addonManager_->addon(name,
true));
321 if (isUserInterfaceValid(ui, instance
322 ? instance->inputMethodMode()
323 : InputMethodMode::PhysicalKeyboard)) {
329 if (oldUI != newUI) {
330 FCITX_DEBUG() <<
"Switching UI addon to " << newUIName;
338 d->uiName_ = std::move(newUIName);
344 updateVirtualKeyboardVisibility();
354 return d->isVirtualKeyboardVisible_;
360 auto *instance = d->addonManager_->instance();
361 if (!instance->virtualKeyboardAutoShow()) {
366 if (ui ==
nullptr || ui->addonInfo() ==
nullptr ||
367 ui->addonInfo()->uiType() != UIType::OnScreenKeyboard) {
372 vkui->showVirtualKeyboard();
378 auto *instance = d->addonManager_->instance();
379 if (!instance->virtualKeyboardAutoHide()) {
384 if (ui ==
nullptr || ui->addonInfo() ==
nullptr ||
385 ui->addonInfo()->uiType() != UIType::OnScreenKeyboard) {
390 vkui->hideVirtualKeyboard();
395 bool oldVisible = d->isVirtualKeyboardVisible_;
396 bool newVisible =
false;
399 if (ui && ui->addonInfo() &&
400 ui->addonInfo()->uiType() == UIType::OnScreenKeyboard) {
402 newVisible = vkui->isVirtualKeyboardVisible();
405 if (oldVisible != newVisible) {
406 d->isVirtualKeyboardVisible_ = newVisible;
408 if (
auto *instance = d->addonManager_->instance()) {
void hideVirtualKeyboard() const
Hide the virtual keyboard.
bool registerAction(const std::string &name, Action *action)
Register an named action.
void unregisterAction(Action *action)
Unregister the action.
The Action class provides an abstraction for user commands that can be added to user interfaces...
void load(const std::string &ui={})
Initialize the UI Addon.
Utilities to enable use object with signal.
std::string currentUI() const
Return the current active addon ui name.
int id()
Return the unique integer id of action.
Manager class for user interface.
Enum type for input context capability.
Action * lookupActionById(int id) const
Lookup an action by id.
Base class for User Interface addon.
void updateAvailability()
Invoke by user interface addon to notify if there is any availability change.
Events triggered that user interface manager that flush the UI update.
void update(UserInterfaceComponent component, InputContext *inputContext)
Mark a user interface component to be updated for given input context.
void expire(InputContext *inputContext)
Remove all pending updates for a given input context.
Connection that will disconnection when it goes out of scope.
bool isVirtualKeyboardVisible() const
Return if virtual keyboard is visible.
Whether client display input panel by itself.
A signal-slot implementation.
void updateVirtualKeyboardVisibility()
Invoke by user interface addon to notify if there is any virtual keyboard visibility change...
const std::string & name() const
The action name when this action is registered.
void showVirtualKeyboard() const
Show the virtual keyboard.
Action * lookupAction(const std::string &name) const
Lookup an action by the name.
An input context represents a client of Fcitx.