From 3721145f2940de8306167bedd10ba2697216c741 Mon Sep 17 00:00:00 2001 From: xiepengfei Date: Tue, 31 Mar 2026 16:08:11 +0800 Subject: [PATCH] fix: Fix the clipboard tab key switching focus issue Fix the clipboard tab key switching focus issue Log: Fix the clipboard tab key switching focus issue pms: BUG-342481 --- dde-clipboard/iconbutton.cpp | 33 ++++++++++++++++-- dde-clipboard/iconbutton.h | 5 ++- dde-clipboard/itemdelegate.cpp | 28 +++++++++++---- dde-clipboard/itemdelegate.h | 1 + dde-clipboard/itemwidget.cpp | 6 ++++ dde-clipboard/itemwidget.h | 3 ++ dde-clipboard/mainwindow.cpp | 64 ++++++++++++++++++++++++++++++++++ dde-clipboard/mainwindow.h | 11 +++++- 8 files changed, 141 insertions(+), 10 deletions(-) diff --git a/dde-clipboard/iconbutton.cpp b/dde-clipboard/iconbutton.cpp index e9b51e55..501ad5c1 100644 --- a/dde-clipboard/iconbutton.cpp +++ b/dde-clipboard/iconbutton.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -55,7 +55,10 @@ void IconButton::paintEvent(QPaintEvent *event) QColor color; if (m_hasBackColor) { color = palette().color(QPalette::Base); - color.setAlpha(m_hover ? m_opacity : (m_opacity / 2)); + if (m_hasFocus) + color.setAlpha(m_opacity); + else + color.setAlpha(m_hover ? m_opacity : (m_opacity / 2)); } else { color = palette().color(QPalette::WindowText); color.setAlpha(m_hasFocus ? 80 : (m_hover ? 50 : 20)); @@ -154,3 +157,29 @@ void IconButton::leaveEvent(QEvent *event) return DWidget::leaveEvent(event); } + +void IconButton::focusInEvent(QFocusEvent *event) +{ + m_hasFocus = true; + update(); + DWidget::focusInEvent(event); +} + +void IconButton::focusOutEvent(QFocusEvent *event) +{ + m_hasFocus = false; + update(); + DWidget::focusOutEvent(event); +} + +void IconButton::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Enter || + event->key() == Qt::Key_Return || + event->key() == Qt::Key_Space) { + Q_EMIT clicked(); + event->accept(); + return; + } + DWidget::keyPressEvent(event); +} diff --git a/dde-clipboard/iconbutton.h b/dde-clipboard/iconbutton.h index 5abc2634..af64b0f7 100644 --- a/dde-clipboard/iconbutton.h +++ b/dde-clipboard/iconbutton.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -71,6 +71,9 @@ class IconButton : public DWidget virtual void leaveEvent(QEvent *event) override; virtual void resizeEvent(QResizeEvent *event) override; virtual QSize sizeHint() const override; + virtual void focusInEvent(QFocusEvent *event) override; + virtual void focusOutEvent(QFocusEvent *event) override; + virtual void keyPressEvent(QKeyEvent *event) override; }; #endif // ICONBUTTON_ diff --git a/dde-clipboard/itemdelegate.cpp b/dde-clipboard/itemdelegate.cpp index 9b9edbd9..d22382ef 100644 --- a/dde-clipboard/itemdelegate.cpp +++ b/dde-clipboard/itemdelegate.cpp @@ -59,12 +59,28 @@ bool ItemDelegate::eventFilter(QObject *obj, QEvent *event) if (QKeyEvent *keyEvent = dynamic_cast(event)) { switch (keyEvent->key()) { case Qt::Key_Tab: - case Qt::Key_Backtab: if (keyEvent->type() == QKeyEvent::KeyPress) { - //转变为特殊按键事件,表示切换内部‘焦点’,tab事件会被listview的viewport捕获 - QKeyEvent kEvent(QEvent::KeyPress, Qt::Key_0, Qt::NoModifier, "change focus"); - qApp->sendEvent(obj, &kEvent); - } - return true; + if (keyEvent->type() == QKeyEvent::KeyPress) { + ItemWidget *item = qobject_cast(obj); + if (item && item->closeButtonHasFocus()) { + Q_EMIT focusEscapeRequested(true); + return true; + } + //转变为特殊按键事件,表示切换内部’焦点’,tab事件会被listview的viewport捕获 + QKeyEvent kEvent(QEvent::KeyPress, Qt::Key_0, Qt::NoModifier, "change focus"); + qApp->sendEvent(obj, &kEvent); + } + return true; + case Qt::Key_Backtab: + if (keyEvent->type() == QKeyEvent::KeyPress) { + ItemWidget *item = qobject_cast(obj); + if (item && !item->closeButtonHasFocus()) { + Q_EMIT focusEscapeRequested(false); + return true; + } + QKeyEvent kEvent(QEvent::KeyPress, Qt::Key_0, Qt::NoModifier, "change focus"); + qApp->sendEvent(obj, &kEvent); + } + return true; default: break; } diff --git a/dde-clipboard/itemdelegate.h b/dde-clipboard/itemdelegate.h index dc47b8f9..6b44bf29 100644 --- a/dde-clipboard/itemdelegate.h +++ b/dde-clipboard/itemdelegate.h @@ -54,6 +54,7 @@ class ItemDelegate : public QStyledItemDelegate * \~chinese \brief 请求隐藏主窗口的信号 */ void hideWindow(); + void focusEscapeRequested(bool forward); }; #endif // LISTDELEGATE_H diff --git a/dde-clipboard/itemwidget.cpp b/dde-clipboard/itemwidget.cpp index f7965ca1..2e794d40 100644 --- a/dde-clipboard/itemwidget.cpp +++ b/dde-clipboard/itemwidget.cpp @@ -702,6 +702,12 @@ void ItemWidget::focusOutEvent(QFocusEvent *event) return DWidget::focusOutEvent(event); } +void ItemWidget::setCloseButtonFocusState(bool focus) +{ + m_closeFocus = focus; + Q_EMIT closeHasFocus(focus); +} + bool ItemWidget::eventFilter(QObject *watcher, QEvent *event) { if (watcher == qApp && event->type() == QEvent::ThemeChange) { diff --git a/dde-clipboard/itemwidget.h b/dde-clipboard/itemwidget.h index e394f955..9a14e3cb 100644 --- a/dde-clipboard/itemwidget.h +++ b/dde-clipboard/itemwidget.h @@ -69,6 +69,9 @@ class ItemWidget : public DWidget static QPixmap GetFileIcon(QString path); static QPixmap GetFileIcon(const FileIconData &data); + bool closeButtonHasFocus() const { return m_closeFocus; } + void setCloseButtonFocusState(bool focus); + Q_SIGNALS: void close(); /*! diff --git a/dde-clipboard/mainwindow.cpp b/dde-clipboard/mainwindow.cpp index 47a277f5..376b1377 100644 --- a/dde-clipboard/mainwindow.cpp +++ b/dde-clipboard/mainwindow.cpp @@ -23,6 +23,7 @@ #include #include "messagemanager.h" +#include "itemwidget.h" #define DOCK_TOP 0 #define DOCK_RIGHT 1 @@ -329,7 +330,9 @@ void MainWindow::initUI() m_clearButton->setFixedHeight(36); m_clearButton->setBackOpacity(200); m_clearButton->setRadius(8); + m_clearButton->setFocusPolicy(Qt::StrongFocus); m_clearButton->setVisible(false); + m_clearButton->installEventFilter(this); titleWidget->setFixedSize(WindowWidth, WindowTitleHeight); // Tips widget shows when data is copied, supports closing @@ -487,6 +490,29 @@ void MainWindow::initConnect() hideAni(); }); + connect(m_itemDelegate, &ItemDelegate::focusEscapeRequested, this, [this](bool forward) { + QModelIndex currentIdx = m_listview->currentIndex(); + int rowCount = m_model->data().size(); + + if (forward) { + // Tab: 尝试跳到下一个 item,如果是最后一个则跳到清除按钮 + int nextRow = currentIdx.isValid() ? currentIdx.row() + 1 : 0; + if (nextRow < rowCount) { + setFocusToItem(nextRow); + } else { + m_clearButton->setFocus(); + } + } else { + // Backtab: 尝试跳到上一个 item 的关闭按钮,如果是第一个则跳到清除按钮 + int prevRow = currentIdx.isValid() ? currentIdx.row() - 1 : rowCount - 1; + if (prevRow >= 0) { + setFocusToItem(prevRow, true); + } else { + m_clearButton->setFocus(); + } + } + }); + connect(m_daemonDockInter, &DBusDaemonDock::FrontendWindowRectChanged, this, &MainWindow::onFrontendWindowRectChanged, Qt::UniqueConnection); connect(m_daemonDockInter, &DBusDaemonDock::PositionChanged, this, &MainWindow::geometryChanged); @@ -677,3 +703,41 @@ bool MainWindow::event(QEvent *event) return DBlurEffectWidget::event(event); } + +void MainWindow::setFocusToItem(int row, bool focusCloseButton) +{ + QModelIndex idx = m_model->index(row, 0); + if (!idx.isValid()) + return; + + m_listview->setCurrentIndex(idx); + if (QWidget *w = m_listview->indexWidget(idx)) { + w->setFocus(); + if (focusCloseButton) { + if (ItemWidget *item = qobject_cast(w)) + item->setCloseButtonFocusState(true); + } + } +} + +bool MainWindow::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == m_clearButton && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Tab) { + // 从清除按钮按 Tab,跳到第一个 item + if (m_model->data().size() > 0) { + setFocusToItem(0); + return true; + } + } else if (keyEvent->key() == Qt::Key_Backtab) { + // 从清除按钮按 Backtab,跳到最后一个 item 的关闭按钮 + int lastRow = m_model->data().size() - 1; + if (lastRow >= 0) { + setFocusToItem(lastRow, true); + return true; + } + } + } + return DBlurEffectWidget::eventFilter(watched, event); +} diff --git a/dde-clipboard/mainwindow.h b/dde-clipboard/mainwindow.h index 20b8f21f..e7dcdeb2 100644 --- a/dde-clipboard/mainwindow.h +++ b/dde-clipboard/mainwindow.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -69,6 +69,7 @@ class MainWindow : public DBlurEffectWidget void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; bool event(QEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; signals: void OpacityChanged(double value) const; @@ -166,6 +167,14 @@ private Q_SLOTS: int getWidth() const { return this->width(); } int getX() const { return this->pos().x(); } + /*! + * \~chinese \name setFocusToItem + * \~chinese \brief 设置焦点到指定行的 item + * \~chinese \param row 行号 + * \~chinese \param focusCloseButton 是否将焦点设置到关闭按钮 + */ + void setFocusToItem(int row, bool focusCloseButton = false); + private: DBusDisplay *m_displayInter; DBusDaemonDock *m_daemonDockInter;