-
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,9 +25,11 @@ | |
| #include <QBoxLayout> | ||
| #include <QDebug> | ||
| #include <QMouseEvent> | ||
| #include <QScroller> | ||
| #include <QString> | ||
| #include <QToolButton> | ||
| #include <QCursor> | ||
| #include <QAbstractTextDocumentLayout> | ||
|
|
||
| SettingLabel::SettingLabel(QString text, QWidget *parent) | ||
| : QWidget(parent) | ||
|
|
@@ -253,6 +255,9 @@ | |
| m_airplaneModeWidget->setFixedWidth(ItemWidth); | ||
| m_mainLayout->addWidget(m_airplaneModeWidget); | ||
|
|
||
| // Install event filter to handle Enter event for link hover detection | ||
| m_airplaneModeLabel->installEventFilter(this); | ||
|
|
||
| QToolButton *disableIcon = new QToolButton(m_disableWidget); | ||
| disableIcon->setAttribute(Qt::WA_TransparentForMouseEvents); | ||
| disableIcon->setIcon(QIcon::fromTheme("bluetooth_disable")); | ||
|
|
@@ -373,3 +378,41 @@ | |
| m_minHeight = minHeight; | ||
| updateSize(); | ||
| } | ||
|
|
||
| bool BluetoothApplet::eventFilter(QObject *watched, QEvent *event) | ||
| { | ||
| // 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); | ||
|
Comment on lines
+386
to
+390
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| if (label) { | ||
| // Manual hit test using a temporary QTextDocument | ||
| QTextDocument doc; | ||
|
Comment on lines
+388
to
+393
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
|
||
| doc.setDefaultFont(label->font()); | ||
| doc.setMarkdown(label->text()); | ||
|
|
||
| // 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 | ||
| doc.setTextWidth(label->contentsRect().width()); | ||
|
|
||
| // Map mouse position to document coordinates | ||
| // Assuming text starts at contentsRect().topLeft() | ||
| QPoint textPos = mouseEvent->pos() - label->contentsRect().topLeft(); | ||
|
|
||
| const QString anchor = doc.documentLayout()->anchorAt(textPos); | ||
|
|
||
| if (!anchor.isEmpty()) { | ||
| m_airplaneModeLabel->setCursor(Qt::PointingHandCursor); | ||
| } else { | ||
| m_airplaneModeLabel->setCursor(Qt::ArrowCursor); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return QWidget::eventFilter(watched, 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
eventFilteradds non‑trivial complexity. You can keep the behavior but reduce complexity and per‑event cost by:eventFilterConcrete steps:
1. Cache a
QTextDocumentinstead of rebuilding on every moveAdd a member to
BluetoothApplet(or to a dedicated label subclass, see next section):Initialize/update it when the label text or size changes:
And the helper:
Now
eventFilterbecomes cheaper and simpler: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:Usage:
This keeps all existing behavior, but removes link‑hover complexity from
BluetoothAppletand avoids repeated document construction or casting in a genericeventFilter.