Improve mouse wheel scrolling in configuration dialogs

Enable mouse wheel scrolling throughout scroll areas instead of
requiring hover over scrollbars. Add event filter to forward wheel
events to scrollbars. Fix Input tab scrolling by only consuming
wheel events in ConfigureInputPlayer when actively mapping inputs.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-11-12 16:41:57 +10:00
parent d3ae46a353
commit 16f815a7b9
2 changed files with 111 additions and 3 deletions

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "citron/configuration/configure_dialog.h"
#include <cmath>
#include <memory>
#include <QApplication>
#include <QButtonGroup>
@@ -10,8 +11,10 @@
#include <QPushButton>
#include <QScreen>
#include <QScrollArea>
#include <QScrollBar>
#include <QString>
#include <QTimer>
#include <QWheelEvent>
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_enums.h"
@@ -39,6 +42,50 @@
#include "citron/theme.h"
#include "citron/uisettings.h"
// Event filter class to forward wheel events to scroll area's scrollbar
class ScrollAreaWheelEventFilter : public QObject {
public:
explicit ScrollAreaWheelEventFilter(QScrollArea* scroll_area, QObject* parent = nullptr,
bool prefer_horizontal = false)
: QObject(parent), scroll_area_(scroll_area), prefer_horizontal_(prefer_horizontal) {}
protected:
bool eventFilter(QObject* obj, QEvent* event) override {
if (event->type() == QEvent::Wheel && scroll_area_) {
auto* wheel_event = static_cast<QWheelEvent*>(event);
const QPoint angle_delta = wheel_event->angleDelta();
// Determine which scrollbar to use based on scroll direction and preference
bool use_horizontal = prefer_horizontal_ || (std::abs(angle_delta.x()) > std::abs(angle_delta.y()));
if (use_horizontal) {
// Try horizontal scrolling first
if (scroll_area_->horizontalScrollBar()->maximum() > 0) {
QApplication::sendEvent(scroll_area_->horizontalScrollBar(), wheel_event);
return true;
}
}
// Try vertical scrolling
if (scroll_area_->verticalScrollBar()->maximum() > 0) {
QApplication::sendEvent(scroll_area_->verticalScrollBar(), wheel_event);
return true;
}
// If vertical didn't work and we didn't try horizontal, try it now
if (!use_horizontal && scroll_area_->horizontalScrollBar()->maximum() > 0) {
QApplication::sendEvent(scroll_area_->horizontalScrollBar(), wheel_event);
return true;
}
}
return QObject::eventFilter(obj, event);
}
private:
QScrollArea* scroll_area_;
bool prefer_horizontal_;
};
static QScrollArea* CreateScrollArea(QWidget* widget) {
auto* scroll_area = new QScrollArea();
scroll_area->setWidget(widget);
@@ -46,6 +93,11 @@ static QScrollArea* CreateScrollArea(QWidget* widget) {
scroll_area->setFrameShape(QFrame::NoFrame);
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
// Install event filter on the widget to forward wheel events to scrollbar
auto* filter = new ScrollAreaWheelEventFilter(scroll_area, scroll_area);
widget->installEventFilter(filter);
return scroll_area;
}
@@ -113,6 +165,11 @@ rainbow_timer{new QTimer(this)} {
ui->setupUi(this);
// Enable wheel event scrolling on the top button scroll area (horizontal scrolling)
auto* top_button_filter = new ScrollAreaWheelEventFilter(ui->topButtonScrollArea, this, true);
ui->topButtonScrollArea->widget()->installEventFilter(top_button_filter);
ui->topButtonScrollArea->installEventFilter(top_button_filter);
last_palette_text_color = qApp->palette().color(QPalette::WindowText);
if (!UISettings::values.configure_dialog_geometry.isEmpty()) {

View File

@@ -5,12 +5,16 @@
#include <algorithm>
#include <memory>
#include <utility>
#include <QApplication>
#include <QGridLayout>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QScrollArea>
#include <QScrollBar>
#include <QTimer>
#include <QWheelEvent>
#include "common/assert.h"
#include "common/param_package.h"
#include "configuration/qt_config.h"
@@ -1536,9 +1540,56 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
}
void ConfigureInputPlayer::wheelEvent(QWheelEvent* event) {
const int x = event->angleDelta().x();
const int y = event->angleDelta().y();
input_subsystem->GetMouse()->MouseWheelChange(x, y);
// Only handle wheel events for input mapping when in input mapping mode
// Otherwise, forward to the scroll area for scrolling
if (input_setter) {
const int x = event->angleDelta().x();
const int y = event->angleDelta().y();
input_subsystem->GetMouse()->MouseWheelChange(x, y);
event->accept();
} else {
// Find the parent scroll area and forward the event to its scrollbar
// The widget hierarchy is: ConfigureInputPlayer -> QWidget (tab) -> QTabWidget ->
// ConfigureInput -> QWidget (viewport) -> QScrollArea
QWidget* current = this;
QScrollArea* scroll_area = nullptr;
// Traverse up the parent chain to find the scroll area
while (current) {
// Check if current widget is a scroll area
if (auto* sa = qobject_cast<QScrollArea*>(current)) {
scroll_area = sa;
break;
}
// Check if current widget's parent is a scroll area (for viewport case)
QWidget* parent = current->parentWidget();
if (parent) {
if (auto* sa = qobject_cast<QScrollArea*>(parent)) {
scroll_area = sa;
break;
}
}
current = parent;
}
if (scroll_area) {
// Forward to vertical scrollbar if available
if (scroll_area->verticalScrollBar()->maximum() > 0) {
QApplication::sendEvent(scroll_area->verticalScrollBar(), event);
event->accept();
return;
}
// Otherwise try horizontal scrollbar
if (scroll_area->horizontalScrollBar()->maximum() > 0) {
QApplication::sendEvent(scroll_area->horizontalScrollBar(), event);
event->accept();
return;
}
}
// If no scroll area found, let the event propagate normally
event->ignore();
}
}
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {