Move scroll buttons to title bar, add build info status bar

- Replace broken bottom waveform scroll buttons with title bar < > buttons
- Add scrollLeft()/scrollRight() methods to WaveformDisplay
- Add status bar showing git branch, commit, dirty state, and build timestamp
- Inject git version info via CMake at configure time
- Enable COPY_PLUGIN_AFTER_BUILD
- Increase baseHeight by 20px for status bar
This commit is contained in:
Armin 2026-07-22 13:27:26 +02:00
commit 93c8c73399
5 changed files with 93 additions and 24 deletions

View file

@ -17,7 +17,7 @@ juce_add_plugin(Monoslicer
NEEDS_MIDI_OUTPUT FALSE NEEDS_MIDI_OUTPUT FALSE
IS_MIDI_EFFECT FALSE IS_MIDI_EFFECT FALSE
EDITOR_WANTS_KEYBOARD_FOCUS TRUE EDITOR_WANTS_KEYBOARD_FOCUS TRUE
COPY_PLUGIN_AFTER_BUILD FALSE COPY_PLUGIN_AFTER_BUILD TRUE
VST3_CATEGORIES Instrument Sampler VST3_CATEGORIES Instrument Sampler
) )
@ -29,10 +29,54 @@ target_sources(Monoslicer PRIVATE
Source/PianoRollDisplay.cpp Source/PianoRollDisplay.cpp
) )
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT GIT_COMMIT_HASH)
set(GIT_COMMIT_HASH "unknown")
endif()
execute_process(
COMMAND git symbolic-ref --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
execute_process(
COMMAND git diff --quiet HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_DIRTY_RESULT
)
execute_process(
COMMAND git diff --cached --quiet
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_CACHED_DIRTY_RESULT
)
set(GIT_DIRTY "")
if(NOT GIT_DIRTY_RESULT EQUAL 0 OR NOT GIT_CACHED_DIRTY_RESULT EQUAL 0)
set(GIT_DIRTY "-dirty")
endif()
if(GIT_BRANCH)
set(GIT_VERSION "${GIT_BRANCH} @ ${GIT_COMMIT_HASH}${GIT_DIRTY}")
else()
set(GIT_VERSION "detached @ ${GIT_COMMIT_HASH}${GIT_DIRTY}")
endif()
target_compile_definitions(Monoslicer PUBLIC target_compile_definitions(Monoslicer PUBLIC
JUCE_WEB_BROWSER=0 JUCE_WEB_BROWSER=0
JUCE_USE_CURL=0 JUCE_USE_CURL=0
JUCE_VST3_CAN_REPLACE_VST2=0 JUCE_VST3_CAN_REPLACE_VST2=0
GIT_VERSION="${GIT_VERSION}"
) )
target_link_libraries(Monoslicer target_link_libraries(Monoslicer

View file

@ -24,6 +24,8 @@ MonoslicerEditor::MonoslicerEditor(MonoslicerProcessor& p)
setupTitleBtn(zoomInButton, "Zoom In"); setupTitleBtn(zoomInButton, "Zoom In");
setupTitleBtn(zoomOutButton, "Zoom Out"); setupTitleBtn(zoomOutButton, "Zoom Out");
setupTitleBtn(zoomResetButton, "Reset Zoom"); setupTitleBtn(zoomResetButton, "Reset Zoom");
setupTitleBtn(scrollLeftButton, "Scroll waveform left");
setupTitleBtn(scrollRightButton, "Scroll waveform right");
addAndMakeVisible(scaleComboBox); addAndMakeVisible(scaleComboBox);
scaleComboBox.addItem("75%", 1); scaleComboBox.addItem("75%", 1);
@ -55,6 +57,14 @@ MonoslicerEditor::MonoslicerEditor(MonoslicerProcessor& p)
addAndMakeVisible(waveformDisplay); addAndMakeVisible(waveformDisplay);
// Status bar
addAndMakeVisible(statusBar);
statusBar.setText("Build: " GIT_VERSION " (" __DATE__ ", " __TIME__ ")",
juce::dontSendNotification);
statusBar.setColour(juce::Label::textColourId, juce::Colour(0xff666688));
statusBar.setFont(juce::Font(juce::FontOptions(11.0f)));
statusBar.setJustificationType(juce::Justification::centredRight);
// Slice knobs // Slice knobs
attachSlider(bassSlider, bassLabel, "Bass", "bass", bassAttachment); attachSlider(bassSlider, bassLabel, "Bass", "bass", bassAttachment);
attachSlider(trebleSlider, trebleLabel, "Treble", "treble", trebleAttachment); attachSlider(trebleSlider, trebleLabel, "Treble", "treble", trebleAttachment);
@ -197,6 +207,8 @@ MonoslicerEditor::~MonoslicerEditor()
zoomInButton.removeListener(this); zoomInButton.removeListener(this);
zoomOutButton.removeListener(this); zoomOutButton.removeListener(this);
zoomResetButton.removeListener(this); zoomResetButton.removeListener(this);
scrollLeftButton.removeListener(this);
scrollRightButton.removeListener(this);
stretchButton.removeListener(this); stretchButton.removeListener(this);
} }
@ -242,6 +254,8 @@ void MonoslicerEditor::resized()
auto bounds = getLocalBounds(); auto bounds = getLocalBounds();
auto titleBar = bounds.removeFromTop(40); auto titleBar = bounds.removeFromTop(40);
auto statusBarArea = bounds.removeFromBottom(18).withTrimmedRight(5);
statusBar.setBounds(statusBarArea);
int tx = 120; int tx = 120;
@ -263,6 +277,9 @@ void MonoslicerEditor::resized()
placeBtn(zoomOutButton, 28); placeBtn(zoomOutButton, 28);
placeBtn(zoomResetButton, 32); placeBtn(zoomResetButton, 32);
placeBtn(zoomInButton, 28); placeBtn(zoomInButton, 28);
tx += 6;
placeBtn(scrollLeftButton, 24);
placeBtn(scrollRightButton, 24);
scaleComboBox.setBounds(titleBar.getRight() - 100, titleBar.getY() + 8, 90, 24); scaleComboBox.setBounds(titleBar.getRight() - 100, titleBar.getY() + 8, 90, 24);
octaveComboBox.setBounds(titleBar.getRight() - 176, titleBar.getY() + 8, 72, 24); octaveComboBox.setBounds(titleBar.getRight() - 176, titleBar.getY() + 8, 72, 24);
@ -446,6 +463,14 @@ void MonoslicerEditor::buttonClicked(juce::Button* button)
{ {
waveformDisplay.zoomReset(); waveformDisplay.zoomReset();
} }
else if (button == &scrollLeftButton)
{
waveformDisplay.scrollLeft();
}
else if (button == &scrollRightButton)
{
waveformDisplay.scrollRight();
}
} }
void MonoslicerEditor::comboBoxChanged(juce::ComboBox* comboBoxThatHasChanged) void MonoslicerEditor::comboBoxChanged(juce::ComboBox* comboBoxThatHasChanged)

View file

@ -41,11 +41,13 @@ private:
juce::TextButton zoomInButton { "+" }; juce::TextButton zoomInButton { "+" };
juce::TextButton zoomOutButton { "-" }; juce::TextButton zoomOutButton { "-" };
juce::TextButton zoomResetButton { "1:1" }; juce::TextButton zoomResetButton { "1:1" };
juce::TextButton scrollLeftButton { "<" };
juce::TextButton scrollRightButton { ">" };
juce::ComboBox scaleComboBox; juce::ComboBox scaleComboBox;
juce::ComboBox octaveComboBox; juce::ComboBox octaveComboBox;
static constexpr int baseWidth = 1100; static constexpr int baseWidth = 1100;
static constexpr int baseHeight = 550; static constexpr int baseHeight = 570;
// Slice knobs // Slice knobs
juce::Slider bassSlider, trebleSlider, sensitivitySlider; juce::Slider bassSlider, trebleSlider, sensitivitySlider;
@ -88,6 +90,7 @@ private:
std::unique_ptr<ComboBoxAttachment> filterTypeAttachment; std::unique_ptr<ComboBoxAttachment> filterTypeAttachment;
juce::FileChooser fileChooser { "Load Audio File", juce::File{}, "*.wav;*.aiff;*.flac;*.ogg" }; juce::FileChooser fileChooser { "Load Audio File", juce::File{}, "*.wav;*.aiff;*.flac;*.ogg" };
juce::Label statusBar;
void setupSlider(juce::Slider& slider, juce::Label& label, const juce::String& name); void setupSlider(juce::Slider& slider, juce::Label& label, const juce::String& name);
void attachSlider(juce::Slider& slider, juce::Label& label, const juce::String& name, void attachSlider(juce::Slider& slider, juce::Label& label, const juce::String& name,

View file

@ -7,13 +7,6 @@ WaveformDisplay::WaveformDisplay(SliceManager& sm) : sliceManager(sm)
hScrollBar.setRangeLimits(0.0, 1.0); hScrollBar.setRangeLimits(0.0, 1.0);
hScrollBar.setCurrentRange(0.0, 1.0); hScrollBar.setCurrentRange(0.0, 1.0);
auto setupScrollBtn = [this](juce::TextButton& btn) {
addAndMakeVisible(btn);
btn.setColour(juce::TextButton::buttonColourId, juce::Colour(0xff2a2a4a));
};
setupScrollBtn(scrollLeftBtn);
setupScrollBtn(scrollRightBtn);
startTimerHz(30); startTimerHz(30);
} }
@ -67,6 +60,22 @@ void WaveformDisplay::zoomReset()
repaint(); repaint();
} }
void WaveformDisplay::scrollLeft()
{
scrollOffset -= 1.0f / zoomFactor;
scrollOffset = juce::jlimit(0.0f, 1.0f, scrollOffset);
syncScrollBar();
repaint();
}
void WaveformDisplay::scrollRight()
{
scrollOffset += 1.0f / zoomFactor;
scrollOffset = juce::jlimit(0.0f, 1.0f, scrollOffset);
syncScrollBar();
repaint();
}
void WaveformDisplay::syncScrollBar() void WaveformDisplay::syncScrollBar()
{ {
if (updatingScroll) return; if (updatingScroll) return;
@ -209,7 +218,6 @@ void WaveformDisplay::paint(juce::Graphics& g)
g.setColour(juce::Colours::grey); g.setColour(juce::Colours::grey);
g.setFont(14.0f); g.setFont(14.0f);
g.drawText("No audio file loaded.", textArea, juce::Justification::centred); g.drawText("No audio file loaded.", textArea, juce::Justification::centred);
hScrollBar.setBounds(bounds.removeFromBottom(scrollbarHeight).toNearestInt());
return; return;
} }
@ -225,13 +233,6 @@ void WaveformDisplay::paint(juce::Graphics& g)
auto waveArea = waveBounds.withTrimmedLeft(keyWidth); auto waveArea = waveBounds.withTrimmedLeft(keyWidth);
drawWaveform(g, waveArea); drawWaveform(g, waveArea);
drawSliceMarkers(g, waveArea); drawSliceMarkers(g, waveArea);
// Scrollbar at bottom
hScrollBar.setBounds(juce::Rectangle<int>(
static_cast<int>(bounds.getBottomLeft().getX()),
static_cast<int>(bounds.getBottom() - scrollbarHeight),
static_cast<int>(bounds.getWidth()),
static_cast<int>(scrollbarHeight)));
} }
void WaveformDisplay::drawKeys(juce::Graphics& g, juce::Rectangle<float> bounds) void WaveformDisplay::drawKeys(juce::Graphics& g, juce::Rectangle<float> bounds)
@ -531,10 +532,7 @@ void WaveformDisplay::resized()
{ {
auto bounds = getLocalBounds(); auto bounds = getLocalBounds();
auto scrollBarRow = bounds.removeFromBottom(static_cast<int>(scrollbarHeight)); auto scrollBarRow = bounds.removeFromBottom(static_cast<int>(scrollbarHeight));
scrollLeftBtn.setBounds(scrollBarRow.getX(), scrollBarRow.getY(), scrollBtnWidth, scrollBarRow.getHeight()); hScrollBar.setBounds(scrollBarRow);
scrollRightBtn.setBounds(scrollBarRow.getRight() - scrollBtnWidth, scrollBarRow.getY(), scrollBtnWidth, scrollBarRow.getHeight());
hScrollBar.setBounds(scrollBarRow.getX() + scrollBtnWidth, scrollBarRow.getY(),
scrollBarRow.getWidth() - scrollBtnWidth * 2, scrollBarRow.getHeight());
} }
void WaveformDisplay::mouseDown(const juce::MouseEvent& e) void WaveformDisplay::mouseDown(const juce::MouseEvent& e)

View file

@ -34,6 +34,8 @@ public:
void zoomIn(); void zoomIn();
void zoomOut(); void zoomOut();
void zoomReset(); void zoomReset();
void scrollLeft();
void scrollRight();
int xToSample(int x) const; int xToSample(int x) const;
int sampleToX(int sample) const; int sampleToX(int sample) const;
@ -63,10 +65,7 @@ private:
std::function<void(int)> onKeyClick; std::function<void(int)> onKeyClick;
juce::ScrollBar hScrollBar { false }; juce::ScrollBar hScrollBar { false };
juce::TextButton scrollLeftBtn { "<" };
juce::TextButton scrollRightBtn { ">" };
bool updatingScroll = false; bool updatingScroll = false;
static constexpr int scrollBtnWidth = 16;
juce::AudioBuffer<float> overviewBuffer; juce::AudioBuffer<float> overviewBuffer;
bool overviewDirty = true; bool overviewDirty = true;