Add Material 3 candidate window addon for Fcitx5

Fork of the classicui addon (Fcitx5 5.1.23) with a Material 3 look, spring
based animations and a right click action menu for candidates.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
justmeandClaude Sonnet 5.5 committed 2026-10-19 21:58:54 +08:00
commit 0804634dc1
68 files changed
+11474

No files matched your search

+321
View File
@@ -0,0 +1,321 @@
/*
* SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/
#ifndef _FCITX_UI_CLASSIC_CLASSICUI_H_
#define _FCITX_UI_CLASSIC_CLASSICUI_H_
#include <cstddef>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
#include "fcitx-config/configuration.h"
#include "fcitx-config/iniparser.h"
#include "fcitx-config/option.h"
#include "fcitx-config/rawconfig.h"
#include "fcitx-utils/color.h"
#include "fcitx-utils/eventloopinterface.h"
#include "fcitx-utils/handlertable.h"
#include "fcitx-utils/i18n.h"
#include "fcitx-utils/stringutils.h"
#include "fcitx/addoninstance.h"
#include "fcitx/addonmanager.h"
#include "fcitx/event.h"
#include "fcitx/instance.h"
#include "fcitx/userinterface.h"
#include "classicui_public.h"
#include "config.h"
#include "plasmathemewatchdog.h"
#include "portalsettingmonitor.h"
#include "theme.h"
#ifdef ENABLE_X11
#include "xcb_public.h"
#endif
#ifdef WAYLAND_FOUND
#include "wayland_public.h"
#endif
#ifdef ENABLE_DBUS
#endif
namespace fcitx::m3ui {
inline constexpr std::string_view PlasmaThemeName = "plasma";
class UIInterface {
public:
UIInterface(std::string name) : name_(std::move(name)) {}
const std::string &name() const { return name_; }
virtual ~UIInterface() {}
virtual void update(UserInterfaceComponent component,
InputContext *inputContext) = 0;
virtual void updateCursor(InputContext * /*unused*/) {}
virtual void updateCurrentInputMethod(InputContext * /*unused*/) {}
virtual void suspend() = 0;
virtual void resume() {}
virtual void setEnableTray(bool) = 0;
private:
const std::string name_;
};
struct ThemeNameConstrain {
using Type = std::string;
bool check(const std::string &value) const {
const auto path = std::filesystem::path(value);
return !value.empty() && path.is_relative() &&
!path.has_parent_path() && path.filename() == path &&
path.filename() != "." && path.filename() != "..";
}
void dumpDescription(RawConfig & /*unused*/) const {}
};
struct ThemeAnnotation : public EnumAnnotation {
void setThemes(std::vector<std::pair<std::string, std::string>> themes,
bool plasmaTheme) {
themes_ = std::move(themes);
plasmaTheme_ = plasmaTheme;
}
void dumpDescription(RawConfig &config) const {
EnumAnnotation::dumpDescription(config);
config.setValueByPath("LaunchSubConfig", "True");
for (size_t i = 0; i < themes_.size(); i++) {
config.setValueByPath("Enum/" + std::to_string(i),
themes_[i].first);
config.setValueByPath("EnumI18n/" + std::to_string(i),
themes_[i].second);
if (themes_[i].first != PlasmaThemeName &&
!Theme::isSystemThemeName(themes_[i].first)) {
config.setValueByPath(
"SubConfigPath/" + std::to_string(i),
stringutils::concat("fcitx://config/addon/m3ui/theme/",
themes_[i].first));
} else {
config.setValueByPath("SubConfigPath/" + std::to_string(i), "");
}
}
}
private:
std::vector<std::pair<std::string, std::string>> themes_;
bool plasmaTheme_ = false;
};
struct MenuFontAnnotation : private FontAnnotation, private ToolTipAnnotation {
MenuFontAnnotation()
: ToolTipAnnotation(
_("This is only effective when the tray icon is xembed.")) {}
bool skipDescription() { return false; }
bool skipSave() { return false; }
void dumpDescription(RawConfig &config) {
FontAnnotation::dumpDescription(config);
ToolTipAnnotation::dumpDescription(config);
}
};
FCITX_CONFIGURATION(
ClassicUIConfig,
Option<bool> verticalCandidateList{this, "Vertical Candidate List",
_("Vertical Candidate List"), false};
Option<bool> useWheelForPaging{
this, "WheelForPaging", _("Use mouse wheel to go to prev or next page"),
true};
OptionWithAnnotation<std::string, FontAnnotation> font{
this, "Font", _("Font"), "Sans 10"};
OptionWithAnnotation<std::string, MenuFontAnnotation> menuFont{
this, "MenuFont", _("Menu Font"), "Sans 10"};
OptionWithAnnotation<std::string, FontAnnotation> trayFont{
this, "TrayFont", _("Tray Font"), "Sans Bold 10"};
Option<Color> trayBorderColor{this, "TrayOutlineColor",
_("Tray Label Outline Color"),
Color("#000000ff")};
Option<Color> trayTextColor{this, "TrayTextColor",
_("Tray Label Text Color"), Color("#ffffffff")};
Option<bool> preferTextIcon{this, "PreferTextIcon", _("Prefer Text Icon"),
false};
OptionWithAnnotation<bool, ToolTipAnnotation> showLayoutNameInIcon{
this,
"ShowLayoutNameInIcon",
_("Show Layout Name In Icon"),
true,
{},
{},
{_("Show layout name in icon if there is more than one active layout. "
"If prefer text icon is set to true, this option will be "
"ignored.")}};
OptionWithAnnotation<bool, ToolTipAnnotation>
useInputMethodLanguageToDisplayText{
this,
"UseInputMethodLanguageToDisplayText",
_("Use input method language to display text"),
true,
{},
{},
{_("For example, display character with Chinese variant when using "
"Pinyin and Japanese variant when using Anthy. The font "
"configuration needs to support this to use this feature.")}};
Option<std::string, ThemeNameConstrain, DefaultMarshaller<std::string>,
ThemeAnnotation>
theme{this, "Theme", _("Theme"), "default"};
Option<std::string, ThemeNameConstrain, DefaultMarshaller<std::string>,
ThemeAnnotation>
themeDark{this, "DarkTheme", _("Dark Theme"), "default-dark"};
Option<bool> useDarkTheme{this, "UseDarkTheme",
_("Follow system light/dark color scheme"),
false};
Option<bool> useAccentColor{
this, "UseAccentColor",
_("Follow system accent color if it is supported by theme and desktop"),
true};
OptionWithAnnotation<bool, ToolTipAnnotation> perScreenDPI{
this,
"PerScreenDPI",
_("Use Per Screen DPI on X11"),
false,
{},
{},
{_("This option will be always disabled on XWayland.")}};
Option<int, IntConstrain, DefaultMarshaller<int>, ToolTipAnnotation>
forceWaylandDPI{
this,
"ForceWaylandDPI",
_("Force font DPI on Wayland"),
0,
IntConstrain(0),
{},
{_("Normally Wayland uses 96 as font DPI in combination with the "
"screen scale factor. This option allows you to override the "
"font DPI. If the value is 0, it means this option is "
"disabled.")}};
Option<bool> animations{this, "Animations", _("Enable animations"), true};
Option<bool> bounce{this, "Bounce",
_("Bouncy overshoot (off: critically damped)"), true};
Option<int, IntConstrain> spatialStiffness{
this, "SpatialStiffness",
_("Spring stiffness for movement, higher is faster"), 2000,
IntConstrain(50, 20000)};
Option<int, IntConstrain> spatialDamping{
this, "SpatialDamping",
_("Spring damping ratio for movement in percent, lower bounces more"),
62, IntConstrain(20, 100)};
Option<int, IntConstrain> effectsStiffness{
this, "EffectsStiffness",
_("Spring stiffness for fading, higher is faster"), 3800,
IntConstrain(50, 20000)};
Option<int, IntConstrain> slideDistance{
this, "SlideDistance", _("Slide-in distance (px)"), 22,
IntConstrain(0, 200)};
Option<int, IntConstrain> slideDirection{
this, "SlideDirection",
_("Slide-in direction (0: from left, 1: from right, 2: from below)"),
0, IntConstrain(0, 2)};
OptionWithAnnotation<bool, ToolTipAnnotation> fractionalScale{
this,
"EnableFractionalScale",
_("Enable fractional scale under Wayland"),
true,
{},
{},
{_("This option require support from wayland compositor.")}};);
class M3UI final : public UserInterface {
public:
M3UI(Instance *instance);
~M3UI();
FCITX_ADDON_DEPENDENCY_LOADER(xcb, instance_->addonManager());
FCITX_ADDON_DEPENDENCY_LOADER(wayland, instance_->addonManager());
FCITX_ADDON_DEPENDENCY_LOADER(waylandim, instance_->addonManager());
FCITX_ADDON_DEPENDENCY_LOADER(dbus, instance_->addonManager());
Instance *instance() const { return instance_; }
const Configuration *getConfig() const override;
void setConfig(const RawConfig &config) override {
config_.load(config, true);
safeSaveAsIni(config_, "conf/m3ui.conf");
reloadTheme();
}
const Configuration *getSubConfig(const std::string &path) const override;
void setSubConfig(const std::string &path,
const RawConfig &config) override;
auto &config() const { return config_; }
Theme &theme() { return theme_; }
void suspend() override;
void resume() override;
bool suspended() const { return suspended_; }
bool available() override { return true; }
void update(UserInterfaceComponent component,
InputContext *inputContext) override;
void reloadConfig() override;
std::vector<unsigned char> labelIcon(const std::string &label,
unsigned int size);
bool preferTextIcon() const;
bool showLayoutNameInIcon() const;
private:
FCITX_ADDON_DEPENDENCY_LOADER(notificationitem, instance_->addonManager());
FCITX_ADDON_EXPORT_FUNCTION(M3UI, labelIcon);
FCITX_ADDON_EXPORT_FUNCTION(M3UI, preferTextIcon);
FCITX_ADDON_EXPORT_FUNCTION(M3UI, showLayoutNameInIcon);
UIInterface *uiForEvent(Event &event);
UIInterface *uiForInputContext(InputContext *inputContext);
UIInterface *uiForDisplay(const std::string &display);
void reloadTheme();
#ifdef ENABLE_X11
std::unique_ptr<HandlerTableEntry<XCBConnectionCreated>>
xcbCreatedCallback_;
std::unique_ptr<HandlerTableEntry<XCBConnectionClosed>> xcbClosedCallback_;
#endif
#ifdef WAYLAND_FOUND
std::unique_ptr<HandlerTableEntry<WaylandConnectionCreated>>
waylandCreatedCallback_;
std::unique_ptr<HandlerTableEntry<WaylandConnectionClosed>>
waylandClosedCallback_;
#endif
std::unique_ptr<EventSource> deferedReloadTheme_;
#ifdef ENABLE_DBUS
std::unique_ptr<PortalSettingMonitor> settingMonitor_;
std::unique_ptr<PortalSettingEntry> darkModeEntry_;
std::unique_ptr<PortalSettingEntry> accentColorEntry_;
#endif
std::vector<std::unique_ptr<HandlerTableEntry<EventHandler>>>
eventHandlers_;
std::vector<std::unique_ptr<HandlerTableEntry<EventHandler>>>
persistentEventHandlers_;
std::unique_ptr<HandlerTableEntryBase> sniHandler_;
std::unordered_map<std::string, std::unique_ptr<UIInterface>> uis_;
Instance *instance_;
ClassicUIConfig config_;
Theme theme_;
mutable Theme subconfigTheme_;
bool suspended_ = true;
bool isDark_ = false;
std::optional<Color> accentColor_ = std::nullopt;
std::unique_ptr<EventSource> deferedEnableTray_;
std::unique_ptr<PlasmaThemeWatchdog> plasmaThemeWatchdog_;
};
} // namespace fcitx::m3ui
#endif // _FCITX_UI_CLASSIC_CLASSICUI_H_