Skip to content

fix: sync keyboard layout between DCC and fcitx5#147

Merged
wyu71 merged 1 commit intolinuxdeepin:masterfrom
wyu71:master
Apr 23, 2026
Merged

fix: sync keyboard layout between DCC and fcitx5#147
wyu71 merged 1 commit intolinuxdeepin:masterfrom
wyu71:master

Conversation

@wyu71
Copy link
Copy Markdown
Contributor

@wyu71 wyu71 commented Apr 23, 2026

Add bidirectional synchronization between DCC keyboard layout settings and fcitx5 input method configuration.

添加DCC键盘布局与fcitx5输入法的双向同步功能。
DCC切换布局时自动同步到fcitx5(添加/排序/设置当前),
fcitx5输入法列表变化时同步第一个键盘布局到DCC。

Log: 添加DCC键盘布局与fcitx5输入法双向同步
PMS: BUG-357551
Influence: DCC键盘布局设置与fcitx5输入法配置保持一致,
切换/添加/删除键盘布局时自动同步,支持带变体的键盘布局。

Summary by Sourcery

Synchronize keyboard layouts between DCC and fcitx5 so that changes in either system are reflected in the other while avoiding sync loops.

New Features:

  • Add bidirectional synchronization between DCC keyboard layout selection and fcitx5 keyboard input methods, including support for variant layouts.
  • Automatically align fcitx5's current input method order and selection when the user changes or removes the current DCC keyboard layout.
  • Update DCC's keyboard layout list and current selection based on the first keyboard-type input method configured in fcitx5.

Enhancements:

  • Expose keyboard layout presence checks over all available layouts and switch keyboard model maps to return const references for more efficient access.
  • Introduce guards and pending state handling to prevent feedback loops and ensure reliable synchronization when the fcitx5 controller is not yet available.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 23, 2026

Reviewer's Guide

Implements bidirectional synchronization between DCC keyboard layouts and fcitx5 input methods by mapping layout keys, wiring KeyboardController signals to Fcitx5ConfigToolWorker, and updating fcitx5 IM lists and DCC layouts while guarding against feedback loops.

Sequence diagram for DCC layout change syncing to fcitx5

sequenceDiagram
    actor User
    participant DCC_UI
    participant KeyboardController
    participant Fcitx5ConfigToolWorkerPrivate
    participant IMConfig
    participant DbusController

    User->>DCC_UI: Select keyboard layout
    DCC_UI->>KeyboardController: setCurrentLayout(layoutKey)
    KeyboardController->>KeyboardController: m_worker->setLayout(layoutKey)
    KeyboardController-->>Fcitx5ConfigToolWorkerPrivate: currentLayoutSet(layoutKey)

    alt Controller not available
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: syncCurrentLayoutToFcitx5(layoutKey)
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: m_pendingLayoutKey = layoutKey
    else Controller available
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: syncCurrentLayoutToFcitx5(layoutKey)
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: SyncGuard sets m_syncInProgress = true
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: fcitxKey = dccLayoutToFcitxIMKey(layoutKey)
        Fcitx5ConfigToolWorkerPrivate->>IMConfig: imEntries()
        IMConfig-->>Fcitx5ConfigToolWorkerPrivate: entries
        alt Entry exists and not first
            Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: move entry to front
        else Entry missing
            Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: prepend new keyboard entry
        end
        Fcitx5ConfigToolWorkerPrivate->>IMConfig: setIMEntries(updatedEntries)
        Fcitx5ConfigToolWorkerPrivate->>IMConfig: emitChanged()
        Fcitx5ConfigToolWorkerPrivate->>IMConfig: save()
        Fcitx5ConfigToolWorkerPrivate->>DbusController: SetCurrentIM(fcitxKey)
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: SyncGuard resets m_syncInProgress = false
    end

    DbusController-->>IMConfig: InputMethodGroupsChanged
    IMConfig-->>Fcitx5ConfigToolWorkerPrivate: imListChanged()
    Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: m_syncInProgress prevents resync loop
Loading

Sequence diagram for fcitx5 IM list change syncing first keyboard to DCC

sequenceDiagram
    participant DbusController
    participant IMConfig
    participant Fcitx5ConfigToolWorkerPrivate
    participant KeyboardController

    DbusController-->>IMConfig: InputMethodGroupsChanged
    IMConfig-->>Fcitx5ConfigToolWorkerPrivate: imListChanged()

    alt Sync not in progress
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: syncFcitx5FirstKeyboardToDCC()
        Fcitx5ConfigToolWorkerPrivate->>IMConfig: imEntries()
        IMConfig-->>Fcitx5ConfigToolWorkerPrivate: entries
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: find first keyboard-* entry
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: firstKeyboardLayout = fcitxIMKeyToDccLayout(entry.key)
        Fcitx5ConfigToolWorkerPrivate->>KeyboardController: allLayoutsContains(firstKeyboardLayout)
        alt Layout not supported by DCC
            Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: return
        else Layout supported
            Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: SyncGuard sets m_syncInProgress = true
            Fcitx5ConfigToolWorkerPrivate->>KeyboardController: userLayoutsContains(firstKeyboardLayout)
            alt Not in user layouts
                Fcitx5ConfigToolWorkerPrivate->>KeyboardController: addUserLayout(firstKeyboardLayout)
            end
            Fcitx5ConfigToolWorkerPrivate->>KeyboardController: setCurrentLayout(firstKeyboardLayout)
            KeyboardController-->>Fcitx5ConfigToolWorkerPrivate: currentLayoutSet(firstKeyboardLayout)
            Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: syncCurrentLayoutToFcitx5(firstKeyboardLayout) guarded by SyncGuard
            Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: SyncGuard resets m_syncInProgress = false
        end
        alt Pending layout key exists
            Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: syncCurrentLayoutToFcitx5(m_pendingLayoutKey)
        end
    else Sync already in progress
        Fcitx5ConfigToolWorkerPrivate->>Fcitx5ConfigToolWorkerPrivate: Ignore imListChanged
    end
Loading

Class diagram for keyboard layout and fcitx5 sync structures

classDiagram
    class Fcitx5ConfigToolWorkerPrivate {
        -fcitx::kcm::Fcitx5ConfigProxy *configProxy
        -fcitx::kcm::Fcitx5AddonsProxy *addonsProxy
        -IMListModel *imListModel
        -dccV25::KeyboardController *keyboardController
        -bool m_syncInProgress
        -QString m_pendingLayoutKey
        +init()
        +initConnect()
        -syncCurrentLayoutToFcitx5(QString layoutKey)
        -syncLayoutDeletedFromFcitx5(QString layoutKey)
        -syncFcitx5FirstKeyboardToDCC()
    }

    class SyncGuard {
        -bool &flag
        +SyncGuard(bool &flag)
        +~SyncGuard()
        -SyncGuard(SyncGuard &other)
        -operator=(SyncGuard &other)
    }

    class KeyboardController {
        -KeyboardModel *m_model
        -KeyboardWorker *m_worker
        +QMap~QString,QString~ userLayouts() const
        +QString userLayoutAt(int index, bool isValue) const
        +bool userLayoutsContains(QString key)
        +bool allLayoutsContains(QString key) const
        +QSortFilterProxyModel *layoutSearchModel()
        +void addUserLayout(QString layout)
        +void deleteUserLayout(QString layout)
        +int layoutCount() const
        +void setCurrentLayout(QString key)
        +void layoutDeleted(QString key, bool isCurrentLayout)
        +void currentLayoutSet(QString key)
    }

    class KeyboardModel {
        -QMap~QString,QString~ m_userLayout
        -QMap~QString,QString~ m_allLayouts
        +QString curLayout() const
        +QString curLang() const
        +const QMap~QString,QString~ &userLayout() const
        +QMap~QString,QString~ kbLayout() const
        +const QMap~QString,QString~ &allLayout() const
    }

    class IMConfig {
        +fcitx::FcitxQtStringKeyValueList imEntries() const
        +void setIMEntries(fcitx::FcitxQtStringKeyValueList entries)
        +void emitChanged()
        +void save()
        +signal imListChanged()
    }

    class DbusController {
        +void SetCurrentIM(QString imKey)
        +signal InputMethodGroupsChanged()
    }

    Fcitx5ConfigToolWorkerPrivate --> KeyboardController : keyboardController
    Fcitx5ConfigToolWorkerPrivate --> IMConfig : imConfig
    Fcitx5ConfigToolWorkerPrivate --> DbusController : controller()
    Fcitx5ConfigToolWorkerPrivate ..> SyncGuard : uses
    KeyboardController --> KeyboardModel : m_model
    KeyboardController ..> Fcitx5ConfigToolWorkerPrivate : signals currentLayoutSet, layoutDeleted
    IMConfig ..> Fcitx5ConfigToolWorkerPrivate : signal imListChanged
    DbusController ..> IMConfig : signal InputMethodGroupsChanged
Loading

File-Level Changes

Change Details Files
Add bidirectional sync logic between DCC keyboard layouts and fcitx5 IM configuration, including key mapping helpers, sync functions, and signal wiring with reentrancy protection.
  • Introduce helper functions to convert between DCC layout keys (with ';' and optional variants) and fcitx5 keyboard IM keys, plus a utility to find entries by key.
  • Add a SyncGuard helper and m_syncInProgress/m_pendingLayoutKey members to prevent recursive updates during synchronization and handle pending layout updates when the controller is unavailable.
  • Connect KeyboardController signals (current layout set, layout deleted) and fcitx5 IM change signals to new private sync methods in Fcitx5ConfigToolWorkerPrivate.
  • Implement syncCurrentLayoutToFcitx5 to prepend or move the corresponding keyboard IM to the front of the fcitx5 IM list and set it as current via DBus.
  • Implement syncLayoutDeletedFromFcitx5 to remove the corresponding keyboard IM entry from fcitx5 and advance the current IM when the current layout is deleted in DCC.
  • Implement syncFcitx5FirstKeyboardToDCC to find the first keyboard IM in fcitx5, ensure it exists in available DCC layouts, add it to user layouts if needed, and set it as the current DCC layout.
src/dcc-fcitx5configtool/operation/fcitx5configtool.cpp
src/dcc-fcitx5configtool/operation/private/fcitx5configtool_p.h
Extend KeyboardController/KeyboardModel APIs and signals to support sync logic and avoid unnecessary container copies.
  • Add allLayoutsContains to KeyboardController to query the full layout map from the model, used to validate layouts before syncing from fcitx5.
  • Emit layoutDeleted and currentLayoutSet signals from KeyboardController when deleting and changing layouts so Fcitx5ConfigToolWorkerPrivate can react to those events.
  • Adjust KeyboardModel userLayout and allLayout accessors to return const references instead of copies for more efficient lookups and use them in KeyboardController::deleteUserLayout.
  • Wire layout deletion logic to compute a stable key and current-layout status before emitting layoutDeleted.
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardcontroller.cpp
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardcontroller.h
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardmodel.cpp
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardmodel.h
Update licensing headers to reflect the extended year range.
  • Adjust SPDX-FileCopyrightText year ranges in KeyboardController and KeyboardModel headers and implementation files.
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardcontroller.cpp
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardcontroller.h
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardmodel.cpp
src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardmodel.h
src/dcc-fcitx5configtool/operation/private/fcitx5configtool_p.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The newly introduced layoutDeleted and currentLayoutSet in KeyboardController are declared as slots but are used as signals (Q_EMIT and connect expecting signals); they should be moved to the signals section rather than public Q_SLOTS to match their intended usage.
  • In syncCurrentLayoutToFcitx5 and related sync functions, consider adding a brief comment near m_syncInProgress/SyncGuard explaining the reentrancy/dead-loop prevention strategy, as the interaction between DCC and fcitx5 callbacks is non-trivial and easy to regress in future changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The newly introduced `layoutDeleted` and `currentLayoutSet` in `KeyboardController` are declared as slots but are used as signals (`Q_EMIT` and `connect` expecting signals); they should be moved to the `signals` section rather than `public Q_SLOTS` to match their intended usage.
- In `syncCurrentLayoutToFcitx5` and related sync functions, consider adding a brief comment near `m_syncInProgress`/`SyncGuard` explaining the reentrancy/dead-loop prevention strategy, as the interaction between DCC and fcitx5 callbacks is non-trivial and easy to regress in future changes.

## Individual Comments

### Comment 1
<location path="src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardcontroller.h" line_range="99-100" />
<code_context>
     void keyDone(const QString &accels);
     void keyEvent(const QString &accels);

+    void layoutDeleted(const QString &key, bool isCurrentLayout);
+    void currentLayoutSet(const QString &key);
+
     void conflictTextChanged();
</code_context>
<issue_to_address>
**issue (bug_risk):** These should be declared as signals, not slots, to work with the new connect syntax.

`layoutDeleted` and `currentLayoutSet` are currently under `public Q_SLOTS:`, but they’re used with the function-pointer `connect` syntax and emitted via `Q_EMIT`. For this usage, Qt requires them to be declared as `signals`. Please move these to a `Q_SIGNALS:` section so moc treats them correctly and the code compiles with the new connect syntax.
</issue_to_address>

### Comment 2
<location path="src/dcc-fcitx5configtool/keyboard-layout/operation/keyboardcontroller.cpp" line_range="317-324" />
<code_context>

 void KeyboardController::deleteUserLayout(const QString &layout)
 {
+    const auto &userLayout = m_model->userLayout();
+    QString key = userLayout.key(layout, layout);
+    QString currentKey = userLayout.key(currentLayout());
+    bool isCur = (key == currentKey);
     m_worker->delUserLayout(layout);
+    Q_EMIT layoutDeleted(key, isCur);
 }

</code_context>
<issue_to_address>
**issue (bug_risk):** The use of QMap::key() suggests `layout` is treated as a value, which may conflict with other methods that treat it as a key.

`QMap::key(value, defaultKey)` looks up a key by value, but other APIs (e.g. `userLayoutsContains(const QString &key)`) treat the external `key` as the actual map key. If `layout` here is already the map key, using `userLayout.key(layout, layout)` is unnecessary and can even return a different key if a value matches another key. It also means `delUserLayout` still receives `layout`, not the derived `key`, which is inconsistent. Please verify whether `layout` at call sites is meant to be the map key or the value, and then align both `delUserLayout(...)` and the `layoutDeleted(...)` emission with that same notion of key.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@wyu71 wyu71 force-pushed the master branch 2 times, most recently from 7d15ad6 to 3c21cc0 Compare April 23, 2026 06:21
Add bidirectional synchronization between DCC keyboard layout
settings and fcitx5 input method configuration.

添加DCC键盘布局与fcitx5输入法的双向同步功能。
DCC切换布局时自动同步到fcitx5(添加/排序/设置当前),
fcitx5输入法列表变化时同步第一个键盘布局到DCC。

Log: 添加DCC键盘布局与fcitx5输入法双向同步
PMS: BUG-357551
Influence: DCC键盘布局设置与fcitx5输入法配置保持一致,
切换/添加/删除键盘布局时自动同步,支持带变体的键盘布局。
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这份代码主要实现了 DCC (Deepin Control Center) 的键盘布局与 Fcitx5 输入法框架之间的双向同步功能。下面是对代码的详细审查和改进建议:

1. 语法与逻辑审查

优点:

  • 同步锁机制 (SyncGuard):引入了 m_syncInProgress 标志位和 SyncGuard RAII 类,有效防止了 DCC 和 Fcitx5 之间因信号互相触发导致的死循环(无限递归调用)。这是一个很好的设计模式。
  • 版权年份更新:统一更新了版权年份至 2026。
  • 类型安全性KeyboardModel::userLayoutallLayout 的返回值从 QMap 改为 const QMap&,避免了不必要的拷贝,提高了性能。

潜在逻辑问题:

  • deleteUserLayout 中的 isCur 判断时机
    KeyboardController::deleteUserLayout 中,通过 currentLayout() 获取当前布局并判断 isCur。但是,m_worker->delUserLayout(layout) 被调用后,底层的 currentLayout 可能会发生变化(例如自动切换到下一个布局)。

    • 建议:目前的逻辑是先判断 isCur 再删除,然后发送信号。如果目的是通知 UI "刚刚删除的那个布局是不是当前的",这个逻辑是通的。但如果目的是"删除后当前布局是什么",则需要在删除后重新获取。根据信号名 layoutDeleted(key, isCurrentLayout),语义似乎是前者,所以逻辑基本正确,但需确保 UI 端正确理解这个 isCurrentLayout 是指"被删除项在删除前的状态"。
  • indexOfEntry 的线性查找
    indexOfEntry 函数使用了 for 循环进行线性查找。虽然 FcitxQtStringKeyValueList 通常不会特别大,但如果输入法列表非常长,这会是一个 O(N) 操作。

    • 建议:如果列表很大,可以考虑使用 QHash 建立索引,或者检查 FcitxQtStringKeyValueList 是否支持更高效的查找方式。但在当前场景下(输入法列表通常几十个),性能影响可忽略。

2. 代码质量

优点:

  • RAII 封装SyncGuard 封装得很好,禁用了拷贝构造和赋值操作,防止了意外行为。
  • Lambda 表达式使用:在 initConnect 中使用 Lambda 捕获 this 处理信号,代码紧凑且上下文清晰。

改进意见:

  • dccLayoutToFcitxIMKeyfcitxIMKeyToDccLayout 的健壮性
    这两个静态函数负责 key 的转换。

    • dccLayoutToFcitxIMKey:处理了 ; 替换为 - 以及末尾多余的 -。但如果输入 key 本身就是空字符串,结果会是 "keyboard-",这可能是一个无效的 key。
    • fcitxIMKeyToDccLayout:如果 imKey 不是以 "keyboard-" 开头,返回空字符串。
    • 建议:增加输入参数的非空校验,或者对转换后的结果进行有效性检查。例如,fcitxIMKeyToDccLayout 返回空字符串时,调用者(如 syncFcitx5FirstKeyboardToDCC)已经做了 isEmpty() 检查,这是好的。
  • syncFcitx5FirstKeyboardToDCC 的逻辑
    该函数遍历 Fcitx5 的 IM 列表,找到第一个键盘布局,然后同步到 DCC。

    • 问题:如果 Fcitx5 的第一个 IM 是非键盘类型(例如拼音输入法),代码会跳过它继续找,直到找到键盘类型。逻辑是合理的。
    • 潜在问题:如果 Fcitx5 的列表中没有键盘布局(只有 IME),firstKeyboardLayout 为空,函数直接返回。这是正确的处理。但如果 Fcitx5 有键盘布局,但该布局在 DCC 的 allLayouts 中不存在(例如系统刚更新,Fcitx5 支持了新布局但 DCC 的数据没更新),函数也会返回。
    • 建议:在 !keyboardController->allLayoutsContains(firstKeyboardLayout) 分支中,除了打印 Debug 日志外,可能需要考虑是否要触发一次 DCC 布局列表的刷新,或者明确告知用户该布局不可用。
  • syncCurrentLayoutToFcitx5 的逻辑

    • 逻辑:将 DCC 的当前布局同步到 Fcitx5。如果该布局已在 Fcitx5 列表中,将其移到首位;如果不在,添加到首位。
    • 问题if (existIndex > 0) 这里的判断。如果 existIndex == 0(即已经在首位),代码什么都不做,直接跳到最后的 SetCurrentIM。这是正确的。
    • 潜在问题:如果 existIndex > 0,代码执行 movesave。如果 existIndex < 0(不存在),代码执行 prependsave。最后都调用 SetCurrentIM
    • 建议:逻辑清晰,无需修改。

3. 代码性能

  • 返回值优化
    KeyboardModel::userLayoutallLayout 改为返回 const QMap<QString, QString> & 是非常好的优化,避免了数据拷贝。
  • 字符串操作
    dccLayoutToFcitxIMKeyfcitxIMKeyToDccLayout 涉及多次字符串查找和拼接。
    • stripped.replace(';', '-')QString::replace 会创建新字符串。
    • stripped.chop(1):原地修改。
    • 建议:性能影响极小,无需优化。

4. 代码安全

  • 空指针检查
    syncCurrentLayoutToFcitx5syncLayoutDeletedFromFcitx5syncFcitx5FirstKeyboardToDCC 中,都有对 dbusProvidercontroller 的空指针检查,这很好。
  • 信号槽连接中的 this 指针
    initConnect 中的 Lambda 表达式捕获了 this 指针。由于 Fcitx5ConfigToolWorkerPrivate 继承自 QObject,且连接类型默认为 AutoConnection,如果发送者和接收者在同一线程,则是直接调用;如果在不同线程,则是队列连接。这里看起来是在同一线程,所以是安全的。
    • 注意:如果未来这些对象被移动到不同线程,需要确保线程安全。
  • SyncGuard 的安全性
    禁用了拷贝构造和赋值,防止了 flag 被意外复制或释放,非常安全。

5. 详细改进建议

  1. deleteUserLayout 中的逻辑注释
    建议在 KeyboardController::deleteUserLayout 中添加注释,说明 isCur 是指"被删除的布局在删除前是否为当前布局",以避免后续维护者误解。

  2. fcitxIMKeyToDccLayout 的边界情况
    建议在 fcitxIMKeyToDccLayout 中处理 imKey"keyboard-" 的情况(虽然 dccLayoutToFcitxIMKey 目前不太可能生成这种 key,但为了健壮性)。如果 imKey"keyboard-"stripped 为空,dashPos 为 -1,返回 ";"。这可能不是一个有效的 DCC key。

    • 修改建议
      static QString fcitxIMKeyToDccLayout(const QString &imKey) {
          const QString prefix = QStringLiteral("keyboard-");
          if (!imKey.startsWith(prefix)) return QString();
          QString stripped = imKey.mid(prefix.length());
          if (stripped.isEmpty()) return QString(); // 增加空检查
          int dashPos = stripped.indexOf('-');
          if (dashPos < 0) {
              return stripped + ';';
          }
          return stripped.left(dashPos) + ';' + stripped.mid(dashPos + 1);
      }
  3. syncFcitx5FirstKeyboardToDCC 的用户体验
    当 Fcitx5 的布局在 DCC 中不存在时,目前只是打印日志。如果这种情况频繁发生(例如系统更新),用户可能会困惑为什么布局没有同步。

    • 建议:虽然这可能超出当前 diff 的范围,但值得记录为一个潜在的用户体验改进点。

总结

这份代码整体质量很高,逻辑清晰,使用了 RAII 模式来处理同步问题,并且对性能敏感的部分(如返回值)进行了优化。主要的改进点在于增强边界条件的处理和增加必要的注释以提高代码的可维护性。

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: lzwind, wyu71

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@wyu71 wyu71 merged commit 3d8ea0a into linuxdeepin:master Apr 23, 2026
12 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants