# HG changeset patch # User Chris Cannam # Date 1427208031 0 # Node ID 2a9f1eb6e0edb56f2df64355656e7819c2330c6a # Parent 26da827e8fb52d5fa599b05c01e9360481fdccb9 Try adding a combined simple level/pan widget diff -r 26da827e8fb5 -r 2a9f1eb6e0ed svgui.pro --- a/svgui.pro Mon Mar 23 11:26:28 2015 +0000 +++ b/svgui.pro Tue Mar 24 14:40:31 2015 +0000 @@ -116,6 +116,7 @@ widgets/LayerTree.h \ widgets/LayerTreeDialog.h \ widgets/LEDButton.h \ + widgets/LevelPanWidget.h \ widgets/ListInputDialog.h \ widgets/MIDIFileImportDialog.h \ widgets/ModelDataTableDialog.h \ @@ -154,6 +155,7 @@ widgets/LayerTree.cpp \ widgets/LayerTreeDialog.cpp \ widgets/LEDButton.cpp \ + widgets/LevelPanWidget.cpp \ widgets/ListInputDialog.cpp \ widgets/MIDIFileImportDialog.cpp \ widgets/ModelDataTableDialog.cpp \ diff -r 26da827e8fb5 -r 2a9f1eb6e0ed widgets/LevelPanWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LevelPanWidget.cpp Tue Mar 24 14:40:31 2015 +0000 @@ -0,0 +1,253 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "LevelPanWidget.h" + +#include +#include +#include + +#include "layer/ColourMapper.h" + +#include + +using std::cerr; +using std::endl; + +static const int maxLevel = 5; +static const int maxPan = 2; // range is -maxPan to maxPan + +LevelPanWidget::LevelPanWidget(QWidget *parent) : + QWidget(parent), + m_level(maxLevel), + m_pan(0), + m_editable(true) +{ +} + +LevelPanWidget::~LevelPanWidget() +{ +} + +void +LevelPanWidget::setLevel(float level) +{ + m_level = int(round(level * maxLevel)); + if (m_level < 0) m_level = 0; + if (m_level > maxLevel) m_level = maxLevel; + update(); +} + +void +LevelPanWidget::setPan(float pan) +{ + m_pan = int(round(pan * maxPan)); + if (m_pan < -maxPan) m_pan = -maxPan; + if (m_pan > maxPan) m_pan = maxPan; + update(); +} + +void +LevelPanWidget::setEditable(bool editable) +{ + m_editable = editable; + update(); +} + +float +LevelPanWidget::getLevel() const +{ + return float(m_level) / float(maxLevel); +} + +float +LevelPanWidget::getPan() const +{ + return float(m_pan) / float(maxPan); +} + +void +LevelPanWidget::emitLevelChanged() +{ + cerr << "emitting levelChanged(" << getLevel() << ")" << endl; + emit levelChanged(getLevel()); +} + +void +LevelPanWidget::emitPanChanged() +{ + cerr << "emitting panChanged(" << getPan() << ")" << endl; + emit panChanged(getPan()); +} + +void +LevelPanWidget::mousePressEvent(QMouseEvent *e) +{ + mouseMoveEvent(e); +} + +void +LevelPanWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (!m_editable) return; + + int level, pan; + toCell(e->pos(), level, pan); + if (level == m_level && pan == m_pan) { + return; + } + if (level != m_level) { + m_level = level; + emitLevelChanged(); + } + if (pan != m_pan) { + m_pan = pan; + emitPanChanged(); + } + update(); +} + +void +LevelPanWidget::mouseReleaseEvent(QMouseEvent *e) +{ + mouseMoveEvent(e); +} + +void +LevelPanWidget::wheelEvent(QWheelEvent *e) +{ + if (e->modifiers() & Qt::ControlModifier) { + if (e->delta() > 0) { + if (m_pan < maxPan) { + ++m_pan; + emitPanChanged(); + update(); + } + } else { + if (m_pan > -maxPan) { + --m_pan; + emitPanChanged(); + update(); + } + } + } else { + if (e->delta() > 0) { + if (m_level < maxLevel) { + ++m_level; + emitLevelChanged(); + update(); + } + } else { + if (m_level > 0) { + --m_level; + emitLevelChanged(); + update(); + } + } + } +} + +void +LevelPanWidget::toCell(QPointF loc, int &level, int &pan) const +{ + double w = width(), h = height(); + int npan = maxPan * 2 + 1; + double wcell = w / npan, hcell = h / maxLevel; + level = int((h - loc.y()) / hcell) + 1; + if (level < 1) level = 1; + if (level > maxLevel) level = maxLevel; + pan = int(loc.x() / wcell) - maxPan; + if (pan < -maxPan) pan = -maxPan; + if (pan > maxPan) pan = maxPan; +} + +QSizeF +LevelPanWidget::cellSize() const +{ + double w = width(), h = height(); + int npan = maxPan * 2 + 1; + double wcell = w / npan, hcell = h / maxLevel; + return QSizeF(wcell, hcell); +} + +QPointF +LevelPanWidget::cellCentre(int level, int pan) const +{ + QSizeF cs = cellSize(); + return QPointF(cs.width() * (pan + maxPan) + cs.width() / 2., + height() - cs.height() * level + cs.height() / 2.); +} + +QSizeF +LevelPanWidget::cellLightSize() const +{ + double extent = 3. / 4.; + QSizeF cs = cellSize(); + double m = std::min(cs.width(), cs.height()); + return QSizeF(m * extent, m * extent); +} + +QRectF +LevelPanWidget::cellLightRect(int level, int pan) const +{ + QSizeF cls = cellLightSize(); + QPointF cc = cellCentre(level, pan); + return QRectF(cc.x() - cls.width() / 2., + cc.y() - cls.height() / 2., + cls.width(), + cls.height()); +} + +double +LevelPanWidget::thinLineWidth() const +{ + double tw = ceil(width() / (maxPan * 2. * 10.)); + double th = ceil(height() / (maxLevel * 10.)); + return std::min(th, tw); +} + +void +LevelPanWidget::paintEvent(QPaintEvent *) +{ + QPainter paint(this); + ColourMapper mapper(ColourMapper::Sunset, 0, maxLevel); + + paint.setRenderHint(QPainter::Antialiasing, true); + + QPen pen; + + double thin = thinLineWidth(); + + pen.setColor(QColor(127, 127, 127, 127)); + pen.setWidthF(cellLightSize().width() + thin); + pen.setCapStyle(Qt::RoundCap); + paint.setPen(pen); + + for (int pan = -maxPan; pan <= maxPan; ++pan) { + paint.drawLine(cellCentre(1, pan), cellCentre(maxLevel, pan)); + } + + pen.setColor(Qt::black); + pen.setWidthF(thin); + pen.setCapStyle(Qt::FlatCap); + paint.setPen(pen); + + for (int level = 1; level <= m_level; ++level) { + // level starts at 1 because we handle mute separately + paint.setBrush(mapper.map(level)); + paint.drawEllipse(cellLightRect(level, m_pan)); + } +} + + diff -r 26da827e8fb5 -r 2a9f1eb6e0ed widgets/LevelPanWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LevelPanWidget.h Tue Mar 24 14:40:31 2015 +0000 @@ -0,0 +1,81 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef LEVEL_PAN_WIDGET_H +#define LEVEL_PAN_WIDGET_H + +#include + +/** + * A simple widget for coarse level and pan control. + * + * For this initial implementation at least, pan is in five steps only + * (hard left, mid-left, centre, mid-right, hard right) and level is + * in five plus mute. + */ + +class LevelPanWidget : public QWidget +{ + Q_OBJECT + +public: + LevelPanWidget(QWidget *parent = 0); + ~LevelPanWidget(); + + /// Return level as a gain value in the range [0,1] + float getLevel() const; + + /// Return pan as a value in the range [-1,1] + float getPan() const; + + /// Find out whether the widget is editable + bool isEditable() const; + +public slots: + /// Set level in the range [0,1] -- will be rounded + void setLevel(float); + + /// Set pan in the range [-1,1] -- will be rounded + void setPan(float); + + /// Specify whether the widget is editable or read-only (default editable) + void setEditable(bool); + +signals: + void levelChanged(float); + void panChanged(float); + +protected: + virtual void mousePressEvent(QMouseEvent *ev); + virtual void mouseMoveEvent(QMouseEvent *ev); + virtual void mouseReleaseEvent(QMouseEvent *ev); + virtual void wheelEvent(QWheelEvent *ev); + virtual void paintEvent(QPaintEvent *ev); + + void emitLevelChanged(); + void emitPanChanged(); + + int m_level; + int m_pan; + bool m_editable; + + QSizeF cellSize() const; + QPointF cellCentre(int level, int pan) const; + QSizeF cellLightSize() const; + QRectF cellLightRect(int level, int pan) const; + double thinLineWidth() const; + void toCell(QPointF loc, int &level, int &pan) const; +}; + +#endif