Skip to content

Commit

Permalink
Merge pull request #11663 from ronso0/pref-scroll-safeguard
Browse files Browse the repository at this point in the history
Preferences: use helper for input widget scroll safeguard
  • Loading branch information
daschuer committed Jun 30, 2023
2 parents e0735c0 + 3348692 commit faeebc2
Show file tree
Hide file tree
Showing 24 changed files with 302 additions and 517 deletions.
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgprefautodj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ DlgPrefAutoDJ::DlgPrefAutoDJ(QWidget* pParent,
QOverload<int>::of(&QSpinBox::valueChanged),
this,
&DlgPrefAutoDJ::slotSetRandomQueueMin);

setScrollSafeGuardForAllInputWidgets(this);
}

DlgPrefAutoDJ::~DlgPrefAutoDJ() {
Expand Down
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgprefbeats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ DlgPrefBeats::DlgPrefBeats(QWidget* parent, UserSettingsPointer pConfig)
&QCheckBox::stateChanged,
this,
&DlgPrefBeats::slotReanalyzeImportedChanged);

setScrollSafeGuard(comboBoxBeatPlugin);
}

DlgPrefBeats::~DlgPrefBeats() {
Expand Down
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgprefbroadcast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent,
&QCheckBox::stateChanged,
this,
&DlgPrefBroadcast::enableCustomMetadataChanged);

setScrollSafeGuardForAllInputWidgets(this);
}

DlgPrefBroadcast::~DlgPrefBroadcast() {
Expand Down
8 changes: 3 additions & 5 deletions src/preferences/dialog/dlgprefcolors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,15 @@ DlgPrefColors::DlgPrefColors(
this,
&DlgPrefColors::slotReplaceCueColorClicked);

loadSettings();
setScrollSafeGuardForAllInputWidgets(this);

slotUpdate();
}

DlgPrefColors::~DlgPrefColors() {
}

void DlgPrefColors::slotUpdate() {
loadSettings();
}

void DlgPrefColors::loadSettings() {
comboBoxHotcueColors->clear();
comboBoxTrackColors->clear();
for (const auto& palette : qAsConst(mixxx::PredefinedColorPalettes::kPalettes)) {
Expand Down
3 changes: 1 addition & 2 deletions src/preferences/dialog/dlgprefcolors.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class DlgPrefColors : public DlgPreferencePage, public Ui::DlgPrefColorsDlg {

public slots:
/// Called when the preference dialog (not this page) is shown to the user.
/// Loads the config keys and sets the widgets in the dialog to match
void slotUpdate() override;
/// Called when the user clicks the global "Apply" button.
void slotApply() override;
Expand All @@ -43,8 +44,6 @@ class DlgPrefColors : public DlgPreferencePage, public Ui::DlgPrefColorsDlg {
void slotEditHotcuePaletteClicked();

private:
/// Loads the config keys and sets the widgets in the dialog to match
void loadSettings();
void openColorPaletteEditor(
const QString& paletteName,
bool editHotcuePalette);
Expand Down
68 changes: 68 additions & 0 deletions src/preferences/dialog/dlgpreferencepage.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#include "preferences/dialog/dlgpreferencepage.h"

#include <QApplication>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include <QLayout>
#include <QSlider>
#include <QSpinBox>

#include "defs_urls.h"
#include "moc_dlgpreferencepage.cpp"

Expand All @@ -13,3 +21,63 @@ DlgPreferencePage::~DlgPreferencePage() {
QUrl DlgPreferencePage::helpUrl() const {
return QUrl();
}

void DlgPreferencePage::setScrollSafeGuardForAllInputWidgets(QObject* obj) {
// Set the focus policy to for scrollable input widgets and connect them
// to the custom event filter
for (auto* ch : obj->children()) {
// children() does not descend into QGroupBox,
// so we need to do it manually
QGroupBox* gBox = qobject_cast<QGroupBox*>(ch);
if (gBox) {
setScrollSafeGuardForAllInputWidgets(gBox);
continue;
}

QComboBox* combo = qobject_cast<QComboBox*>(ch);
if (combo) {
setScrollSafeGuard(combo);
continue;
}
QSpinBox* spin = qobject_cast<QSpinBox*>(ch);
if (spin) {
setScrollSafeGuard(spin);
continue;
}
QDoubleSpinBox* spinDouble = qobject_cast<QDoubleSpinBox*>(ch);
if (spinDouble) {
setScrollSafeGuard(spinDouble);
continue;
}
QSlider* slider = qobject_cast<QSlider*>(ch);
if (slider) {
setScrollSafeGuard(slider);
continue;
}
}
}

void DlgPreferencePage::setScrollSafeGuard(QWidget* pWidget) {
pWidget->setFocusPolicy(Qt::StrongFocus);
pWidget->installEventFilter(this);
}

bool DlgPreferencePage::eventFilter(QObject* obj, QEvent* e) {
if (e->type() == QEvent::Wheel) {
// Reject scrolling only if widget is unfocused.
// Object to widget cast is needed to check the focus state.
QComboBox* combo = qobject_cast<QComboBox*>(obj);
QSpinBox* spin = qobject_cast<QSpinBox*>(obj);
QDoubleSpinBox* spinDbl = qobject_cast<QDoubleSpinBox*>(obj);
QSlider* slider = qobject_cast<QSlider*>(obj);
if ((combo && !combo->hasFocus()) ||
(spin && !spin->hasFocus()) ||
(spinDbl && !spinDbl->hasFocus()) ||
(slider && !slider->hasFocus())) {
QApplication::sendEvent(qobject_cast<QObject*>(layout()), e);
// QApplication::sendEvent(layout()->parent(), e); ??
return true;
}
}
return QObject::eventFilter(obj, e);
}
8 changes: 8 additions & 0 deletions src/preferences/dialog/dlgpreferencepage.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ class DlgPreferencePage : public QWidget {
/// overriding this. The default implementation returns an invalid QUrl.
virtual QUrl helpUrl() const;

void setScrollSafeGuardForAllInputWidgets(QObject* obj);
/// Avoid undesired value changes when scrolling a preferences page while
/// the pointer is above an input widget (QSpinBox, QComboBox, QSlider):
/// * set the focus policy to Qt::StrongFocus (focusable by click & tab key)
/// * forward wheel events to the top-level layout
void setScrollSafeGuard(QWidget* pWidget);
bool eventFilter(QObject* obj, QEvent* e);

QColor m_pLinkColor;

public slots:
Expand Down
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgprefinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ DlgPrefInterface::DlgPrefInterface(
slotSetTooltips();
});

setScrollSafeGuardForAllInputWidgets(this);

slotUpdate();
}

Expand Down
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgprefkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ DlgPrefKey::DlgPrefKey(QWidget* parent, UserSettingsPointer pConfig)
// Connections
connect(plugincombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &DlgPrefKey::pluginSelected);
setScrollSafeGuard(plugincombo);

connect(banalyzerenabled, &QCheckBox::stateChanged,
this, &DlgPrefKey::analyzerEnabled);
connect(bfastAnalysisEnabled, &QCheckBox::stateChanged,
Expand Down
25 changes: 1 addition & 24 deletions src/preferences/dialog/dlgpreflibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,37 +134,14 @@ DlgPrefLibrary::DlgPrefLibrary(
this,
&DlgPrefLibrary::slotSyncTrackMetadataToggled);

// Avoid undesired spinbox value changes while scrolling the preferences page
setFocusPolicyInstallEventFilter(spinbox_bpm_precision);
setFocusPolicyInstallEventFilter(spinbox_history_track_duplicate_distance);
setFocusPolicyInstallEventFilter(spinbox_history_min_tracks_to_keep);
setFocusPolicyInstallEventFilter(spinBoxRowHeight);
setFocusPolicyInstallEventFilter(searchDebouncingTimeoutSpinBox);
setScrollSafeGuardForAllInputWidgets(this);

// Initialize the controls after all slots have been connected
slotUpdate();
}

DlgPrefLibrary::~DlgPrefLibrary() = default;

void DlgPrefLibrary::setFocusPolicyInstallEventFilter(QSpinBox* box) {
box->setFocusPolicy(Qt::StrongFocus);
box->installEventFilter(this);
}

// Catch scroll events over spinboxes and pass them to the scroll area instead.
bool DlgPrefLibrary::eventFilter(QObject* obj, QEvent* e) {
if (e->type() == QEvent::Wheel) {
// Reject scrolling only if widget is unfocused.
// Object to widget cast is needed to check the focus state.
QSpinBox* spin = qobject_cast<QSpinBox*>(obj);
if (spin && !spin->hasFocus()) {
QApplication::sendEvent(verticalLayout, e);
return true;
}
}
return QObject::eventFilter(obj, e);
}
void DlgPrefLibrary::slotShow() {
m_bAddedDirectory = false;
}
Expand Down
2 changes: 0 additions & 2 deletions src/preferences/dialog/dlgpreflibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ class DlgPrefLibrary : public DlgPreferencePage, public Ui::DlgPrefLibraryDlg {
void slotSeratoMetadataExportClicked(bool);

private:
void setFocusPolicyInstallEventFilter(QSpinBox* box);
bool eventFilter(QObject* object, QEvent* event) override;
void initializeDirList();
void setLibraryFont(const QFont& font);
void updateSearchLineEditHistoryOptions();
Expand Down
44 changes: 5 additions & 39 deletions src/preferences/dialog/dlgprefmixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,6 @@ DlgPrefMixer::DlgPrefMixer(
connect(radioButtonAdditive, &QRadioButton::clicked, this, &DlgPrefMixer::slotUpdate);
connect(radioButtonConstantPower, &QRadioButton::clicked, this, &DlgPrefMixer::slotUpdate);

// Set the focus policy for comboboxes and sliders and connect them to the
// custom event filter. See eventFilter() for details.
// Deck EQ & QuickEffect comboxboxes are set up accordingly in slotNumDecksChanged(),
// main EQ sliders in slotMainEqEffectChanged()
SliderHiEQ->setFocusPolicy(Qt::StrongFocus);
SliderHiEQ->installEventFilter(this);
SliderLoEQ->setFocusPolicy(Qt::StrongFocus);
SliderLoEQ->installEventFilter(this);
comboBoxMainEq->setFocusPolicy(Qt::StrongFocus);
comboBoxMainEq->installEventFilter(this);
// Don't allow the xfader graph getting keyboard focus
graphicsViewXfader->setFocusPolicy(Qt::NoFocus);

Expand Down Expand Up @@ -158,6 +148,8 @@ DlgPrefMixer::DlgPrefMixer(

setUpMainEQ();

setScrollSafeGuardForAllInputWidgets(this);

slotUpdate();
slotApply();
}
Expand All @@ -172,25 +164,6 @@ DlgPrefMixer::~DlgPrefMixer() {
m_deckQuickEffectSelectors.clear();
}

// Catch scroll events and filter them if they addressed an unfocused combobox or
// silder and send them to the scroll area instead.
// This avoids undesired value changes of unfocused widget when scrolling the page.
// Values can be changed only by explicit selection, dragging sliders and with
// Up/Down (Left/Right respectively), PageUp/PageDown as well as Home/End keys.
bool DlgPrefMixer::eventFilter(QObject* obj, QEvent* e) {
if (e->type() == QEvent::Wheel) {
// Reject scrolling only if widget is unfocused.
// Object to widget cast is needed to check the focus state.
QComboBox* combo = qobject_cast<QComboBox*>(obj);
QSlider* slider = qobject_cast<QSlider*>(obj);
if ((combo && !combo->hasFocus()) || (slider && !slider->hasFocus())) {
QApplication::sendEvent(verticalLayout, e);
return true;
}
}
return QObject::eventFilter(obj, e);
}

void DlgPrefMixer::slotNumDecksChanged(double numDecks) {
int oldDecks = m_deckEqEffectSelectors.size();
while (m_deckEqEffectSelectors.size() < static_cast<int>(numDecks)) {
Expand All @@ -200,25 +173,21 @@ void DlgPrefMixer::slotNumDecksChanged(double numDecks) {

// Create the drop down list for deck EQs
QComboBox* pEqComboBox = new QComboBox(this);
// Ignore scroll events if combobox is not focused.
// See eventFilter() for details.
pEqComboBox->setFocusPolicy(Qt::StrongFocus);
pEqComboBox->installEventFilter(this);
m_deckEqEffectSelectors.append(pEqComboBox);
connect(pEqComboBox,
QOverload<int>::of(&QComboBox::currentIndexChanged),
this,
&DlgPrefMixer::slotEffectChangedOnDeck);
setScrollSafeGuard(pEqComboBox);

// Create the drop down list for Quick Effects
QComboBox* pQuickEffectComboBox = new QComboBox(this);
pQuickEffectComboBox->setFocusPolicy(Qt::StrongFocus);
pQuickEffectComboBox->installEventFilter(this);
m_deckQuickEffectSelectors.append(pQuickEffectComboBox);
connect(pQuickEffectComboBox,
QOverload<int>::of(&QComboBox::currentIndexChanged),
this,
&DlgPrefMixer::slotQuickEffectChangedOnDeck);
setScrollSafeGuard(pQuickEffectComboBox);

QString deckGroupName = PlayerManager::groupForDeck(deckNo - 1);
QString unitGroup = QuickEffectChain::formatEffectChainGroup(deckGroupName);
Expand Down Expand Up @@ -980,10 +949,7 @@ void DlgPrefMixer::slotMainEqEffectChanged(int effectIndex) {
slider->setMinimumHeight(90);
// Set the index as a property because we need it inside slotUpdateFilter()
slider->setProperty("index", QVariant(i));
// Ignore scroll events if slider is not focused.
// See eventFilter() for details.
slider->setFocusPolicy(Qt::StrongFocus);
slider->installEventFilter(this);
setScrollSafeGuard(slider);
slidersGridLayout->addWidget(slider, 1, i + 1, Qt::AlignCenter);
m_mainEQSliders.append(slider);
// catch drag event
Expand Down
2 changes: 0 additions & 2 deletions src/preferences/dialog/dlgprefmixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,6 @@ class DlgPrefMixer : public DlgPreferencePage, public Ui::DlgPrefMixerDlg {

void applySelectionsToDecks();

bool eventFilter(QObject* obj, QEvent* e) override;

UserSettingsPointer m_pConfig;

QGraphicsScene* m_pxfScene;
Expand Down
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgprefmodplug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ DlgPrefModplug::DlgPrefModplug(QWidget *parent,
m_pLinkColor,
"OpenMPT manual",
"http://wiki.openmpt.org/Manual:_Setup/Player")));

setScrollSafeGuardForAllInputWidgets(this);
}

DlgPrefModplug::~DlgPrefModplug() {
Expand Down
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgprefrecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ DlgPrefRecord::DlgPrefRecord(QWidget* parent, UserSettingsPointer pConfig)
comboBoxSplitting->setCurrentIndex(4);
}

setScrollSafeGuard(comboBoxSplitting);

// Do the one-time connection of signals here.
connect(SliderQuality, &QAbstractSlider::valueChanged, this, &DlgPrefRecord::slotSliderQuality);
connect(SliderQuality, &QAbstractSlider::sliderMoved, this, &DlgPrefRecord::slotSliderQuality);
Expand Down
4 changes: 4 additions & 0 deletions src/preferences/dialog/dlgprefreplaygain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ DlgPrefReplayGain::DlgPrefReplayGain(QWidget* parent, UserSettingsPointer pConfi
&QAbstractSlider::sliderReleased,
this,
&DlgPrefReplayGain::slotApply);
setScrollSafeGuard(SliderReplayGainBoost);

connect(SliderDefaultBoost,
&QAbstractSlider::valueChanged,
this,
Expand All @@ -49,6 +51,8 @@ DlgPrefReplayGain::DlgPrefReplayGain(QWidget* parent, UserSettingsPointer pConfi
&QAbstractSlider::sliderReleased,
this,
&DlgPrefReplayGain::slotApply);
setScrollSafeGuard(SliderDefaultBoost);

connect(checkBoxReanalyze,
&QCheckBox::stateChanged,
this,
Expand Down

0 comments on commit faeebc2

Please sign in to comment.