package slider components into its own widget

This commit is contained in:
2025-12-25 21:39:12 -06:00
parent 65fd963a3f
commit 97de073690
13 changed files with 244 additions and 241 deletions

View File

@@ -1,9 +1,8 @@
#include "MainWindow.h"
#include <QTimer>
#include "ui_MainWindow.h"
#include "SmartSlider.h"
#include "../ParameterStore.h"
MainWindow::MainWindow(QWidget *parent) :
@@ -22,25 +21,18 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui_->buttonIncrement, &QPushButton::clicked, this, &MainWindow::onIncrementClicked);
connect(ui_->buttonReset, &QPushButton::clicked, this, &MainWindow::onResetClicked);
// slider business
// TODO: smart slider widget
connect(ui_->slider, &QSlider::valueChanged, this, &MainWindow::onSliderValueChanged);
connect(ui_->inputMin, &QLineEdit::editingFinished, this, &MainWindow::onMinChanged);
connect(ui_->inputMax, &QLineEdit::editingFinished, this, &MainWindow::onMaxChanged);
connect(ui_->inputStep, &QLineEdit::editingFinished, this, &MainWindow::onStepChanged);
connect(ui_->inputValue, &QLineEdit::editingFinished, this, &MainWindow::onValueChanged);
// synth business
audio_->start();
// init defaults
// TODO:: there's gotta be a better way
ui_->slider->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1Frequency)].def);
ui_->slider->setMinimum(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1Frequency)].min);
ui_->slider->setMaximum(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1Frequency)].max);
ui_->inputValue->setText(QString::number(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1Frequency)].def));
ui_->inputMin->setText(QString::number(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1Frequency)].min));
ui_->inputMax->setText(QString::number(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1Frequency)].max));
// slider business
connect(ui_->sliderGainR, &SmartSlider::valueChanged, this, [this](float v) {
audio_->parameters()->set(ParamId::Osc1VolumeEnvR, v); // bind valueChanged signal to the ParameterStore
});
// initialize to defaults
ui_->sliderGainR->setRange(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].max);
ui_->sliderGainR->setValue(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
// TODO: package a smartSlider into an envelope controller widget
// then intialization and parameter bindings can be done from the envelope controller
}
MainWindow::~MainWindow() {
@@ -68,51 +60,3 @@ void MainWindow::onResetClicked() {
void MainWindow::updateCounterLabel() {
ui_->labelCounter->setText(QString::number(counter_));
}
// allows only numbers to be set
void MainWindow::onSliderValueChanged(int value) {
QSignalBlocker blocker(ui_->inputValue);
ui_->inputValue->setText(QString::number(value));
// forward value so synthesizer can read
audio_->parameters()->set(ParamId::Osc1Frequency, static_cast<float>(value));
}
// allows only values within the min, max to be set by the text field
void MainWindow::onValueChanged() {
bool ok = false;
int value = ui_->inputValue->text().toInt(&ok);
if(!ok) return;
value = qBound(ui_->slider->minimum(), value, ui_->slider->maximum());
ui_->slider->setValue(value);
}
void MainWindow::applySliderRange() {
bool minOk, maxOk;
int min = ui_->inputMin->text().toInt(&minOk);
int max = ui_->inputMax->text().toInt(&maxOk);
if(!minOk || !maxOk || min > max) return;
ui_->slider->setRange(min, max);
syncValueToUi(ui_->slider->value());
}
void MainWindow::applySliderStep() {
bool ok;
int step = ui_->inputStep->text().toInt(&ok);
if(!ok || step <= 0) return;
ui_->slider->setSingleStep(step);
ui_->slider->setPageStep(step);
}
void MainWindow::syncValueToUi(int value) {
QSignalBlocker block1(ui_->slider);
QSignalBlocker block2(ui_->inputValue);
value = qBound(ui_->slider->minimum(), value, ui_->slider->maximum());
ui_->slider->setValue(value);
ui_->inputValue->setText(QString::number(value));
}

View File

@@ -7,13 +7,11 @@
#include "../synth/AudioEngine.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
class MainWindow : public QMainWindow {
Q_OBJECT
public:
@@ -28,12 +26,6 @@ private slots:
void onIncrementClicked();
void onResetClicked();
void onSliderValueChanged(int value);
void onMinChanged() { applySliderRange(); }
void onMaxChanged() { applySliderRange(); }
void onStepChanged() { applySliderStep(); }
void onValueChanged();
private:
Ui::MainWindow *ui_;
int counter_ = 0;
@@ -42,10 +34,6 @@ private:
void updateCounterLabel();
void applySliderRange();
void applySliderStep();
void syncValueToUi(int value);
AudioEngine* audio_ = nullptr;
KeyboardController keyboard_;

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
<height>605</height>
</rect>
</property>
<property name="windowTitle">
@@ -71,124 +71,26 @@
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QSlider" name="slider">
<widget class="SmartSlider" name="sliderGainR" native="true">
<property name="geometry">
<rect>
<x>390</x>
<y>210</y>
<width>16</width>
<height>160</height>
<x>340</x>
<y>120</y>
<width>171</width>
<height>321</height>
</rect>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
</widget>
<widget class="QLineEdit" name="inputMax">
<property name="geometry">
<rect>
<x>370</x>
<y>190</y>
<width>61</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<stylestrategy>PreferDefault</stylestrategy>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>100</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QLineEdit" name="inputStep">
<property name="geometry">
<rect>
<x>370</x>
<y>170</y>
<width>61</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<stylestrategy>PreferDefault</stylestrategy>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>100</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QLineEdit" name="inputValue">
<property name="geometry">
<rect>
<x>360</x>
<y>140</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<stylestrategy>PreferDefault</stylestrategy>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>100</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QLineEdit" name="inputMin">
<property name="geometry">
<rect>
<x>370</x>
<y>370</y>
<width>61</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<stylestrategy>PreferDefault</stylestrategy>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>100</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>SmartSlider</class>
<extends>QWidget</extends>
<header>SmartSlider.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

View File

@@ -0,0 +1,88 @@
#include "SmartSlider.h"
#include "ui_SmartSlider.h"
#include <iostream>
SmartSlider::SmartSlider(QWidget* parent) : QWidget(parent), ui_(new Ui::SmartSlider) {
ui_->setupUi(this);
ui_->slider->setMinimum(0);
ui_->slider->setMaximum(sliderResolution_);
// connect widgets to slots
connect(ui_->slider, &QSlider::valueChanged, this, &SmartSlider::onSliderChanged);
connect(ui_->spinValue, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SmartSlider::onSpinValueChanged);
connect(ui_->spinMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SmartSlider::onRangeChanged);
connect(ui_->spinMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SmartSlider::onRangeChanged);
}
SmartSlider::~SmartSlider() {
delete ui_;
}
// sets range of the slider, updates the spinBoxes, and clamps output, called by other classes
// probably overengineered
void SmartSlider::setRange(float min, float max) {
if (min >= max) return;
min_ = min; max_ = max;
ui_->slider->setRange(0, sliderResolution_);
ui_->spinMin->setValue(min_);
ui_->spinMax->setValue(max_);
//ui_->spinValue->setRange(min_, max_);
float value = ui_->spinValue->value();
value = std::clamp(value, min_, max_);
float t = (value - min_) / (max_ - min_);
int32_t sliderValue = static_cast<int32_t>(t) * sliderResolution_;
ui_->slider->setValue(sliderValue);
ui_->spinValue->setValue(value);
}
// sets value of the slider and the spinBox, called by other classes
void SmartSlider::setValue(float value) {
value = std::clamp(value, min_, max_);
float t = (value - min_) / (max_ - min_);
int32_t sliderValue = static_cast<int32_t>(t) * sliderResolution_;
ui_->slider->setValue(sliderValue);
ui_->spinValue->setValue(value);
emit valueChanged(value);
}
void SmartSlider::onSliderChanged(int32_t v) {
float min = ui_->spinMin->value();
float max = ui_->spinMax->value();
float value = min + (max - min) * static_cast<float>(v) / sliderResolution_;
ui_->spinValue->setValue(value);
emit valueChanged(value);
}
void SmartSlider::onSpinValueChanged(float v) {
float min = ui_->spinMin->value();
float max = ui_->spinMax->value();
float norm = (v - min) / (max - min);
int32_t sliderValue = qRound(norm * sliderResolution_);
ui_->slider->setValue(sliderValue);
// intentionally skips clamping by min/max
emit valueChanged(v);
}
void SmartSlider::onRangeChanged() {
float min = ui_->spinMin->value();
float max = ui_->spinMax->value();
onSpinValueChanged(ui_->spinValue->value());
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class SmartSlider; }
QT_END_NAMESPACE
// SmartSlider is the widget including a slider, min/max settings, and a value setting parameter
class SmartSlider : public QWidget {
Q_OBJECT
public:
explicit SmartSlider(QWidget* parent = nullptr);
~SmartSlider();
// setters
void setRange(float min, float max);
void setValue(float value);
signals:
void valueChanged(float value);
private slots:
void onSliderChanged(int v);
void onSpinValueChanged(float v);
void onRangeChanged();
private:
Ui::SmartSlider* ui_;
// slider just does ints from 0 to this value
// all the floating point business happens in here
int sliderResolution_ = 10000;
float min_;
float max_;
// I got rid of step because it just didn't work right and I really didn't need it
};

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SmartSlider</class>
<widget class="QWidget" name="SmartSlider">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>168</width>
<height>298</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QSlider" name="slider">
<property name="geometry">
<rect>
<x>70</x>
<y>90</y>
<width>16</width>
<height>160</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
</widget>
<widget class="QDoubleSpinBox" name="spinMin">
<property name="geometry">
<rect>
<x>40</x>
<y>260</y>
<width>82</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QDoubleSpinBox" name="spinMax">
<property name="geometry">
<rect>
<x>40</x>
<y>60</y>
<width>82</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QDoubleSpinBox" name="spinValue">
<property name="geometry">
<rect>
<x>40</x>
<y>10</y>
<width>82</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
</widget>
<widget class="Line" name="line">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>20</width>
<height>271</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
</widget>
<widget class="Line" name="line_2">
<property name="geometry">
<rect>
<x>120</x>
<y>10</y>
<width>20</width>
<height>271</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>