1 /*
2 * volumebutton.cc
3 * Copyright 2014 Ariadne Conill
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions, and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions, and the following disclaimer in the documentation
13 * provided with the distribution.
14 *
15 * This software is provided "as is" and without any warranty, express or
16 * implied. In no event shall the authors be liable for any damages arising from
17 * the use of this software.
18 */
19
20 #include "libaudqt.h"
21
22 #include <QIcon>
23 #include <QMenu>
24 #include <QSlider>
25 #include <QToolButton>
26 #include <QVBoxLayout>
27 #include <QWheelEvent>
28 #include <QWidgetAction>
29
30 #include <libaudcore/drct.h>
31 #include <libaudcore/hook.h>
32 #include <libaudcore/runtime.h>
33
34 namespace audqt
35 {
36
37 class VolumeButton : public QToolButton
38 {
39 public:
40 VolumeButton(QWidget * parent = nullptr);
41
42 private:
43 void updateDelta();
44 void updateIcon(int val);
45 void updateVolume();
46 void setVolume(int val);
47 void setUpButton(QToolButton * button, int dir);
48
49 void wheelEvent(QWheelEvent * e);
50
51 QMenu m_menu;
52 QWidgetAction m_action;
53 QWidget m_container;
54 QToolButton m_buttons[2];
55 QSlider m_slider;
56 int m_scroll_delta = 0;
57
58 HookReceiver<VolumeButton> update_hook{"set volume_delta", this,
59 &VolumeButton::updateDelta};
60
61 Timer<VolumeButton> m_timer{TimerRate::Hz4, this,
62 &VolumeButton::updateVolume};
63 };
64
VolumeButton(QWidget * parent)65 VolumeButton::VolumeButton(QWidget * parent)
66 : QToolButton(parent), m_action(this), m_slider(Qt::Vertical)
67 {
68 m_slider.setMinimumHeight(audqt::sizes.OneInch);
69 m_slider.setRange(0, 100);
70
71 setUpButton(&m_buttons[0], 1);
72 setUpButton(&m_buttons[1], -1);
73
74 auto layout = make_vbox(&m_container, sizes.TwoPt);
75 layout->setContentsMargins(margins.TwoPt);
76 layout->addWidget(&m_buttons[0]);
77 layout->addWidget(&m_slider);
78 layout->addWidget(&m_buttons[1]);
79 layout->setAlignment(&m_slider, Qt::AlignHCenter);
80
81 m_action.setDefaultWidget(&m_container);
82 m_menu.addAction(&m_action);
83
84 setAutoRaise(true);
85 setFocusPolicy(Qt::NoFocus);
86 setMenu(&m_menu);
87 setPopupMode(InstantPopup);
88 setStyleSheet("QToolButton::menu-indicator { image: none; }");
89
90 int val = aud_drct_get_volume_main();
91 m_slider.setValue(val);
92 m_slider.setFocus();
93
94 updateIcon(val);
95 updateDelta();
96
97 connect(&m_slider, &QAbstractSlider::valueChanged, this,
98 &VolumeButton::setVolume);
99
100 m_timer.start();
101 }
102
updateDelta()103 void VolumeButton::updateDelta()
104 {
105 int delta = aud_get_int("volume_delta");
106 m_slider.setSingleStep(delta);
107 m_slider.setPageStep(delta);
108 }
109
updateIcon(int val)110 void VolumeButton::updateIcon(int val)
111 {
112 if (val == 0)
113 setIcon(audqt::get_icon("audio-volume-muted"));
114 else if (val < 34)
115 setIcon(audqt::get_icon("audio-volume-low"));
116 else if (val < 67)
117 setIcon(audqt::get_icon("audio-volume-medium"));
118 else
119 setIcon(audqt::get_icon("audio-volume-high"));
120
121 setToolTip(QString("%1 %").arg(val));
122 }
123
updateVolume()124 void VolumeButton::updateVolume()
125 {
126 if (m_slider.isSliderDown())
127 return;
128
129 int val = aud_drct_get_volume_main();
130 if (val != m_slider.value())
131 {
132 disconnect(&m_slider, nullptr, this, nullptr);
133 m_slider.setValue(val);
134 updateIcon(val);
135 connect(&m_slider, &QAbstractSlider::valueChanged, this,
136 &VolumeButton::setVolume);
137 }
138 }
139
setVolume(int val)140 void VolumeButton::setVolume(int val)
141 {
142 aud_drct_set_volume_main(val);
143 updateIcon(val);
144 }
145
setUpButton(QToolButton * button,int dir)146 void VolumeButton::setUpButton(QToolButton * button, int dir)
147 {
148 button->setText(dir < 0 ? "-" : "+");
149 button->setAutoRaise(true);
150 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
151
152 connect(button, &QAbstractButton::clicked, [this, dir]() {
153 m_slider.setValue(m_slider.value() + dir * aud_get_int("volume_delta"));
154 });
155 }
156
wheelEvent(QWheelEvent * e)157 void VolumeButton::wheelEvent(QWheelEvent * e)
158 {
159 m_scroll_delta += e->angleDelta().y();
160
161 /* we want discrete steps here */
162 int steps = m_scroll_delta / 120;
163 if (steps != 0)
164 {
165 m_scroll_delta -= 120 * steps;
166 m_slider.setValue(m_slider.value() +
167 steps * aud_get_int("volume_delta"));
168 }
169 }
170
volume_button_new(QWidget * parent)171 EXPORT QToolButton * volume_button_new(QWidget * parent)
172 {
173 return new VolumeButton(parent);
174 }
175
176 } // namespace audqt
177