-
Notifications
You must be signed in to change notification settings - Fork 31
fix: optimize cursor behavior for bluetooth applet link #425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Use manual hit detection via QTextDocument in eventFilter to resolve unreliable linkHovered signal issues during vertical movement. Log: optimize cursor behavior for bluetooth applet link Pms: BUG-350071
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: yixinshark The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Reviewer's GuideImplements a custom eventFilter on the Bluetooth applet’s airplane mode label to manually detect link hover via QTextDocument hit-testing and update the mouse cursor reliably, working around issues with the standard linkHovered signal during vertical mouse movement. Sequence diagram for custom cursor handling via eventFiltersequenceDiagram
actor User
participant QEventSystem
participant BluetoothApplet
participant m_airplaneModeLabel
participant QTextDocument
User->>m_airplaneModeLabel: Move mouse
m_airplaneModeLabel->>QEventSystem: QMouseEvent MouseMove
QEventSystem->>BluetoothApplet: eventFilter(watched, event)
BluetoothApplet->>BluetoothApplet: check watched == m_airplaneModeLabel
BluetoothApplet->>m_airplaneModeLabel: get font(), text(), contentsRect()
BluetoothApplet->>QTextDocument: create and configure
BluetoothApplet->>QTextDocument: setDefaultFont(font)
BluetoothApplet->>QTextDocument: setMarkdown(text)
BluetoothApplet->>QTextDocument: setTextWidth(contentsRect.width)
BluetoothApplet->>QTextDocument: documentLayout.anchorAt(textPos)
QTextDocument-->>BluetoothApplet: anchor
alt anchor is not empty
BluetoothApplet->>m_airplaneModeLabel: setCursor(Qt::PointingHandCursor)
else anchor is empty
BluetoothApplet->>m_airplaneModeLabel: setCursor(Qt::ArrowCursor)
end
BluetoothApplet-->>QEventSystem: return QWidget::eventFilter(watched, event)
Class diagram for updated BluetoothApplet event filteringclassDiagram
class QWidget
class QObject
class BluetoothApplet {
+updateMinHeight(minHeight int) void
+updateSize() void
#eventFilter(watched QObject*, event QEvent*) bool
-m_scrollArea QScrollArea*
-m_contentWidget QWidget*
-m_airplaneModeWidget QWidget*
-m_airplaneModeLabel QLabel*
-m_minHeight int
}
QObject <|-- QWidget
QWidget <|-- BluetoothApplet
class QLabel
class QScrollArea
BluetoothApplet o-- QLabel : m_airplaneModeLabel
BluetoothApplet o-- QScrollArea : m_scrollArea
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this 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 3 issues, and left some high level feedback:
- Recreating a QTextDocument and recalculating layout on every MouseMove may be expensive; consider caching a prepared QTextDocument for m_airplaneModeLabel and only rebuilding it when the label text, font, or width changes.
- The comment mentions handling Enter events, but the eventFilter only special-cases Leave and MouseMove; if you need consistent behavior when the cursor first enters the label, consider explicitly handling QEvent::Enter (or hover events) as well.
- The hit detection always uses doc.setMarkdown(label->text()); if m_airplaneModeLabel might contain non-Markdown rich text or HTML in other contexts, you may need to branch based on the label’s text format to keep link detection consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Recreating a QTextDocument and recalculating layout on every MouseMove may be expensive; consider caching a prepared QTextDocument for m_airplaneModeLabel and only rebuilding it when the label text, font, or width changes.
- The comment mentions handling Enter events, but the eventFilter only special-cases Leave and MouseMove; if you need consistent behavior when the cursor first enters the label, consider explicitly handling QEvent::Enter (or hover events) as well.
- The hit detection always uses doc.setMarkdown(label->text()); if m_airplaneModeLabel might contain non-Markdown rich text or HTML in other contexts, you may need to branch based on the label’s text format to keep link detection consistent.
## Individual Comments
### Comment 1
<location> `plugins/dde-dock/bluetooth/componments/bluetoothapplet.cpp:388-393` </location>
<code_context>
+ if (watched == m_airplaneModeLabel) {
+ if (event->type() == QEvent::Leave) {
+ m_airplaneModeLabel->setCursor(Qt::ArrowCursor);
+ } else if (event->type() == QEvent::MouseMove) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
+ QLabel *label = qobject_cast<QLabel*>(watched);
+ if (label) {
+ // Manual hit test using a temporary QTextDocument
+ QTextDocument doc;
+ doc.setDefaultFont(label->font());
+ doc.setMarkdown(label->text());
</code_context>
<issue_to_address>
**suggestion (performance):** QTextDocument construction and markdown parsing on every mouse move can be expensive.
Since this runs on every mouse move, repeatedly creating a QTextDocument and reparsing the markdown may be costly. Please consider caching a prepared QTextDocument (or parsed content) that is updated only when the label text changes, and reuse it for hit testing instead of recreating it each time.
Suggested implementation:
```cpp
QLabel *label = qobject_cast<QLabel*>(watched);
if (label) {
// Manual hit test using a cached QTextDocument to avoid reparsing on every mouse move.
// The document is updated only when the label's text/font/width changes.
const QString currentText = label->text();
const QFont currentFont = label->font();
const qreal currentWidth = label->contentsRect().width();
if (m_airplaneModeDocDirty
|| m_cachedAirplaneModeText != currentText
|| m_cachedAirplaneModeFont != currentFont
|| !qFuzzyCompare(m_cachedAirplaneModeWidth, currentWidth)) {
m_airplaneModeDoc.setDefaultFont(currentFont);
m_airplaneModeDoc.setMarkdown(currentText);
m_airplaneModeDoc.setTextWidth(currentWidth);
m_cachedAirplaneModeText = currentText;
m_cachedAirplaneModeFont = currentFont;
m_cachedAirplaneModeWidth = currentWidth;
m_airplaneModeDocDirty = false;
}
QTextDocument &doc = m_airplaneModeDoc;
// Adjust for alignment and margins if necessary.
// DTipLabel/DLabel might have specific internal layouts, but assuming standard QLabel behavior:
// Width must be set for word wrap to match (already set when cache is updated)
// Map mouse position to document coordinates
// Assuming text starts at contentsRect().topLeft()
QPoint textPos = mouseEvent->pos() - label->contentsRect().topLeft();
```
To fully support this caching, the following changes are also needed elsewhere in the codebase:
1. **Add member variables to `BluetoothApplet` (likely in `bluetoothapplet.h`):**
```cpp
private:
QTextDocument m_airplaneModeDoc;
QString m_cachedAirplaneModeText;
QFont m_cachedAirplaneModeFont;
qreal m_cachedAirplaneModeWidth = 0.0;
bool m_airplaneModeDocDirty = true;
```
2. **Initialize state in the `BluetoothApplet` constructor (in `bluetoothapplet.cpp`):**
- Ensure `m_airplaneModeDocDirty` is set to `true` (if not already via in-class initializer).
- Optionally pre-fill the cache once `m_airplaneModeLabel` is constructed and its initial text is set.
3. **Mark the cache dirty when the label content changes:**
- If `m_airplaneModeLabel` (or its actual type, e.g. `DLabel`) emits `textChanged(const QString &)`, connect it to a lambda/slot that sets `m_airplaneModeDocDirty = true;`.
- If the label's font or size can change dynamically (e.g. on theme/scale changes), also mark the cache dirty in those code paths or rely on the width/font comparison already present.
Example connection in the constructor after `m_airplaneModeLabel` is created:
```cpp
connect(m_airplaneModeLabel, &QLabel::textChanged, this, [this]() {
m_airplaneModeDocDirty = true;
});
```
Adjust the signal type if `m_airplaneModeLabel` is not a plain `QLabel` and uses a different signal.
</issue_to_address>
### Comment 2
<location> `plugins/dde-dock/bluetooth/componments/bluetoothapplet.cpp:386-390` </location>
<code_context>
+{
+ // Use eventFilter because standard linkHovered signal is unreliable for vertical movement
+ if (watched == m_airplaneModeLabel) {
+ if (event->type() == QEvent::Leave) {
+ m_airplaneModeLabel->setCursor(Qt::ArrowCursor);
+ } else if (event->type() == QEvent::MouseMove) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
+ QLabel *label = qobject_cast<QLabel*>(watched);
+ if (label) {
+ // Manual hit test using a temporary QTextDocument
</code_context>
<issue_to_address>
**suggestion:** Redundant qobject_cast when watched is already known to be the target label.
Since this block only runs when `watched == m_airplaneModeLabel`, and `m_airplaneModeLabel` is (and should remain) a `QLabel`, the `qobject_cast<QLabel*>(watched)` and null-check aren’t needed. You can instead use `m_airplaneModeLabel` directly here and rely on that invariant, which simplifies the logic and avoids the extra RTTI cost.
</issue_to_address>
### Comment 3
<location> `plugins/dde-dock/bluetooth/componments/bluetoothapplet.cpp:382` </location>
<code_context>
updateSize();
}
+
+bool BluetoothApplet::eventFilter(QObject *watched, QEvent *event)
+{
+ // Use eventFilter because standard linkHovered signal is unreliable for vertical movement
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring the new eventFilter-based hover handling to cache the text document and/or move the logic into a dedicated label helper or subclass to avoid per-mouse-move reconstruction and generic casting.
The new `eventFilter` adds non‑trivial complexity. You can keep the behavior but reduce complexity and per‑event cost by:
1. **Avoiding document reconstruction on every mouse move**
2. **Avoiding repeated casting/branching in a generic `eventFilter`**
3. **Encapsulating the logic in the label itself (or at least in a small helper)**
Concrete steps:
---
### 1. Cache a `QTextDocument` instead of rebuilding on every move
Add a member to `BluetoothApplet` (or to a dedicated label subclass, see next section):
```cpp
// in BluetoothApplet
private:
std::unique_ptr<QTextDocument> m_airplaneModeDoc;
```
Initialize/update it when the label text or size changes:
```cpp
// after setting up m_airplaneModeLabel
m_airplaneModeLabel->installEventFilter(this);
updateAirplaneModeDoc();
connect(m_airplaneModeLabel, &QLabel::linkActivated, this, [this](const QString &) {
// if text changes dynamically, call updateAirplaneModeDoc();
});
connect(m_airplaneModeLabel, &QLabel::windowTitleChanged, this, [this]() {
updateAirplaneModeDoc();
});
connect(m_airplaneModeLabel, &QWidget::resizeEvent, this, [this]() {
updateAirplaneModeDoc();
});
```
And the helper:
```cpp
void BluetoothApplet::updateAirplaneModeDoc()
{
if (!m_airplaneModeLabel)
return;
if (!m_airplaneModeDoc)
m_airplaneModeDoc = std::make_unique<QTextDocument>();
m_airplaneModeDoc->setDefaultFont(m_airplaneModeLabel->font());
m_airplaneModeDoc->setMarkdown(m_airplaneModeLabel->text());
m_airplaneModeDoc->setTextWidth(m_airplaneModeLabel->contentsRect().width());
}
```
Now `eventFilter` becomes cheaper and simpler:
```cpp
bool BluetoothApplet::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_airplaneModeLabel) {
if (event->type() == QEvent::Leave) {
m_airplaneModeLabel->setCursor(Qt::ArrowCursor);
return true;
}
if (event->type() == QEvent::MouseMove && m_airplaneModeDoc) {
auto *mouseEvent = static_cast<QMouseEvent *>(event);
const QPoint textPos = mouseEvent->pos() - m_airplaneModeLabel->contentsRect().topLeft();
const QString anchor = m_airplaneModeDoc->documentLayout()->anchorAt(textPos);
m_airplaneModeLabel->setCursor(anchor.isEmpty() ? Qt::ArrowCursor
: Qt::PointingHandCursor);
return true;
}
}
return QWidget::eventFilter(watched, event);
}
```
This keeps your workaround but removes the per‑move document construction and most casting branching.
---
### 2. Optionally encapsulate in a small label subclass
If you want to go one step further and de‑mix concerns from `BluetoothApplet`, you can move the hover logic into a small subclass used only for this label:
```cpp
class HoverLinkLabel : public DLabel {
Q_OBJECT
public:
explicit HoverLinkLabel(QWidget *parent = nullptr)
: DLabel(parent)
{
setMouseTracking(true);
}
protected:
void resizeEvent(QResizeEvent *e) override
{
DLabel::resizeEvent(e);
updateDoc();
}
void setText(const QString &text) override
{
DLabel::setText(text);
updateDoc();
}
void leaveEvent(QEvent *e) override
{
setCursor(Qt::ArrowCursor);
DLabel::leaveEvent(e);
}
void mouseMoveEvent(QMouseEvent *event) override
{
if (m_doc) {
const QPoint textPos = event->pos() - contentsRect().topLeft();
const QString anchor = m_doc->documentLayout()->anchorAt(textPos);
setCursor(anchor.isEmpty() ? Qt::ArrowCursor : Qt::PointingHandCursor);
}
DLabel::mouseMoveEvent(event);
}
private:
void updateDoc()
{
if (!m_doc)
m_doc = std::make_unique<QTextDocument>();
m_doc->setDefaultFont(font());
m_doc->setMarkdown(text());
m_doc->setTextWidth(contentsRect().width());
}
std::unique_ptr<QTextDocument> m_doc;
};
```
Usage:
```cpp
m_airplaneModeLabel = new HoverLinkLabel(m_airplaneModeWidget);
m_airplaneModeLabel->setText(tr("Disable [Airplane Mode](#) first if you want to connect to a Bluetooth"));
m_airplaneModeLabel->setTextFormat(Qt::MarkdownText);
m_airplaneModeLabel->setWordWrap(true);
```
This keeps all existing behavior, but removes link‑hover complexity from `BluetoothApplet` and avoids repeated document construction or casting in a generic `eventFilter`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| } else if (event->type() == QEvent::MouseMove) { | ||
| QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); | ||
| QLabel *label = qobject_cast<QLabel*>(watched); | ||
| if (label) { | ||
| // Manual hit test using a temporary QTextDocument | ||
| QTextDocument doc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (performance): QTextDocument construction and markdown parsing on every mouse move can be expensive.
Since this runs on every mouse move, repeatedly creating a QTextDocument and reparsing the markdown may be costly. Please consider caching a prepared QTextDocument (or parsed content) that is updated only when the label text changes, and reuse it for hit testing instead of recreating it each time.
Suggested implementation:
QLabel *label = qobject_cast<QLabel*>(watched);
if (label) {
// Manual hit test using a cached QTextDocument to avoid reparsing on every mouse move.
// The document is updated only when the label's text/font/width changes.
const QString currentText = label->text();
const QFont currentFont = label->font();
const qreal currentWidth = label->contentsRect().width();
if (m_airplaneModeDocDirty
|| m_cachedAirplaneModeText != currentText
|| m_cachedAirplaneModeFont != currentFont
|| !qFuzzyCompare(m_cachedAirplaneModeWidth, currentWidth)) {
m_airplaneModeDoc.setDefaultFont(currentFont);
m_airplaneModeDoc.setMarkdown(currentText);
m_airplaneModeDoc.setTextWidth(currentWidth);
m_cachedAirplaneModeText = currentText;
m_cachedAirplaneModeFont = currentFont;
m_cachedAirplaneModeWidth = currentWidth;
m_airplaneModeDocDirty = false;
}
QTextDocument &doc = m_airplaneModeDoc;
// Adjust for alignment and margins if necessary.
// DTipLabel/DLabel might have specific internal layouts, but assuming standard QLabel behavior:
// Width must be set for word wrap to match (already set when cache is updated)
// Map mouse position to document coordinates
// Assuming text starts at contentsRect().topLeft()
QPoint textPos = mouseEvent->pos() - label->contentsRect().topLeft();
To fully support this caching, the following changes are also needed elsewhere in the codebase:
-
Add member variables to
BluetoothApplet(likely inbluetoothapplet.h):private: QTextDocument m_airplaneModeDoc; QString m_cachedAirplaneModeText; QFont m_cachedAirplaneModeFont; qreal m_cachedAirplaneModeWidth = 0.0; bool m_airplaneModeDocDirty = true;
-
Initialize state in the
BluetoothAppletconstructor (inbluetoothapplet.cpp):- Ensure
m_airplaneModeDocDirtyis set totrue(if not already via in-class initializer). - Optionally pre-fill the cache once
m_airplaneModeLabelis constructed and its initial text is set.
- Ensure
-
Mark the cache dirty when the label content changes:
- If
m_airplaneModeLabel(or its actual type, e.g.DLabel) emitstextChanged(const QString &), connect it to a lambda/slot that setsm_airplaneModeDocDirty = true;. - If the label's font or size can change dynamically (e.g. on theme/scale changes), also mark the cache dirty in those code paths or rely on the width/font comparison already present.
Example connection in the constructor afterm_airplaneModeLabelis created:
connect(m_airplaneModeLabel, &QLabel::textChanged, this, [this]() { m_airplaneModeDocDirty = true; });
Adjust the signal type if
m_airplaneModeLabelis not a plainQLabeland uses a different signal. - If
| if (event->type() == QEvent::Leave) { | ||
| m_airplaneModeLabel->setCursor(Qt::ArrowCursor); | ||
| } else if (event->type() == QEvent::MouseMove) { | ||
| QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); | ||
| QLabel *label = qobject_cast<QLabel*>(watched); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Redundant qobject_cast when watched is already known to be the target label.
Since this block only runs when watched == m_airplaneModeLabel, and m_airplaneModeLabel is (and should remain) a QLabel, the qobject_cast<QLabel*>(watched) and null-check aren’t needed. You can instead use m_airplaneModeLabel directly here and rely on that invariant, which simplifies the logic and avoids the extra RTTI cost.
| updateSize(); | ||
| } | ||
|
|
||
| bool BluetoothApplet::eventFilter(QObject *watched, QEvent *event) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider refactoring the new eventFilter-based hover handling to cache the text document and/or move the logic into a dedicated label helper or subclass to avoid per-mouse-move reconstruction and generic casting.
The new eventFilter adds non‑trivial complexity. You can keep the behavior but reduce complexity and per‑event cost by:
- Avoiding document reconstruction on every mouse move
- Avoiding repeated casting/branching in a generic
eventFilter - Encapsulating the logic in the label itself (or at least in a small helper)
Concrete steps:
1. Cache a QTextDocument instead of rebuilding on every move
Add a member to BluetoothApplet (or to a dedicated label subclass, see next section):
// in BluetoothApplet
private:
std::unique_ptr<QTextDocument> m_airplaneModeDoc;Initialize/update it when the label text or size changes:
// after setting up m_airplaneModeLabel
m_airplaneModeLabel->installEventFilter(this);
updateAirplaneModeDoc();
connect(m_airplaneModeLabel, &QLabel::linkActivated, this, [this](const QString &) {
// if text changes dynamically, call updateAirplaneModeDoc();
});
connect(m_airplaneModeLabel, &QLabel::windowTitleChanged, this, [this]() {
updateAirplaneModeDoc();
});
connect(m_airplaneModeLabel, &QWidget::resizeEvent, this, [this]() {
updateAirplaneModeDoc();
});And the helper:
void BluetoothApplet::updateAirplaneModeDoc()
{
if (!m_airplaneModeLabel)
return;
if (!m_airplaneModeDoc)
m_airplaneModeDoc = std::make_unique<QTextDocument>();
m_airplaneModeDoc->setDefaultFont(m_airplaneModeLabel->font());
m_airplaneModeDoc->setMarkdown(m_airplaneModeLabel->text());
m_airplaneModeDoc->setTextWidth(m_airplaneModeLabel->contentsRect().width());
}Now eventFilter becomes cheaper and simpler:
bool BluetoothApplet::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_airplaneModeLabel) {
if (event->type() == QEvent::Leave) {
m_airplaneModeLabel->setCursor(Qt::ArrowCursor);
return true;
}
if (event->type() == QEvent::MouseMove && m_airplaneModeDoc) {
auto *mouseEvent = static_cast<QMouseEvent *>(event);
const QPoint textPos = mouseEvent->pos() - m_airplaneModeLabel->contentsRect().topLeft();
const QString anchor = m_airplaneModeDoc->documentLayout()->anchorAt(textPos);
m_airplaneModeLabel->setCursor(anchor.isEmpty() ? Qt::ArrowCursor
: Qt::PointingHandCursor);
return true;
}
}
return QWidget::eventFilter(watched, event);
}This keeps your workaround but removes the per‑move document construction and most casting branching.
2. Optionally encapsulate in a small label subclass
If you want to go one step further and de‑mix concerns from BluetoothApplet, you can move the hover logic into a small subclass used only for this label:
class HoverLinkLabel : public DLabel {
Q_OBJECT
public:
explicit HoverLinkLabel(QWidget *parent = nullptr)
: DLabel(parent)
{
setMouseTracking(true);
}
protected:
void resizeEvent(QResizeEvent *e) override
{
DLabel::resizeEvent(e);
updateDoc();
}
void setText(const QString &text) override
{
DLabel::setText(text);
updateDoc();
}
void leaveEvent(QEvent *e) override
{
setCursor(Qt::ArrowCursor);
DLabel::leaveEvent(e);
}
void mouseMoveEvent(QMouseEvent *event) override
{
if (m_doc) {
const QPoint textPos = event->pos() - contentsRect().topLeft();
const QString anchor = m_doc->documentLayout()->anchorAt(textPos);
setCursor(anchor.isEmpty() ? Qt::ArrowCursor : Qt::PointingHandCursor);
}
DLabel::mouseMoveEvent(event);
}
private:
void updateDoc()
{
if (!m_doc)
m_doc = std::make_unique<QTextDocument>();
m_doc->setDefaultFont(font());
m_doc->setMarkdown(text());
m_doc->setTextWidth(contentsRect().width());
}
std::unique_ptr<QTextDocument> m_doc;
};Usage:
m_airplaneModeLabel = new HoverLinkLabel(m_airplaneModeWidget);
m_airplaneModeLabel->setText(tr("Disable [Airplane Mode](#) first if you want to connect to a Bluetooth"));
m_airplaneModeLabel->setTextFormat(Qt::MarkdownText);
m_airplaneModeLabel->setWordWrap(true);This keeps all existing behavior, but removes link‑hover complexity from BluetoothApplet and avoids repeated document construction or casting in a generic eventFilter.
deepin pr auto review这段代码主要实现了在 以下是对该代码的审查意见,包括语法逻辑、代码质量、代码性能和代码安全四个方面: 1. 语法逻辑
2. 代码质量
3. 代码性能
4. 代码安全
改进后的代码建议为了解决性能问题和提高健壮性,建议修改如下: 头文件: private:
// ... 其他成员 ...
QTextDocument m_tempDoc; // 使用成员变量缓存文档布局
bool m_isHoveringLink = false; // 缓存悬停状态源文件: void BluetoothApplet::initUi()
{
// ... 原有代码 ...
// 初始化文档对象,用于后续的点击检测
m_tempDoc.setDefaultFont(m_airplaneModeLabel->font());
// 注意:这里需要确保 m_airplaneModeLabel 已经被创建且有文本
// 如果文本是动态变化的,需要添加一个更新 m_tempDoc 的槽函数
updateTempDoc();
m_airplaneModeLabel->installEventFilter(this);
// ...
}
// 新增:更新文档内容的辅助函数
void BluetoothApplet::updateTempDoc()
{
if (m_airplaneModeLabel) {
m_tempDoc.setMarkdown(m_airplaneModeLabel->text());
// 宽度可能在 resize 事件中变化,建议也在 resizeEvent 中更新
m_tempDoc.setTextWidth(m_airplaneModeLabel->contentsRect().width());
}
}
bool BluetoothApplet::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_airplaneModeLabel) {
if (event->type() == QEvent::Leave) {
m_airplaneModeLabel->setCursor(Qt::ArrowCursor);
m_isHoveringLink = false;
} else if (event->type() == QEvent::MouseMove) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
// 确保文档宽度是最新的(处理窗口大小改变的情况)
if (m_tempDoc.textWidth() != m_airplaneModeLabel->contentsRect().width()) {
m_tempDoc.setTextWidth(m_airplaneModeLabel->contentsRect().width());
}
// 计算相对位置
// 注意:这里依然假设文本左对齐。如果是居中,需要计算偏移量。
// 对于 DTipLabel/DLabel,通常需要根据 alignment 调整 textPos
int offsetX = 0;
if (m_airplaneModeLabel->alignment() & Qt::AlignHCenter) {
offsetX = (m_airplaneModeLabel->contentsRect().width() - m_tempDoc.idealWidth()) / 2;
} else if (m_airplaneModeLabel->alignment() & Qt::AlignRight) {
offsetX = m_airplaneModeLabel->contentsRect().width() - m_tempDoc.idealWidth();
}
QPoint textPos = mouseEvent->pos() - m_airplaneModeLabel->contentsRect().topLeft();
textPos.setX(textPos.x() - offsetX); // 应用对齐偏移
const QString anchor = m_tempDoc.documentLayout()->anchorAt(textPos);
const bool isHovering = !anchor.isEmpty();
// 仅在状态改变时更新光标,减少系统调用
if (isHovering != m_isHoveringLink) {
m_airplaneModeLabel->setCursor(isHovering ? Qt::PointingHandCursor : Qt::ArrowCursor);
m_isHoveringLink = isHovering;
}
}
} else if (watched == m_airplaneModeLabel && event->type() == QEvent::Resize) {
// 如果 Label 大小改变,需要更新文档宽度
updateTempDoc();
}
return QWidget::eventFilter(watched, event);
}总结主要改进点:
|
|
TAG Bot New tag: 2.0.25 |
Use manual hit detection via QTextDocument in eventFilter to resolve unreliable linkHovered signal issues during vertical movement.
Log: optimize cursor behavior for bluetooth applet link
Pms: BUG-350071
Summary by Sourcery
Improve hover-based cursor feedback for the Bluetooth applet’s airplane mode link using manual hit detection.
Bug Fixes:
Enhancements: