1010
1111namespace vmc {
1212namespace detail {
13+ inline static juce::Colour baseWindowColor ()
14+ {
15+ return juce::Colour::fromRGB (45 , 48 , 52 );
16+ }
1317inline static void styleIncDecSlider (juce::Slider& s, bool readOnly = false )
1418{
1519 s.setSliderStyle (Slider::IncDecButtons);
@@ -106,6 +110,14 @@ class MainComponent::Content : public Component {
106110 loadButton.setColour (juce::TextButton::textColourOnId, juce::Colours::white);
107111 loadButton.onClick = [this ]() { loadDevice (); };
108112
113+ addAndMakeVisible (aboutButton);
114+ aboutButton.setButtonText (" About" );
115+ aboutButton.setColour (juce::TextButton::textColourOffId, juce::Colours::white.withAlpha (0 .8f ));
116+ aboutButton.setColour (juce::TextButton::textColourOnId, juce::Colours::white);
117+ aboutButton.onClick = [this ]() {
118+ showOrHideAboutDialog ();
119+ };
120+
109121 addAndMakeVisible (program);
110122 detail::styleIncDecSlider (program);
111123 program.setTooltip (" MIDI Program" );
@@ -205,7 +217,7 @@ class MainComponent::Content : public Component {
205217 auto bounds = getLocalBounds ();
206218
207219 // Base aluminum color
208- g.setColour (juce::Colour::fromRGB ( 45 , 48 , 52 ));
220+ g.setColour (detail::baseWindowColor ( ));
209221 g.fillRect (bounds);
210222
211223 // Subtle radial gradient for depth (lighter center)
@@ -255,6 +267,7 @@ class MainComponent::Content : public Component {
255267 ccEditorButton.setBounds (r2.removeFromLeft (80 ));
256268 output.setBounds (r2.removeFromRight (140 ));
257269 r2.removeFromRight (5 ); // Gap before output
270+ aboutButton.setBounds (r2.removeFromRight (70 ));
258271 loadButton.setBounds (r2.removeFromRight (70 ));
259272 saveButton.setBounds (r2.removeFromRight (70 ));
260273
@@ -375,6 +388,88 @@ class MainComponent::Content : public Component {
375388 });
376389 }
377390
391+ void showOrHideAboutDialog ()
392+ {
393+ struct AboutContent : public juce ::Component {
394+ juce::TextEditor text;
395+ AboutContent ()
396+ {
397+ addAndMakeVisible (text);
398+ text.setMultiLine (true , true );
399+ text.setReadOnly (true );
400+ text.setScrollbarsShown (true );
401+ text.setCaretVisible (false );
402+ text.setFont (juce::Font (juce::FontOptions (14 .0f )));
403+ text.setColour (juce::TextEditor::backgroundColourId, juce::Colours::transparentBlack);
404+ text.setColour (juce::TextEditor::outlineColourId, juce::Colours::transparentBlack);
405+
406+ juce::String aboutText;
407+ aboutText << " Virtual MIDI Controller\n\n " ;
408+ aboutText << " Version " << VMC_VERSION_STRING << " \n\n " ;
409+ aboutText << " Copyright (c) Kushview, LLC.\n\n " ;
410+ aboutText << " This program is free software: you can redistribute it\n " ;
411+ aboutText << " and/or modify it under the terms of the GNU General\n " ;
412+ aboutText << " Public License, version 3.\n\n " ;
413+ aboutText << " This program is distributed in the hope that it will be\n " ;
414+ aboutText << " useful, but WITHOUT ANY WARRANTY; without even the\n " ;
415+ aboutText << " implied warranty of MERCHANTABILITY or FITNESS FOR A\n " ;
416+ aboutText << " PARTICULAR PURPOSE.\n\n " ;
417+ aboutText << " For full terms, see: \n " ;
418+ aboutText << " https://www.gnu.org/licenses/gpl-3.0.en.html" ;
419+
420+ text.setText (aboutText, juce::dontSendNotification);
421+ }
422+
423+ void resized () override
424+ {
425+ text.setBounds (getLocalBounds ().reduced (10 ));
426+ }
427+ };
428+
429+ if (aboutWindow && aboutWindow->isVisible ()) {
430+ aboutWindow->closeButtonPressed ();
431+ return ;
432+ }
433+
434+ class AboutWindow : public juce ::DocumentWindow {
435+ public:
436+ AboutWindow ()
437+ : juce::DocumentWindow (" About Virtual MIDI Controller" , detail::baseWindowColor(), juce::DocumentWindow::closeButton)
438+ {
439+ auto * contentComp = new AboutContent ();
440+ setUsingNativeTitleBar (true );
441+ setContentOwned (contentComp, true );
442+ setResizable (false , false );
443+ setSize (540 , 360 );
444+ setAlwaysOnTop (true );
445+ setVisible (false );
446+ }
447+
448+ void closeButtonPressed () override
449+ {
450+ setVisible (false );
451+ setAlwaysOnTop (false );
452+ }
453+
454+ void focusLost (juce::Component::FocusChangeType) override
455+ {
456+ // nooop
457+ }
458+ };
459+
460+ if (! aboutWindow) {
461+ aboutWindow.reset (new AboutWindow ());
462+ aboutWindow->centreAroundComponent (this , aboutWindow->getWidth (), aboutWindow->getHeight ());
463+ }
464+
465+ if (aboutWindow) {
466+ aboutWindow->centreAroundComponent (this , aboutWindow->getWidth (), aboutWindow->getHeight ());
467+ aboutWindow->setVisible (true );
468+ aboutWindow->toFront (true );
469+ aboutWindow->setAlwaysOnTop (true );
470+ }
471+ }
472+
378473private:
379474 friend class MainComponent ;
380475 MainComponent& owner;
@@ -385,6 +480,8 @@ class MainComponent::Content : public Component {
385480 juce::TextButton ccEditorButton;
386481 juce::TextButton saveButton;
387482 juce::TextButton loadButton;
483+ juce::TextButton aboutButton;
484+ std::unique_ptr<juce::DocumentWindow> aboutWindow;
388485 std::unique_ptr<juce::FileChooser> fileChooser;
389486 Device device;
390487 juce::Value midiChannelValue;
0 commit comments