Skip to content

Commit

Permalink
Experiemental slider improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
Hartmnt committed Jan 5, 2024
1 parent 8dc98c9 commit fdb4a26
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 53 deletions.
26 changes: 0 additions & 26 deletions src/mumble/InlineWidgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,6 @@
#include <QKeyEvent>
#include <QWidget>

UpDownKeyEventFilter::UpDownKeyEventFilter(QObject *parent) : QObject(parent) {
}

bool UpDownKeyEventFilter::eventFilter(QObject *obj, QEvent *event) {
// Converts up/down to tab/backtab
// Useful when overriding interactive QWidgetActions such as sliders

if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast< QKeyEvent * >(event);

if (keyEvent->key() == Qt::Key_Up) {
QKeyEvent *keyPress = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier);
QApplication::sendEvent(QApplication::focusWidget(), keyPress);
return true;
}

if (keyEvent->key() == Qt::Key_Down) {
QKeyEvent *keyPress = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QApplication::sendEvent(QApplication::focusWidget(), keyPress);
return true;
}
}

return QObject::eventFilter(obj, event);
}

OverrideTabOrderFilter::OverrideTabOrderFilter(QObject *parent, QWidget *target)
: QObject(parent), focusTarget(target) {
}
Expand Down
10 changes: 0 additions & 10 deletions src/mumble/InlineWidgets.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@
#include <QMenu>
#include <QObject>

class UpDownKeyEventFilter : public QObject {
Q_OBJECT

public:
UpDownKeyEventFilter(QObject *parent);

protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};

class OverrideTabOrderFilter : public QObject {
Q_OBJECT

Expand Down
1 change: 1 addition & 0 deletions src/mumble/ListenerVolumeSlider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ void ListenerVolumeSlider::on_VolumeSlider_changeCompleted() {
}

VolumeAdjustment adjustment = VolumeAdjustment::fromDBAdjustment(m_volumeSlider->value());
updateLabelValue();

if (handler->m_version >= Mumble::Protocol::PROTOBUF_INTRODUCTION_VERSION) {
// With the new audio protocol, volume adjustments for listeners are handled on the server and thus we want
Expand Down
23 changes: 21 additions & 2 deletions src/mumble/MenuLabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,35 @@
// Mumble source tree or at <https://www.mumble.info/LICENSE>.

#include "MenuLabel.h"
#include "widgets/EventFilters.h"

#include <QGridLayout>
#include <QLabel>

MenuLabel::MenuLabel(const QString &text, QObject *parent) : QWidgetAction(parent), m_text(text) {
setMenuRole(QAction::NoRole);
}

QWidget *MenuLabel::createWidget(QWidget *parent) {
QWidget *widget = new QWidget(parent);

// Not setting any focus is not an alternative here, as the
// QWidgetAction WILL get and automatically forward focus to the child.
// The widget needs tab focus policy, so it can process the event filters
// and also forward the focus.
widget->setFocusPolicy(Qt::TabFocus);
widget->installEventFilter(new SkipFocusEventFilter(widget));

// Using a widget and layout here instead of a plain label
// because otherwise screen readers might partially read the
// label text.
QGridLayout *layout = new QGridLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);

QLabel *label = new QLabel(m_text, parent);
label->setFocusPolicy(Qt::TabFocus);
layout->addWidget(label, 0, 0);
widget->setLayout(layout);

return label;
return widget;
}
2 changes: 2 additions & 0 deletions src/mumble/UserLocalVolumeSlider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,7 @@ void UserLocalVolumeSlider::on_VolumeSlider_changeCompleted() {
} else {
Global::get().mw->logChangeNotPermanent(QObject::tr("Local Volume Adjustment..."), user);
}

updateLabelValue();
}
}
4 changes: 2 additions & 2 deletions src/mumble/UserLocalVolumeSlider.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class UserLocalVolumeSlider : public VolumeSliderWidgetAction {
void setUser(unsigned int sessionId);

private slots:
void on_VolumeSlider_valueChanged(int value);
void on_VolumeSlider_changeCompleted();
void on_VolumeSlider_valueChanged(int value) override;
void on_VolumeSlider_changeCompleted() override;
};

#endif
67 changes: 55 additions & 12 deletions src/mumble/VolumeSliderWidgetAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@
// Mumble source tree or at <https://www.mumble.info/LICENSE>.

#include "VolumeSliderWidgetAction.h"
#include "InlineWidgets.h"
#include "MumbleApplication.h"
#include "VolumeAdjustment.h"
#include "widgets/EventFilters.h"

#include <QHBoxLayout>
#include <QLabel>
#include <QSlider>
#include <QToolTip>

VolumeSliderWidgetAction::VolumeSliderWidgetAction(QWidget *parent)
: QWidgetAction(parent), m_volumeSlider(make_qt_unique< QSlider >(Qt::Horizontal, parent)) {
: QWidgetAction(parent), m_widget(make_qt_unique< QWidget >(parent)),
m_volumeSlider(new QSlider(Qt::Horizontal, parent)), m_label(new QLabel("0 db", parent)) {
m_volumeSlider->setMinimum(-30);
m_volumeSlider->setMaximum(30);
m_volumeSlider->setAccessibleName(tr("Slider for volume adjustment"));
m_volumeSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_volumeSlider->setAccessibleName(tr("Local volume adjustment"));

KeyEventObserver *keyEventFilter = new KeyEventObserver(this, QEvent::KeyRelease, false,
{ Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down });
m_label->setStyleSheet("QLabel { margin-left: 0px; padding: 0px; }");
m_volumeSlider->setStyleSheet("QSlider { margin-right: 0px; }");

KeyEventObserver *keyEventFilter =
new KeyEventObserver(this, QEvent::KeyRelease, false, { Qt::Key_Left, Qt::Key_Right });
m_volumeSlider->installEventFilter(keyEventFilter);

// The list of wheel events observed seems odd at first. We have to check for multiple
Expand All @@ -33,27 +38,65 @@ VolumeSliderWidgetAction::VolumeSliderWidgetAction(QWidget *parent)
new MouseWheelEventObserver(this, { Qt::ScrollMomentum, Qt::ScrollUpdate, Qt::ScrollEnd }, false);
m_volumeSlider->installEventFilter(wheelEventFilter);

connect(m_volumeSlider.get(), &QSlider::valueChanged, this,
&VolumeSliderWidgetAction::on_VolumeSlider_valueChanged);
connect(m_volumeSlider.get(), &QSlider::sliderReleased, this,
&VolumeSliderWidgetAction::on_VolumeSlider_changeCompleted);

// Since we do not want the inline label to update when we drag the mouse, we
// must install a click event observer to update the label when the user just randomly
// clicks on the slider bar.
MouseClickEventObserver *mouseEventFilter = new MouseClickEventObserver(this, false);
m_volumeSlider->installEventFilter(mouseEventFilter);
connect(mouseEventFilter, &MouseClickEventObserver::clickEventObserved, this, [=]() {
m_volumeSlider->setFocus(Qt::TabFocusReason);
updateLabelValue(false);
});

// Also update the label explicitly when the slider body is released.
connect(m_volumeSlider, &QSlider::sliderReleased, this, [=]() {
m_volumeSlider->setFocus(Qt::TabFocusReason);
updateLabelValue(false);
});

connect(m_volumeSlider, &QSlider::valueChanged, this, &VolumeSliderWidgetAction::on_VolumeSlider_valueChanged);

connect(m_volumeSlider, &QSlider::sliderReleased, this, &VolumeSliderWidgetAction::on_VolumeSlider_changeCompleted);
connect(keyEventFilter, &KeyEventObserver::keyEventObserved, this,
&VolumeSliderWidgetAction::on_VolumeSlider_changeCompleted);
connect(wheelEventFilter, &MouseWheelEventObserver::wheelEventObserved, this,
&VolumeSliderWidgetAction::on_VolumeSlider_changeCompleted);

setDefaultWidget(m_volumeSlider.get());

UpDownKeyEventFilter *eventFilter = new UpDownKeyEventFilter(this);
m_volumeSlider->installEventFilter(eventFilter);

// Used to display the drag tooltip at the mouse position
m_volumeSlider->setProperty("mouseTracking", true);

QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(m_volumeSlider);
layout->addWidget(m_label);
layout->setContentsMargins(0, 0, -1, 0);
layout->setSpacing(3);
m_widget->setLayout(layout);

m_widget->setFocusProxy(m_volumeSlider);
m_widget->setFocusPolicy(Qt::TabFocus);

setDefaultWidget(m_widget.get());
}

void VolumeSliderWidgetAction::updateLabelValue(bool checkMouseButtons) {
if (checkMouseButtons && MumbleApplication::instance()->mouseButtons() != Qt::NoButton) {
// Do not update the label while the user is dragging the slider.
// This will otherwise cause a glitchy experience.
return;
}

m_label->setText(QString("%01dB").arg(m_volumeSlider->value()));
}

void VolumeSliderWidgetAction::updateSliderValue(float value) {
int dbShift = VolumeAdjustment::toIntegerDBAdjustment(value);
m_volumeSlider->setValue(dbShift);
updateTooltip(dbShift);
updateLabelValue(false);
}

void VolumeSliderWidgetAction::updateTooltip(int value) {
Expand Down
6 changes: 5 additions & 1 deletion src/mumble/VolumeSliderWidgetAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "QtUtils.h"

class QSlider;
class QLabel;

class VolumeSliderWidgetAction : public QWidgetAction {
Q_OBJECT
Expand All @@ -19,8 +20,11 @@ class VolumeSliderWidgetAction : public QWidgetAction {
VolumeSliderWidgetAction(QWidget *parent = nullptr);

protected:
qt_unique_ptr< QSlider > m_volumeSlider;
qt_unique_ptr< QWidget > m_widget;
QSlider *m_volumeSlider;
QLabel *m_label;

void updateLabelValue(bool checkMouseButtons = true);
void updateSliderValue(float value);
void displayTooltip(int value);
void updateTooltip(int value);
Expand Down
69 changes: 69 additions & 0 deletions src/mumble/widgets/EventFilters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

#include <algorithm>

#include <QApplication>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QWidget>

Expand Down Expand Up @@ -66,3 +68,70 @@ bool MouseWheelEventObserver::eventFilter(QObject *obj, QEvent *event) {

return m_consume;
}

MouseClickEventObserver::MouseClickEventObserver(QObject *parent, bool consume) : QObject(parent), m_consume(consume) {
}

bool MouseClickEventObserver::eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = static_cast< QMouseEvent * >(event);

emit clickEventObserved(mouseEvent->buttons());

return m_consume;
}

return QObject::eventFilter(obj, event);
}

UpDownKeyEventFilter::UpDownKeyEventFilter(QObject *parent) : QObject(parent) {
}

bool UpDownKeyEventFilter::eventFilter(QObject *obj, QEvent *event) {
// Converts up/down to tab/backtab
// Useful when overriding interactive QWidgetActions such as sliders

if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast< QKeyEvent * >(event);

if (keyEvent->key() == Qt::Key_Up) {
QKeyEvent *keyPress = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier);
QApplication::sendEvent(QApplication::focusWidget(), keyPress);
return true;
}

if (keyEvent->key() == Qt::Key_Down) {
QKeyEvent *keyPress = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QApplication::sendEvent(QApplication::focusWidget(), keyPress);
return true;
}
}

return QObject::eventFilter(obj, event);
}

SkipFocusEventFilter::SkipFocusEventFilter(QObject *parent) : QObject(parent) {
}

bool SkipFocusEventFilter::eventFilter(QObject *obj, QEvent *event) {
// Detecting FocusIn events is glitchy, therefore we detect key release events
// and forward the focus to the next/previous element.

if (event->type() == QEvent::KeyRelease && QApplication::focusWidget() == obj) {
QKeyEvent *keyEvent = static_cast< QKeyEvent * >(event);

if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Backtab) {
QKeyEvent *keyPress = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier);
QApplication::sendEvent(QApplication::focusWidget(), keyPress);
return true;
}

if (keyEvent->key() == Qt::Key_Down || keyEvent->key() == Qt::Key_Tab) {
QKeyEvent *keyPress = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QApplication::sendEvent(QApplication::focusWidget(), keyPress);
return true;
}
}

return QObject::eventFilter(obj, event);
}
36 changes: 36 additions & 0 deletions src/mumble/widgets/EventFilters.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,40 @@ class MouseWheelEventObserver : public QObject {
bool m_consume;
};

class MouseClickEventObserver : public QObject {
Q_OBJECT

public:
MouseClickEventObserver(QObject *parent, bool consume);

protected:
bool eventFilter(QObject *obj, QEvent *event) override;

signals:
void clickEventObserved(Qt::MouseButtons buttons);

private:
bool m_consume;
};

class UpDownKeyEventFilter : public QObject {
Q_OBJECT

public:
UpDownKeyEventFilter(QObject *parent);

protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};

class SkipFocusEventFilter : public QObject {
Q_OBJECT

public:
SkipFocusEventFilter(QObject *parent);

protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};

#endif

0 comments on commit fdb4a26

Please sign in to comment.