Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SlicerT UI update #7453

Merged
merged 3 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/InstrumentTrackWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "ModelView.h"
#include "SerializingObject.h"
#include "PluginView.h"

class QLabel;
class QLineEdit;
Expand Down Expand Up @@ -67,6 +68,9 @@ class InstrumentTrackWindow : public QWidget, public ModelView,
InstrumentTrackWindow( InstrumentTrackView * _tv );
~InstrumentTrackWindow() override;

void resizeEvent(QResizeEvent* event) override;


// parent for all internal tab-widgets
TabWidget * tabWidgetParent()
{
Expand Down Expand Up @@ -152,6 +156,7 @@ protected slots:
InstrumentSoundShapingView * m_ssView;
InstrumentFunctionNoteStackingView* m_noteStackingView;
InstrumentFunctionArpeggioView* m_arpeggioView;
QWidget* m_instrumentFunctionsView; // container of note stacking and arpeggio
InstrumentMidiIOView * m_midiView;
EffectRackView * m_effectView;
InstrumentTuningView *m_tuningView;
Expand Down
19 changes: 11 additions & 8 deletions include/PluginView.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,26 @@

#include <QWidget>

#include "Plugin.h"
#include "ModelView.h"
#include "Plugin.h"

namespace lmms::gui
{
namespace lmms::gui {

class LMMS_EXPORT PluginView : public QWidget, public ModelView
class LMMS_EXPORT PluginView : public QWidget, public ModelView
{
public:
PluginView( Plugin * _plugin, QWidget * _parent ) :
QWidget( _parent ),
ModelView( _plugin, this )
PluginView(Plugin* _plugin, QWidget* _parent)
: QWidget(_parent)
, ModelView(_plugin, this)
{
}

} ;
void setResizable(bool resizable) { m_isResizable = resizable; }
bool isResizable() { return m_isResizable; }

private:
bool m_isResizable = false;
};

} // namespace lmms::gui

Expand Down
3 changes: 2 additions & 1 deletion plugins/SlicerT/SlicerT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ void SlicerT::playNote(NotePlayHandle* handle, SampleFrame* workingBuffer)
void SlicerT::deleteNotePluginData(NotePlayHandle* handle)
{
delete static_cast<PlaybackState*>(handle->m_pluginData);
emit isPlaying(-1, 0, 0);
}

// uses the spectral flux to determine the change in magnitude
Expand Down Expand Up @@ -246,7 +247,7 @@ void SlicerT::findSlices()
if (noteSnap == 0) { sliceLock = 1; }
for (float& sliceValue : m_slicePoints)
{
sliceValue += sliceLock / 2;
sliceValue += sliceLock / 2.f;
sliceValue -= static_cast<int>(sliceValue) % sliceLock;
}

Expand Down
2 changes: 2 additions & 0 deletions plugins/SlicerT/SlicerT.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public slots:
void findSlices();
void findBPM();

QString getSampleName() { return m_originalSample.sampleFile(); }

QString nodeName() const override;
gui::PluginView* instantiateView(QWidget* parent) override;

Expand Down
140 changes: 116 additions & 24 deletions plugins/SlicerT/SlicerTView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@
#include "SlicerTView.h"

#include <QDropEvent>
#include <QFileInfo>
#include <qpixmap.h>
#include <qpushbutton.h>

#include "Clipboard.h"
#include "DataFile.h"
#include "Engine.h"
#include "InstrumentTrack.h"
#include "InstrumentView.h"
#include "PixmapButton.h"
#include "SampleLoader.h"
#include "SlicerT.h"
#include "Song.h"
#include "StringPairDrag.h"
#include "Track.h"
#include "embed.h"
Expand All @@ -43,57 +44,63 @@ namespace lmms {
namespace gui {

SlicerTView::SlicerTView(SlicerT* instrument, QWidget* parent)
: InstrumentViewFixedSize(instrument, parent)
: InstrumentView(instrument, parent)
, m_slicerTParent(instrument)
, m_fullLogo(PLUGIN_NAME::getIconPixmap("full_logo"))
, m_background(PLUGIN_NAME::getIconPixmap("toolbox"))
{
// window settings
setAcceptDrops(true);
setAutoFillBackground(true);

// render background
QPalette pal;
pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork"));
setPalette(pal);
setMaximumSize(QSize(10000, 10000));
setMinimumSize(QSize(516, 400));
setResizable(true);

m_wf = new SlicerTWaveform(248, 128, instrument, this);
m_wf->move(2, 6);
m_wf->move(0, s_topBarHeight);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Michael did point this out, basically, you can use a QVBoxLayout to get the elements listed vertically, and the bottom toolbox can be wrapped in a QHBoxLayout. That way, you'll have to do little calculation.


m_snapSetting = new ComboBox(this, tr("Slice snap"));
m_snapSetting->setGeometry(185, 200, 55, ComboBox::DEFAULT_HEIGHT);
m_snapSetting->setToolTip(tr("Set slice snapping for detection"));
m_snapSetting->setModel(&m_slicerTParent->m_sliceSnap);

m_syncToggle = new LedCheckBox("Sync", this, tr("SyncToggle"), LedCheckBox::LedColor::Green);
m_syncToggle->move(135, 187);
m_syncToggle = new PixmapButton(this, tr("Sync sample"));
m_syncToggle->setActiveGraphic(PLUGIN_NAME::getIconPixmap("sync_active"));
m_syncToggle->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("sync_inactive"));
m_syncToggle->setCheckable(true);
m_syncToggle->setToolTip(tr("Enable BPM sync"));
m_syncToggle->setModel(&m_slicerTParent->m_enableSync);

m_bpmBox = new LcdSpinBox(3, "19purple", this);
m_bpmBox->move(130, 201);
m_bpmBox->setToolTip(tr("Original sample BPM"));
m_bpmBox->setModel(&m_slicerTParent->m_originalBPM);

m_noteThresholdKnob = createStyledKnob();
m_noteThresholdKnob->move(10, 197);
m_noteThresholdKnob->setToolTip(tr("Threshold used for slicing"));
m_noteThresholdKnob->setModel(&m_slicerTParent->m_noteThreshold);

m_fadeOutKnob = createStyledKnob();
m_fadeOutKnob->move(64, 197);
m_fadeOutKnob->setToolTip(tr("Fade Out per note in milliseconds"));
m_fadeOutKnob->setModel(&m_slicerTParent->m_fadeOutFrames);

m_midiExportButton = new QPushButton(this);
m_midiExportButton->move(199, 150);
m_midiExportButton->setIcon(PLUGIN_NAME::getIconPixmap("copy_midi"));
m_midiExportButton->setToolTip(tr("Copy midi pattern to clipboard"));
connect(m_midiExportButton, &PixmapButton::clicked, this, &SlicerTView::exportMidi);

m_folderButton = new PixmapButton(this);
m_folderButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("folder_icon"));
m_folderButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("folder_icon"));
m_folderButton->setToolTip(tr("Open sample selector"));
connect(m_folderButton, &PixmapButton::clicked, this, &SlicerTView::openFiles);

m_resetButton = new QPushButton(this);
m_resetButton->move(18, 150);
m_resetButton->setIcon(PLUGIN_NAME::getIconPixmap("reset_slices"));
m_resetButton->setToolTip(tr("Reset Slices"));
m_resetButton->setToolTip(tr("Reset slices"));
connect(m_resetButton, &PixmapButton::clicked, m_slicerTParent, &SlicerT::updateSlices);

update();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you switch to layouts, you might not even need this update call

}

Knob* SlicerTView::createStyledKnob()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be easier to add another knob style in Knob.cpp and use that here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would that be any easier? Even if it were, the knob styles are supposed to be for general use, not something specific like knobs used by only one plugin.

Expand Down Expand Up @@ -178,16 +185,101 @@ void SlicerTView::dropEvent(QDropEvent* de)
void SlicerTView::paintEvent(QPaintEvent* pe)
{
QPainter brush(this);
brush.setPen(QColor(255, 255, 255));
brush.setFont(QFont(brush.font().family(), 7, -1, false));

brush.drawText(8, s_topTextY, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Reset"));
brush.drawText(188, s_topTextY, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Midi"));
int boxTopY = height() - s_bottomBoxHeight;

// --- backgrounds and limiters
brush.drawPixmap(QRect(0, boxTopY, s_leftBoxWidth, s_bottomBoxHeight), m_background); // left
brush.fillRect(
QRect(s_leftBoxWidth, boxTopY, width() - s_leftBoxWidth, s_bottomBoxHeight), QColor(23, 26, 31)); // right
brush.fillRect(QRect(0, 0, width(), s_topBarHeight), QColor(20, 23, 27)); // top

// top bar dividers
brush.setPen(QColor(56, 58, 60));
brush.drawLine(0, s_topBarHeight - 1, width(), s_topBarHeight - 1);
brush.drawLine(0, 0, width(), 0);

// sample name divider
brush.setPen(QColor(56, 58, 60));
brush.drawLine(0, boxTopY, width(), boxTopY);

// boxes divider
brush.setPen(QColor(56, 24, 94));
brush.drawLine(s_leftBoxWidth, boxTopY, s_leftBoxWidth, height());

// --- top bar
brush.drawPixmap(
QRect(10, (s_topBarHeight - m_fullLogo.height()) / 2, m_fullLogo.width(), m_fullLogo.height()), m_fullLogo);

int y1_text = m_y1 + 27;

// --- left box
brush.setPen(QColor(255, 255, 255));
brush.drawText(s_x1 - 25, y1_text, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Threshold"));
brush.drawText(s_x2 - 25, y1_text, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Fade Out"));
brush.drawText(s_x3 - 25, y1_text, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Reset"));
brush.drawText(s_x4 - 8, y1_text, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Midi"));
brush.drawText(s_x5 - 16, y1_text, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("BPM"));
brush.drawText(s_x6 - 8, y1_text, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Snap"));

int kor = 15; // knob outer radius
int kir = 9; // knob inner radius

// draw knob backgrounds
brush.setRenderHint(QPainter::Antialiasing);

// draw outer radius 2 times to make smooth
brush.setPen(QPen(QColor(159, 124, 223, 100), 4));
brush.drawArc(QRect(s_x1 - kor, m_y1, kor * 2, kor * 2), -45 * 16, 270 * 16);
brush.drawArc(QRect(s_x2 - kor, m_y1, kor * 2, kor * 2), -45 * 16, 270 * 16);

brush.setPen(QPen(QColor(159, 124, 223, 255), 2));
brush.drawArc(QRect(s_x1 - kor, m_y1, kor * 2, kor * 2), -45 * 16, 270 * 16);
brush.drawArc(QRect(s_x2 - kor, m_y1, kor * 2, kor * 2), -45 * 16, 270 * 16);

// inner knob circle
brush.setBrush(QColor(106, 90, 138));
brush.setPen(QColor(0, 0, 0, 0));
brush.drawEllipse(QPoint(s_x1, m_y1 + 15), kir, kir);
brush.drawEllipse(QPoint(s_x2, m_y1 + 15), kir, kir);

// current sample bar
brush.fillRect(QRect(0, boxTopY - s_sampleBoxHeight, width(), s_sampleBoxHeight), QColor(5, 5, 5));

brush.setPen(QColor(56, 58, 60));
brush.drawLine(width() - 24, boxTopY - s_sampleBoxHeight, width() - 24, boxTopY);

brush.setPen(QColor(255, 255, 255, 180));
brush.setFont(QFont(brush.font().family(), 8, -1, false));
QString sampleName = m_slicerTParent->getSampleName();
if (sampleName == "") { sampleName = "No sample loaded"; }

brush.drawText(5, boxTopY - s_sampleBoxHeight, width(), s_sampleBoxHeight, Qt::AlignLeft, sampleName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like lots of manual layout computations. Ideally the different components would be implemented as widgets of their own which would then be put into Qt's layouts so that they can resize dynamically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure that using Qt's layouts would be more work and more complex. I use that many offsets to get everything to align pixel-perfectly, so even if I used layouts, I would have to add the offset in some way or another. And the move parts are honestly the simplest, the complex part is drawing the text, dividers, gradients, knobs and backgrounds. Don't really see how that could be implemented any differently.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would definitively be possible with the right resize policies and margins. Layouts are used exactly for situations like these so that complex layout calculations do not have to be reimplemented over and over again. Using widgets in layouts might also lead to more reusable components. However, I guess it's nothing for now.

}

void SlicerTView::resizeEvent(QResizeEvent* re)
{
m_y1 = height() - s_bottomBoxOffset;

// left box
m_noteThresholdKnob->move(s_x1 - 25, m_y1);
m_fadeOutKnob->move(s_x2 - 25, m_y1);

m_resetButton->move(s_x3 - 15, m_y1 + 3);
m_midiExportButton->move(s_x4 + 2, m_y1 + 3);

m_bpmBox->move(s_x5 - 13, m_y1 + 4);
m_snapSetting->move(s_x6 - 8, m_y1 + 3);

// right box
m_syncToggle->move((width() - 100), m_y1 + 5);

m_folderButton->move(width() - 20, height() - s_bottomBoxHeight - s_sampleBoxHeight + 1);
michaelgregorius marked this conversation as resolved.
Show resolved Hide resolved

int waveFormHeight = height() - s_bottomBoxHeight - s_topBarHeight - s_sampleBoxHeight;

brush.drawText(8, s_bottomTextY, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Threshold"));
brush.drawText(63, s_bottomTextY, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Fade Out"));
brush.drawText(127, s_bottomTextY, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("BPM"));
brush.drawText(188, s_bottomTextY, s_textBoxWidth, s_textBoxHeight, Qt::AlignCenter, tr("Snap"));
m_wf->resize(width(), waveFormHeight);
}

} // namespace gui
Expand Down
37 changes: 29 additions & 8 deletions plugins/SlicerT/SlicerTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
#define LMMS_GUI_SLICERT_VIEW_H

#include <QPushButton>
#include <qpixmap.h>

#include "ComboBox.h"
#include "Instrument.h"
#include "InstrumentView.h"
#include "Knob.h"
#include "LcdSpinBox.h"
Expand All @@ -42,7 +42,7 @@ class SlicerT;

namespace gui {

class SlicerTView : public InstrumentViewFixedSize
class SlicerTView : public InstrumentView
{
Q_OBJECT

Expand All @@ -55,14 +55,27 @@ public slots:

static constexpr int s_textBoxHeight = 20;
static constexpr int s_textBoxWidth = 50;
static constexpr int s_topTextY = 170;
static constexpr int s_bottomTextY = 220;

static constexpr int s_topBarHeight = 50;
static constexpr int s_bottomBoxHeight = 97;
static constexpr int s_bottomBoxOffset = 65;
static constexpr int s_sampleBoxHeight = 14;
static constexpr int s_folderButtonWidth = 15;
static constexpr int s_leftBoxWidth = 400;


static constexpr int s_x1 = 35;
static constexpr int s_x2 = 85;
static constexpr int s_x3 = 160;
static constexpr int s_x4 = 190;
static constexpr int s_x5 = 275;
static constexpr int s_x6 = 325;
protected:
virtual void dragEnterEvent(QDragEnterEvent* dee);
virtual void dropEvent(QDropEvent* de);
void dragEnterEvent(QDragEnterEvent* dee) override;
void dropEvent(QDropEvent* de) override;

virtual void paintEvent(QPaintEvent* pe);
void paintEvent(QPaintEvent* pe) override;
void resizeEvent(QResizeEvent* event) override;

private:
SlicerT* m_slicerTParent;
Expand All @@ -71,14 +84,22 @@ public slots:
Knob* m_fadeOutKnob;
LcdSpinBox* m_bpmBox;
ComboBox* m_snapSetting;
LedCheckBox* m_syncToggle;
PixmapButton* m_syncToggle;
PixmapButton* m_folderButton;

QPushButton* m_resetButton;
QPushButton* m_midiExportButton;

SlicerTWaveform* m_wf;

Knob* createStyledKnob();

QPixmap m_fullLogo;
QPixmap m_background;


int m_y1;
int m_y2;
};
} // namespace gui
} // namespace lmms
Expand Down
Loading
Loading