Colour the candidate window from the desktop accent via libmonet

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
justmeandClaude Sonnet 5.5 committed 2026-10-20 00:23:12 +08:00
1 parent 064ca6b4d4
commit 5bea01e224
6 files changed
+95 -3

No files matched your search

+1
View File
@@ -10,6 +10,7 @@ UseAccentColor=False
PerScreenDPI=True
ForceWaylandDPI=0
EnableFractionalScale=True
UseMonet=True
Animations=True
Bounce=True
SpatialStiffness=2000
+8
View File
@@ -29,6 +29,12 @@ if (ENABLE_DBUS)
set(CLASSICUI_LIBS ${CLASSICUI_LIBS} Fcitx5::Module::DBus)
endif()
# libmonet, the shared colour code, header only. build.sh fetches it at a pinned
# commit and passes the directory.
if (NOT MONET_INCLUDE_DIR OR NOT EXISTS "${MONET_INCLUDE_DIR}/monet/monet.h")
message(FATAL_ERROR "MONET_INCLUDE_DIR must point at the include directory of libmonet")
endif()
add_fcitx5_addon(m3ui
classicui.cpp window.cpp theme.cpp inputwindow.cpp plasmathemewatchdog.cpp colorhelper.cpp ${CLASSICUI_SRCS}
)
@@ -37,6 +43,8 @@ if (ENABLE_X11)
target_compile_definitions(m3ui PRIVATE -DFCITX_XCB_EWMH)
endif()
target_include_directories(m3ui PRIVATE "${MONET_INCLUDE_DIR}")
target_link_libraries(m3ui
Fcitx5::Core
PkgConfig::Cairo PkgConfig::Pango
+53 -3
View File
@@ -7,6 +7,7 @@
#include "classicui.h"
#include <fcntl.h>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <filesystem>
@@ -180,6 +181,7 @@ void M3UI::reloadTheme() {
auto color = variant.dataAs<uint32_t>();
auto oldIsDark = isDark_;
isDark_ = (color == 1);
portalSchemeKnown_ = true;
if (oldIsDark != isDark_) {
CLASSICUI_DEBUG() << "XDG Portal AppearanceChanged "
"isDark"
@@ -189,8 +191,10 @@ void M3UI::reloadTheme() {
}
};
// The portal is also how the colours for UseMonet are learned, over the
// connection Fcitx5 already has, so nothing has to be started for it.
if (dbus()) {
if (*config_.useDarkTheme) {
if (*config_.useDarkTheme || *config_.useMonet) {
if (!darkModeEntry_ && settingMonitor_) {
darkModeEntry_ = settingMonitor_->watch(
"org.freedesktop.appearance", "color-scheme", parseMessage);
@@ -253,7 +257,7 @@ void M3UI::reloadTheme() {
};
if (dbus()) {
if (*config_.useAccentColor) {
if (*config_.useAccentColor || *config_.useMonet) {
if (!accentColorEntry_ && settingMonitor_) {
accentColorEntry_ =
settingMonitor_->watch("org.freedesktop.appearance",
@@ -266,7 +270,53 @@ void M3UI::reloadTheme() {
}
#endif
theme_.populateColor(accentColor_);
if (*config_.useMonet) {
theme_.applyMonet(mapScheme(monetScheme()));
}
// The accent colour is only watched for UseMonet, it must not recolour the
// fields of a classic theme unless the user asked for that.
theme_.populateColor(*config_.useAccentColor ? accentColor_
: std::nullopt);
}
monet::Scheme M3UI::monetScheme() const {
// Nothing is ever started from here: a slow program would stall the input
// method. The portal is read through the connection Fcitx5 has, the other
// sources only read files.
monet::Context ctx = monet::Context::system();
ctx.run = [](const std::vector<std::string> &, int) {
return std::optional<std::string>();
};
monet::Source portal{
"fcitx-portal", [this](const monet::Context &) {
monet::Appearance a;
if (accentColor_) {
auto q = [](float c) {
return static_cast<std::uint8_t>(std::lround(c * 255.0f));
};
a.accent = monet::Rgb{q(accentColor_->redF()),
q(accentColor_->greenF()),
q(accentColor_->blueF())};
}
if (portalSchemeKnown_) {
a.mode = isDark_ ? monet::Mode::Dark : monet::Mode::Light;
}
return a;
}};
// MONET_SEED and MONET_MODE first, then what the portal said, then the rest.
std::vector<monet::Source> sources;
for (auto &source : monet::defaultSources(ctx)) {
sources.push_back(std::move(source));
if (sources.back().name == "override") {
sources.push_back(portal);
}
}
const monet::Appearance a = monet::detect(ctx, sources);
CLASSICUI_DEBUG() << "Monet seed " << monet::hex(*a.accent) << " from "
<< a.accentSource << ", "
<< (*a.mode == monet::Mode::Dark ? "dark" : "light")
<< " from " << a.modeSource;
return monet::generate(*a.accent, *a.mode);
}
void M3UI::suspend() {
+9
View File
@@ -201,6 +201,11 @@ FCITX_CONFIGURATION(
"font DPI. If the value is 0, it means this option is "
"disabled.")}};
Option<bool> useMonet{
this, "UseMonet",
_("Take the colours from the desktop: its accent colour and its light "
"or dark setting"),
true};
Option<bool> animations{this, "Animations", _("Enable animations"), true};
Option<bool> bounce{this, "Bounce",
_("Bouncy overshoot (off: critically damped)"), true};
@@ -244,6 +249,8 @@ public:
FCITX_ADDON_DEPENDENCY_LOADER(dbus, instance_->addonManager());
Instance *instance() const { return instance_; }
const Configuration *getConfig() const override;
// The scheme for the current desktop colours, see classicui.cpp.
monet::Scheme monetScheme() const;
void setConfig(const RawConfig &config) override {
config_.load(config, true);
safeSaveAsIni(config_, "conf/m3ui.conf");
@@ -330,6 +337,8 @@ private:
mutable Theme subconfigTheme_;
bool suspended_ = true;
bool isDark_ = false;
// The portal has answered, so isDark_ is a value and not just the default.
bool portalSchemeKnown_ = false;
std::optional<Color> accentColor_ = std::nullopt;
std::unique_ptr<EventSource> deferedEnableTray_;
+19
View File
@@ -1180,6 +1180,25 @@ std::vector<Rect> Theme::mask(const BackgroundImageConfig &cfg, int width,
return result;
}
void Theme::applyMonet(const MonetColors &c) {
// A classic theme draws its panel from images, there is nothing to recolour.
if (inputPanel->surfaceColor->alpha() == 0) {
return;
}
auto *panel = inputPanel.mutableValue();
panel->surfaceColor.setValue(c.surface);
panel->outlineColor.setValue(c.outline);
panel->highlightFillColor.setValue(c.highlightFill);
panel->normalColor.setValue(c.normal);
panel->highlightColor.setValue(c.highlight);
panel->highlightBackgroundColor.setValue(c.highlightBackground);
panel->highlightCandidateColor.setValue(c.highlightCandidate);
panel->candidateLabelColor.setValue(c.candidateLabel);
panel->highlightCandidateLabelColor.setValue(c.highlightCandidateLabel);
panel->candidateCommentColor.setValue(c.candidateComment);
panel->highlightCandidateCommentColor.setValue(c.highlightCandidateComment);
}
void Theme::populateColor(std::optional<Color> accent) {
inputPanelBackground_ = *inputPanel->background->color;
inputPanelBorder_ = *inputPanel->background->borderColor;
+5
View File
@@ -30,6 +30,7 @@
#include "fcitx-utils/rect.h"
#include "fcitx/icontheme.h"
#include "common.h"
#include "monettheme.h"
namespace fcitx::m3ui {
enum class Gravity {
@@ -364,6 +365,10 @@ public:
const auto &accentColorFields() const { return accentColorFields_; }
void populateColor(std::optional<Color> accent);
// Replaces the colours of a Material style theme (one with a SurfaceColor)
// with colours that follow the desktop. Other themes are left as they are.
// Call it after load() and before populateColor().
void applyMonet(const MonetColors &colors);
const auto &inputPanelBackground() const { return inputPanelBackground_; }
const auto &inputPanelBorder() const { return inputPanelBorder_; }