Hide forgotten candidates, add autostart script, Chinese README
- Record words picked with "Forget candidate" in a block list and drop them from the candidate list before each refresh, so words from read only dictionaries stay gone. - Add scripts/enable-autostart.sh for starting with m3ui at login. - Make the Chinese README the default, keep the English one as README.en.md. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0804634dc1
commit
8d14454bb9
7 files changed
+329
-100
No files matched your search
+131
@@ -0,0 +1,131 @@
|
||||
# fcitx5-m3ui
|
||||
|
||||
A Material 3 styled candidate window for [Fcitx5](https://github.com/fcitx/fcitx5),
|
||||
built as a user interface addon (`m3ui`) that replaces `classicui`.
|
||||
|
||||
[简体中文](README.md)
|
||||
|
||||
## Features
|
||||
|
||||
- Rounded Material 3 panel with light and dark themes that follow the system.
|
||||
- Spring based animations: the panel slides in with a small bounce and fades in,
|
||||
the highlight pill glides between candidates. Stiffness and damping are
|
||||
configurable.
|
||||
- Right click a candidate to open an action menu. It shows whatever actions the
|
||||
input method offers, for Pinyin that is *Forget candidate*, *Pin to top as
|
||||
custom phrase* and *Delete from custom phrase*.
|
||||
- A forgotten candidate stays gone, also words from read only dictionaries
|
||||
(see *Block list* below).
|
||||
- Works on Wayland and X11 (Xwayland) input contexts, positioning and pointer
|
||||
handling are inherited from `classicui`.
|
||||
- Optional KWin helper that shortens KWin's own fade out for the candidate
|
||||
window only.
|
||||
|
||||
## Status
|
||||
|
||||
Personal project, only tried on Fedora 44, KDE Plasma 6.7 on Wayland, NVIDIA
|
||||
GPU, Fcitx5 5.1.23 with the Pinyin engine. Animations, the right click menu,
|
||||
forgetting candidates and starting at login have been used on that setup.
|
||||
|
||||
**`scripts/build.sh` has not been run from scratch in a clean environment**, and
|
||||
no other distribution or desktop has been tried. Please open an issue if
|
||||
something breaks.
|
||||
|
||||
The addon is a fork of `classicui` and is built inside the Fcitx5 source tree,
|
||||
because Fcitx5's Wayland protocol wrappers are not installed by `fcitx5-devel`.
|
||||
The Fcitx5 source version used for the build must match the installed Fcitx5.
|
||||
|
||||
There is no fade out animation of its own: on Wayland the window is destroyed as
|
||||
soon as the panel has nothing to show.
|
||||
|
||||
## Build
|
||||
|
||||
Needs the Fcitx5 development files and the dependencies of `classicui`
|
||||
(cairo, pango, librsvg, gdk-pixbuf, wayland-protocols, xcb libraries). On Fedora:
|
||||
|
||||
```
|
||||
sudo dnf install fcitx5-devel extra-cmake-modules cairo-devel pango-devel \
|
||||
gdk-pixbuf2-devel librsvg2-devel wayland-protocols-devel \
|
||||
plasma-wayland-protocols-devel systemd-devel libuuid-devel fmt-devel \
|
||||
expat-devel iso-codes-devel json-devel xkeyboard-config-devel \
|
||||
xcb-util-devel xcb-util-keysyms-devel xcb-util-wm-devel \
|
||||
xcb-util-image-devel xcb-imdkit-devel libxkbfile-devel libxkbcommon-x11-devel
|
||||
```
|
||||
|
||||
then
|
||||
|
||||
```
|
||||
scripts/build.sh # downloads Fcitx5 + yoga, overlays src/, builds m3ui
|
||||
scripts/install.sh # installs into ~/.local, nothing outside $HOME
|
||||
```
|
||||
|
||||
`build.sh` picks the Fcitx5 version from `pkg-config`, override it with
|
||||
`FCITX_VERSION`.
|
||||
|
||||
## Use
|
||||
|
||||
```
|
||||
FCITX_ADDON_DIRS=$HOME/.local/m3ui/lib64/fcitx5:/usr/lib64/fcitx5 fcitx5 -r -u m3ui
|
||||
```
|
||||
|
||||
`FCITX_ADDON_DIRS` is how Fcitx5 finds addons outside the system directory.
|
||||
`fcitx5 -r` (no arguments) brings the stock interface back.
|
||||
|
||||
**Start with m3ui at every login:**
|
||||
|
||||
```
|
||||
scripts/enable-autostart.sh
|
||||
systemctl --user daemon-reload && systemctl --user restart app-org.fcitx.Fcitx5@autostart.service
|
||||
```
|
||||
|
||||
It writes two user level overrides (the XDG autostart entry and D-Bus
|
||||
activation) and changes only the `Exec` line. If the addon fails to load
|
||||
Fcitx5 falls back to classicui. To undo, delete the two files the script
|
||||
prints.
|
||||
|
||||
Settings live in `~/.config/fcitx5/conf/m3ui.conf`, see
|
||||
[config/m3ui.conf.example](config/m3ui.conf.example).
|
||||
|
||||
| Key | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `Animations` | True | Master switch for animations |
|
||||
| `Bounce` | True | Off makes movement critically damped (no overshoot) |
|
||||
| `SpatialStiffness` | 2000 | Spring stiffness for movement, higher is faster |
|
||||
| `SpatialDamping` | 62 | Damping ratio in percent, lower bounces more |
|
||||
| `EffectsStiffness` | 3800 | Spring stiffness for fading |
|
||||
| `SlideDistance` | 22 | Slide in distance in px |
|
||||
| `SlideDirection` | 0 | 0 from left, 1 from right, 2 from below |
|
||||
| `Theme` / `DarkTheme` | `m3ui-light` / `m3ui-dark` | Themes in `themes/` |
|
||||
|
||||
Themes are classicui themes with extra keys in `[InputPanel]`: `SurfaceColor`,
|
||||
`OutlineColor`, `HighlightFillColor`, `PanelRadius`, `ItemRadius`. A transparent
|
||||
`SurfaceColor` falls back to the classic image based drawing, so classic themes
|
||||
still work (without animation).
|
||||
|
||||
## Block list
|
||||
|
||||
The Pinyin engine's *Forget candidate* only removes words you have learned
|
||||
while typing. Words from the read only system dictionary (`sc.dict`) or from
|
||||
extra dictionaries come back when you type the same input again, also in the
|
||||
current composition.
|
||||
|
||||
When you pick *Forget candidate*, m3ui therefore also records the word in
|
||||
`~/.local/share/fcitx5/m3ui/blocked-words.txt` (one word per line) and removes
|
||||
those words from the candidate list before every refresh.
|
||||
|
||||
- Matching is exact on the full text of the candidate.
|
||||
- To bring a word back, delete its line and restart Fcitx5.
|
||||
- This is a layer on top of the engine. With the stock classicui the words
|
||||
show up again.
|
||||
|
||||
## KWin fade out (optional, KDE Plasma)
|
||||
|
||||
KWin's *Fading Popups* effect fades popups out over 600 ms and the candidate
|
||||
window counts as a popup. `kwin/install-fadingpopups-override.sh` copies the
|
||||
effect from your system into `~/.local/share/kwin-wayland/effects/` and patches
|
||||
the copy so only the window class `fcitx fcitx` is affected. The script fails
|
||||
if the upstream effect source no longer matches what it patches.
|
||||
|
||||
## License
|
||||
|
||||
LGPL-2.1-or-later, see [NOTICE.md](NOTICE.md).
|
||||
@@ -1,41 +1,41 @@
|
||||
# fcitx5-m3ui
|
||||
|
||||
A Material 3 styled candidate window for [Fcitx5](https://github.com/fcitx/fcitx5),
|
||||
built as a user interface addon (`m3ui`) that replaces `classicui`.
|
||||
给 [Fcitx5](https://github.com/fcitx/fcitx5) 用的 Material 3 风格候选框,
|
||||
以界面插件(`m3ui`)的形式替换 `classicui`。
|
||||
|
||||
[简体中文](README.zh-CN.md)
|
||||
[English](README.en.md)
|
||||
|
||||
## Features
|
||||
## 特性
|
||||
|
||||
- Rounded Material 3 panel with light and dark themes that follow the system.
|
||||
- Spring based animations: the panel slides in with a small bounce and fades in,
|
||||
the highlight pill glides between candidates. Stiffness and damping are
|
||||
configurable.
|
||||
- Right click a candidate to open an action menu. It shows whatever actions the
|
||||
input method offers, for Pinyin that is *Forget candidate*, *Pin to top as
|
||||
custom phrase* and *Delete from custom phrase*.
|
||||
- Works on Wayland and X11 (Xwayland) input contexts, positioning and pointer
|
||||
handling are inherited from `classicui`.
|
||||
- Optional KWin helper that shortens KWin's own fade out for the candidate
|
||||
window only.
|
||||
- Material 3 圆角面板,深色 / 浅色主题跟随系统。
|
||||
- 弹簧动画:候选框带轻微回弹从侧边滑入并淡入,高亮块在候选词之间滑动。
|
||||
刚度、阻尼、方向、距离都可以调。
|
||||
- 在候选词上**右键**打开动作菜单,内容取决于输入法提供的动作。拼音有
|
||||
"忘记候选词 / 置顶为自定义短语 / 从自定义短语中删除"。
|
||||
- **忘记候选词后不会再出现**,包括系统词库和扩展词库里的词(见下文"屏蔽列表")。
|
||||
- Wayland 和 X11(Xwayland)输入上下文都支持,定位和鼠标处理沿用 `classicui`。
|
||||
- 可选的 KWin 辅助脚本:只缩短候选框自己的 KWin 淡出时间。
|
||||
|
||||
## Status
|
||||
## 状态
|
||||
|
||||
Personal project, only tried on Fedora 44, KDE Plasma 6.7 on Wayland, NVIDIA
|
||||
GPU, Fcitx5 5.1.23 with the Pinyin engine. The build scripts have not been run
|
||||
on any other setup.
|
||||
个人项目,只在 Fedora 44、KDE Plasma 6.7(Wayland)、NVIDIA 显卡、
|
||||
Fcitx5 5.1.23、拼音引擎上用过。动画、右键菜单、忘记候选词、开机自动启动
|
||||
在这套环境里都实际用过。
|
||||
|
||||
The addon is a fork of `classicui` and is built inside the Fcitx5 source tree,
|
||||
because Fcitx5's Wayland protocol wrappers are not installed by `fcitx5-devel`.
|
||||
The Fcitx5 source version used for the build must match the installed Fcitx5.
|
||||
**`scripts/build.sh` 没有在干净环境里从零完整跑过**,其他发行版和桌面也没有试过,
|
||||
遇到问题请开 issue。
|
||||
|
||||
There is no fade out animation of its own: on Wayland the window is destroyed as
|
||||
soon as the panel has nothing to show.
|
||||
本插件是 `classicui` 的分支,需要在 Fcitx5 源码树内构建:Fcitx5 的 Wayland
|
||||
协议封装不包含在 `fcitx5-devel` 里。构建用的源码版本必须和已安装的 Fcitx5 一致,
|
||||
Fcitx5 升级后需要重新构建;如果插件加载失败,Fcitx5 会自动退回 `classicui`,
|
||||
不会没有输入法。
|
||||
|
||||
## Build
|
||||
没有自己的淡出动画:Wayland 下面板没有内容时窗口会被立刻销毁。
|
||||
|
||||
Needs the Fcitx5 development files and the dependencies of `classicui`
|
||||
(cairo, pango, librsvg, gdk-pixbuf, wayland-protocols, xcb libraries). On Fedora:
|
||||
## 构建
|
||||
|
||||
需要 Fcitx5 开发文件和 `classicui` 的依赖(cairo、pango、librsvg、gdk-pixbuf、
|
||||
wayland-protocols、xcb 相关库)。Fedora 上:
|
||||
|
||||
```
|
||||
sudo dnf install fcitx5-devel extra-cmake-modules cairo-devel pango-devel \
|
||||
@@ -46,53 +46,79 @@ sudo dnf install fcitx5-devel extra-cmake-modules cairo-devel pango-devel \
|
||||
xcb-util-image-devel xcb-imdkit-devel libxkbfile-devel libxkbcommon-x11-devel
|
||||
```
|
||||
|
||||
then
|
||||
然后:
|
||||
|
||||
```
|
||||
scripts/build.sh # downloads Fcitx5 + yoga, overlays src/, builds m3ui
|
||||
scripts/install.sh # installs into ~/.local, nothing outside $HOME
|
||||
scripts/build.sh # 下载 Fcitx5 和 yoga 源码,叠加 src/,构建 m3ui
|
||||
scripts/install.sh # 安装到 ~/.local,不改动 $HOME 以外的任何东西
|
||||
```
|
||||
|
||||
`build.sh` picks the Fcitx5 version from `pkg-config`, override it with
|
||||
`FCITX_VERSION`.
|
||||
`build.sh` 会用 `pkg-config` 取已安装的 Fcitx5 版本,可以用 `FCITX_VERSION` 覆盖。
|
||||
|
||||
## Use
|
||||
## 使用
|
||||
|
||||
手动试用:
|
||||
|
||||
```
|
||||
FCITX_ADDON_DIRS=$HOME/.local/m3ui/lib64/fcitx5:/usr/lib64/fcitx5 fcitx5 -r -u m3ui
|
||||
```
|
||||
|
||||
`FCITX_ADDON_DIRS` is how Fcitx5 finds addons outside the system directory, it
|
||||
has to be set in the environment that starts Fcitx5 if you want this on every
|
||||
login. `fcitx5 -r` brings the stock interface back.
|
||||
`FCITX_ADDON_DIRS` 是让 Fcitx5 找到系统目录以外的插件,`fcitx5 -r`(不带参数)
|
||||
可以回到原版界面。
|
||||
|
||||
Settings live in `~/.config/fcitx5/conf/m3ui.conf`, see
|
||||
[config/m3ui.conf.example](config/m3ui.conf.example).
|
||||
**开机自动使用**:
|
||||
|
||||
| Key | Default | Meaning |
|
||||
```
|
||||
scripts/enable-autostart.sh
|
||||
systemctl --user daemon-reload && systemctl --user restart app-org.fcitx.Fcitx5@autostart.service
|
||||
```
|
||||
|
||||
它会在家目录里写两个覆盖文件(XDG 自启动项和 D-Bus 激活项),只改 `Exec` 一行。
|
||||
撤销只需删掉脚本输出的那两个文件。
|
||||
|
||||
## 配置
|
||||
|
||||
配置文件是 `~/.config/fcitx5/conf/m3ui.conf`,示例见
|
||||
[config/m3ui.conf.example](config/m3ui.conf.example)。
|
||||
|
||||
| 键 | 默认值 | 含义 |
|
||||
|---|---|---|
|
||||
| `Animations` | True | Master switch for animations |
|
||||
| `Bounce` | True | Off makes movement critically damped (no overshoot) |
|
||||
| `SpatialStiffness` | 2000 | Spring stiffness for movement, higher is faster |
|
||||
| `SpatialDamping` | 62 | Damping ratio in percent, lower bounces more |
|
||||
| `EffectsStiffness` | 3800 | Spring stiffness for fading |
|
||||
| `SlideDistance` | 22 | Slide in distance in px |
|
||||
| `SlideDirection` | 0 | 0 from left, 1 from right, 2 from below |
|
||||
| `Theme` / `DarkTheme` | `m3ui-light` / `m3ui-dark` | Themes in `themes/` |
|
||||
| `Animations` | True | 动画总开关 |
|
||||
| `Bounce` | True | 关闭后移动为临界阻尼(无回弹) |
|
||||
| `SpatialStiffness` | 2000 | 移动的弹簧刚度,越大越快 |
|
||||
| `SpatialDamping` | 62 | 阻尼比(百分数),越小回弹越明显 |
|
||||
| `EffectsStiffness` | 3800 | 淡入的弹簧刚度 |
|
||||
| `SlideDistance` | 22 | 滑入距离(像素) |
|
||||
| `SlideDirection` | 0 | 0 从左,1 从右,2 从下 |
|
||||
| `Theme` / `DarkTheme` | `m3ui-light` / `m3ui-dark` | `themes/` 里的主题 |
|
||||
|
||||
Themes are classicui themes with extra keys in `[InputPanel]`: `SurfaceColor`,
|
||||
`OutlineColor`, `HighlightFillColor`, `PanelRadius`, `ItemRadius`. A transparent
|
||||
`SurfaceColor` falls back to the classic image based drawing, so classic themes
|
||||
still work (without animation).
|
||||
主题是 classicui 主题,在 `[InputPanel]` 里多了几个键:`SurfaceColor`、
|
||||
`OutlineColor`、`HighlightFillColor`、`PanelRadius`、`ItemRadius`。
|
||||
`SurfaceColor` 完全透明时回退到 classicui 的图片绘制,所以原有的 classicui
|
||||
主题仍然可用(但没有动画)。
|
||||
|
||||
## KWin fade out (optional, KDE Plasma)
|
||||
## 屏蔽列表
|
||||
|
||||
KWin's *Fading Popups* effect fades popups out over 600 ms and the candidate
|
||||
window counts as a popup. `kwin/install-fadingpopups-override.sh` copies the
|
||||
effect from your system into `~/.local/share/kwin-wayland/effects/` and patches
|
||||
the copy so only the window class `fcitx fcitx` is affected. The script fails
|
||||
if the upstream effect source no longer matches what it patches.
|
||||
拼音引擎的"忘记候选词"只会删除你自己打字学到的词,系统词库(`sc.dict`)和
|
||||
扩展词库里的词是只读的,忘记之后重新输入仍会出现,当前输入状态里也一样。
|
||||
|
||||
## License
|
||||
所以 m3ui 在你点"忘记候选词"时,会把这个词记到
|
||||
`~/.local/share/fcitx5/m3ui/blocked-words.txt`(一行一个词),并在每次刷新候选
|
||||
之前把它们从候选列表里摘掉。
|
||||
|
||||
LGPL-2.1-or-later, see [NOTICE.md](NOTICE.md).
|
||||
- 按候选词的完整文字精确匹配,只隐藏这个词本身。
|
||||
- 想恢复某个词:删掉文件里对应的一行,然后重启 Fcitx5。
|
||||
- 这是界面层的补充。换回原版 classicui 后,这些词会重新出现。
|
||||
|
||||
## KWin 淡出(可选,KDE Plasma)
|
||||
|
||||
KWin 的"渐隐弹出窗口"特效会把弹出窗口淡出 600 毫秒,候选框也被算作弹出窗口。
|
||||
`kwin/install-fadingpopups-override.sh` 会把系统里的这个特效复制到
|
||||
`~/.local/share/kwin-wayland/effects/` 并打补丁,只对窗口类 `fcitx fcitx` 生效:
|
||||
跳过 KWin 的淡入(让 m3ui 自己的弹入动画可见),并把淡出缩短到 90 毫秒
|
||||
(`FADE_OUT_MS` 可调)。其他弹出窗口不受影响。如果上游特效的源码变了、补丁对不上,
|
||||
脚本会直接报错退出。
|
||||
|
||||
## 许可证
|
||||
|
||||
LGPL-2.1-or-later,见 [NOTICE.md](NOTICE.md)。
|
||||
@@ -1,41 +0,0 @@
|
||||
# fcitx5-m3ui
|
||||
|
||||
给 [Fcitx5](https://github.com/fcitx/fcitx5) 用的 Material 3 风格候选框,
|
||||
以界面插件(`m3ui`)的形式替换 `classicui`。
|
||||
|
||||
[English](README.md)
|
||||
|
||||
## 特性
|
||||
|
||||
- Material 3 圆角面板,深色/浅色主题跟随系统。
|
||||
- 弹簧动画:候选框带轻微回弹滑入并淡入,高亮块在候选词之间滑动,刚度和阻尼可调。
|
||||
- 在候选词上右键打开动作菜单,内容取决于输入法提供的动作,拼音有
|
||||
"忘记候选词 / 置顶为自定义短语 / 从自定义短语中删除"。
|
||||
- Wayland 和 X11(Xwayland)输入上下文都支持,定位和鼠标处理沿用 `classicui`。
|
||||
- 可选的 KWin 辅助脚本:只缩短候选框自己的 KWin 淡出时间。
|
||||
|
||||
## 状态
|
||||
|
||||
个人项目,只在 Fedora 44、KDE Plasma 6.7(Wayland)、NVIDIA 显卡、
|
||||
Fcitx5 5.1.23、拼音引擎上用过,构建脚本没有在其他环境跑过。
|
||||
|
||||
本插件是 `classicui` 的分支,需要在 Fcitx5 源码树内构建(Fcitx5 的 Wayland 协议封装
|
||||
不包含在 `fcitx5-devel` 里),构建用的源码版本必须和已安装的 Fcitx5 一致。
|
||||
|
||||
没有自己的淡出动画:Wayland 下面板无内容时窗口会被立刻销毁。
|
||||
|
||||
## 构建与使用
|
||||
|
||||
依赖安装命令、`scripts/build.sh`、`scripts/install.sh` 以及启动方式见
|
||||
[English README](README.md)。启动命令:
|
||||
|
||||
```
|
||||
FCITX_ADDON_DIRS=$HOME/.local/m3ui/lib64/fcitx5:/usr/lib64/fcitx5 fcitx5 -r -u m3ui
|
||||
```
|
||||
|
||||
想回到原版界面运行 `fcitx5 -r`。配置在 `~/.config/fcitx5/conf/m3ui.conf`,
|
||||
示例见 [config/m3ui.conf.example](config/m3ui.conf.example)。
|
||||
|
||||
## 许可证
|
||||
|
||||
LGPL-2.1-or-later,见 [NOTICE.md](NOTICE.md)。
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# Makes Fcitx5 start with m3ui at every login. Writes two user level overrides,
|
||||
# one for the XDG autostart entry and one for D-Bus activation, nothing outside
|
||||
# $HOME is touched. If the addon cannot be loaded Fcitx5 falls back to classicui.
|
||||
#
|
||||
# Undo: delete the two files printed below and run
|
||||
# systemctl --user daemon-reload
|
||||
set -euo pipefail
|
||||
|
||||
prefix=${PREFIX:-$HOME/.local/m3ui}
|
||||
config=${XDG_CONFIG_HOME:-$HOME/.config}
|
||||
data=${XDG_DATA_HOME:-$HOME/.local/share}
|
||||
|
||||
lib=$(find "$prefix" -name libm3ui.so 2>/dev/null | head -1)
|
||||
[ -n "$lib" ] || { echo "libm3ui.so not found under $prefix, run scripts/install.sh first" >&2; exit 1; }
|
||||
addondir=$(dirname "$lib")
|
||||
system_addondir=$(pkg-config --variable=addondir Fcitx5Core 2>/dev/null || true)
|
||||
system_addondir=${system_addondir:-/usr/lib64/fcitx5}
|
||||
dirs="$addondir:$system_addondir"
|
||||
|
||||
autostart=$config/autostart/org.fcitx.Fcitx5.desktop
|
||||
service=$data/dbus-1/services/org.fcitx.Fcitx5.service
|
||||
mkdir -p "$(dirname "$autostart")" "$(dirname "$service")"
|
||||
|
||||
# Copy the system entry and change only the Exec line.
|
||||
sed -E "s#^Exec=/usr/bin/fcitx5\$#Exec=env FCITX_ADDON_DIRS=$dirs /usr/bin/fcitx5 -u m3ui#" \
|
||||
/etc/xdg/autostart/org.fcitx.Fcitx5.desktop >"$autostart"
|
||||
grep -q '^Exec=env FCITX_ADDON_DIRS=' "$autostart" ||
|
||||
{ echo "unexpected system autostart file, nothing changed" >&2; rm -f "$autostart"; exit 1; }
|
||||
|
||||
cat >"$service" <<SERVICE
|
||||
[D-BUS Service]
|
||||
Name=org.fcitx.Fcitx5
|
||||
Exec=/usr/bin/env FCITX_ADDON_DIRS=$dirs /usr/bin/fcitx5 -u m3ui
|
||||
SERVICE
|
||||
|
||||
echo "wrote $autostart"
|
||||
echo "wrote $service"
|
||||
echo "Apply now: systemctl --user daemon-reload && systemctl --user restart app-org.fcitx.Fcitx5@autostart.service"
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -66,6 +67,7 @@ FCITX_DEFINE_LOG_CATEGORY(m3ui_logcategory, "m3ui");
|
||||
using AccentColorDBusType = FCITX_STRING_TO_DBUS_TYPE("(ddd)");
|
||||
|
||||
M3UI::M3UI(Instance *instance) : instance_(instance) {
|
||||
loadBlockedWords();
|
||||
#ifdef ENABLE_DBUS
|
||||
if (auto *dbusAddon = dbus()) {
|
||||
dbus::VariantTypeRegistry::defaultRegistry()
|
||||
@@ -150,6 +152,34 @@ M3UI::M3UI(Instance *instance) : instance_(instance) {
|
||||
|
||||
M3UI::~M3UI() {}
|
||||
|
||||
std::filesystem::path M3UI::blockedWordsPath() const {
|
||||
return StandardPaths::global().userDirectory(StandardPathsType::PkgData) /
|
||||
"m3ui" / "blocked-words.txt";
|
||||
}
|
||||
|
||||
void M3UI::loadBlockedWords() {
|
||||
blockedWords_.clear();
|
||||
std::ifstream in(blockedWordsPath());
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
if (!line.empty()) {
|
||||
blockedWords_.insert(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M3UI::blockWord(const std::string &word) {
|
||||
if (word.empty() || word.find('\n') != std::string::npos ||
|
||||
!blockedWords_.insert(word).second) {
|
||||
return;
|
||||
}
|
||||
const auto path = blockedWordsPath();
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(path.parent_path(), ec);
|
||||
std::ofstream out(path, std::ios::app);
|
||||
out << word << '\n';
|
||||
}
|
||||
|
||||
void M3UI::reloadConfig() {
|
||||
readAsIni(config_, "conf/m3ui.conf");
|
||||
reloadTheme();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "fcitx-config/configuration.h"
|
||||
@@ -266,7 +267,20 @@ public:
|
||||
bool preferTextIcon() const;
|
||||
bool showLayoutNameInIcon() const;
|
||||
|
||||
// Words the user chose to forget. Forgetting in the Pinyin engine only
|
||||
// removes learned words, anything from a read only dictionary would come
|
||||
// back, so the window also hides these.
|
||||
bool hasBlockedWords() const { return !blockedWords_.empty(); }
|
||||
bool isBlocked(const std::string &word) const {
|
||||
return blockedWords_.contains(word);
|
||||
}
|
||||
void blockWord(const std::string &word);
|
||||
|
||||
private:
|
||||
void loadBlockedWords();
|
||||
std::filesystem::path blockedWordsPath() const;
|
||||
std::unordered_set<std::string> blockedWords_;
|
||||
|
||||
FCITX_ADDON_DEPENDENCY_LOADER(notificationitem, instance_->addonManager());
|
||||
FCITX_ADDON_EXPORT_FUNCTION(M3UI, labelIcon);
|
||||
FCITX_ADDON_EXPORT_FUNCTION(M3UI, preferTextIcon);
|
||||
|
||||
@@ -130,6 +130,16 @@ double springSettleSeconds(double stiffness, double damping) {
|
||||
return damping >= 1.0 ? 7.5 / w : 5.3 / (damping * w);
|
||||
}
|
||||
|
||||
// The engine's "forget" action, as shown in English or Chinese.
|
||||
bool isForgetAction(const std::string &text) {
|
||||
std::string lower = text;
|
||||
std::ranges::transform(lower, lower.begin(), [](unsigned char c) {
|
||||
return static_cast<char>(std::tolower(c));
|
||||
});
|
||||
return lower.find("forget") != std::string::npos ||
|
||||
text.find("\xe5\xbf\x98\xe8\xae\xb0") != std::string::npos; // 忘记
|
||||
}
|
||||
|
||||
double millisecondsSince(std::chrono::steady_clock::time_point start,
|
||||
std::chrono::steady_clock::time_point now) {
|
||||
return std::chrono::duration<double, std::milli>(now - start).count();
|
||||
@@ -419,6 +429,19 @@ std::pair<int, int> InputWindow::update(InputContext *inputContext) {
|
||||
auxUp = instance->outputFilter(inputContext, inputPanel.auxUp());
|
||||
auxDown = instance->outputFilter(inputContext, inputPanel.auxDown());
|
||||
candidateList = inputPanel.candidateList();
|
||||
// Drop forgotten words, removing from the engine's list keeps labels
|
||||
// and selection keys consistent with what is shown.
|
||||
if (candidateList && parent_->hasBlockedWords()) {
|
||||
if (auto *modifiable = candidateList->toModifiable()) {
|
||||
for (int i = modifiable->totalSize() - 1; i >= 0; i--) {
|
||||
if (parent_->isBlocked(modifiable->candidateFromAll(i)
|
||||
.text()
|
||||
.toString())) {
|
||||
modifiable->remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!inputPanel.overlayMessage().empty()) {
|
||||
auxUp = inputPanel.overlayMessage();
|
||||
}
|
||||
@@ -952,10 +975,12 @@ void InputWindow::click(int x, int y) {
|
||||
if (menuOpen_) {
|
||||
// Copy what is needed first, triggering an action rebuilds the list.
|
||||
int actionId = -1;
|
||||
std::string actionText;
|
||||
const auto candidateIndex = menuCandidate_;
|
||||
for (size_t i = 0; i < menuRegions_.size(); i++) {
|
||||
if (menuRegions_[i].contains(x, y)) {
|
||||
actionId = menuActions_[i].id();
|
||||
actionText = menuActions_[i].text();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -965,6 +990,11 @@ void InputWindow::click(int x, int y) {
|
||||
const auto *candidate =
|
||||
nthCandidateIgnorePlaceholder(*candidateList, candidateIndex);
|
||||
if (actionable && candidate) {
|
||||
// Block before the engine refreshes its list so the refreshed
|
||||
// list is already filtered.
|
||||
if (isForgetAction(actionText)) {
|
||||
parent_->blockWord(candidate->text().toString());
|
||||
}
|
||||
actionable->triggerAction(*candidate, actionId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user