In der Regel verfügen Audio-Plugins – egal ob Effekt oder Instrument – über eine grafische Benutzeroberfläche (GUI). Aus diesem Grund wollen wir heute mal damit anfangen, eine minimale Anwendung in einem Fenster zu generieren und ein wenig das Aussehen unserer App ändern. Diese Einführung ist an den offiziellen Tutorials der JUCE Website angelehnt, daher nutze ich die gleichen Namen für die Projekte.

Wir starten zunächst mal den Projucer und wählen die Option GUI Application. Das Projekt nennen wie MainWindowTutorial. Oben rechts im Projucer kann ich an dieser Stelle auswählen, welche Quellcode-Dateien erzeugt werden sollen. Hier wählen wir „Create a main.cpp file only„.

Nun klicken wir unten auf Create… und wechseln dann in unsere IDE (hier: Visual Studio 2019). Die main.cpp wurde folgendermaßen generiert:

#include <JuceHeader.h>

//============================================================
class MainWindowTutorialApplication  : public JUCEApplication
{
public:
    //========================================================
    MainWindowTutorialApplication() {}

    const String getApplicationName() override { 
        return ProjectInfo::projectName; }
    const String getApplicationVersion() override { 
        return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed() override { 
        return true; }

    //========================================================
    void initialise (const String& commandLine) override
    {
        // Add your application's initialisation code here..
    }

    void shutdown() override
    {
        // Add your application's shutdown code here..
    }

    //========================================================
    void systemRequestedQuit() override
    {
        // This is called when the app is being asked to quit: 
        // you can ignore this request and let the app carry on 
        // running, or call quit() to allow the app to close.
        quit();
    }

    void anotherInstanceStarted (const String& commandLine) override
    {
        // When another instance of the app is launched while this 
        // one is running, this method is invoked, and the 
        // commandLine parameter tells you what the other instance's 
        // command-line arguments were.
    }
};

//============================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (MainWindowTutorialApplication)

Hier wurde die MainWindowTutorialApplication Klasse erstellt, die eine Unterklasse von JUCEApplication ist. An dieser Stelle sei kurz angemerkt, dass es eine gute Idee ist sich die JUCE Klassenübersicht im Browser als Lesezeichen abzulegen. Dort kann man immer mal schnell nachschauen, wozu die einzelnen Klassen gut sind.

Wie man sieht, macht die Anwendung an dieser Stelle noch nichts. Wir müssen uns daher eine Klasse erstellen, die ein Fenster auf den Bildschirm zaubert. Diese nennen wir MainWindow, und sie erbt die Eigenschaften von DocumentWindow. Diese Klasse fügen wir folgendermaßen in unseren Code ein:

#include <JuceHeader.h>

//============================================================
class MainWindowTutorialApplication  : public JUCEApplication
{
public:
    //========================================================
    MainWindowTutorialApplication() {}

    const String getApplicationName() override { 
        return ProjectInfo::projectName; }
    const String getApplicationVersion() override { 
        return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed() override { 
        return true; }

    //=========================================================
    class MainWindow : public DocumentWindow
    {
    public:
        MainWindow(String name) : DocumentWindow(name,
            Colours::lightgrey,
            DocumentWindow::allButtons)
        {
            centreWithSize(300, 200);
            setVisible(true);
        }

        void closeButtonPressed() override
        {
            JUCEApplication::getInstance()->systemRequestedQuit();
        }

    private:
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
    };

    //========================================================
    void initialise (const String& commandLine) override
    {
        // Add your application's initialisation code here..
    }

    void shutdown() override
    {
        // Add your application's shutdown code here..
    }

    //========================================================
    void systemRequestedQuit() override
    {
        // This is called when the app is being asked to quit: 
        // you can ignore this request and let the app carry on 
        // running, or call quit() to allow the app to close.
        quit();
    }

    void anotherInstanceStarted (const String& commandLine) override
    {
        // When another instance of the app is launched while this 
        // one is running, this method is invoked, and the 
        // commandLine parameter tells you what the other instance's 
        // command-line arguments were.
    }

private:
    std::unique_ptr<MainWindow> mainWindow;
};

//============================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (MainWindowTutorialApplication)

Damit das Fenster auch sichtbar wird, müssen wir in der initialise() Methode noch Folgendes einfügen:

void initialise (const String& commandLine) override
{
    mainWindow.reset(new MainWindow(getApplicationName()));
}

…und der shutdown() Methode fügen wir dies hinzu:

void shutdown() override
{
    mainWindow = nullptr;
}

Was haben wir hier? Dem Konstruktor von MainWindow wird ein String name übergeben und dieser dann an den Konstruktor der Oberklasse DocumentWindow weitergegeben. Diesen Namen bekommen wir in der in der initialise() Methode mithilfe der getApplicationName() Methode. D.h. der Name unseres Fensters entspricht dem Namen des Projektes „MainWindowsTutorial“.

Außerdem geben wir dem Konstruktor von DocumentWindow noch eine Farbe (Colours::lightgrey) und die Information mit, dass wir alle Buttons zum Schließen, Vergrößern und Minimieren eines Fensters haben wollen (DocumentWindow::allButtons). Wenn wir das Programm nun kompilieren und ausführen, bekommen wir ein minimalistisches Fenster.

Dieses Fenster kann ich nun verschieben, minimieren, maximieren und schließen um die Anwendung zu beenden.

Um ein wenig besser zu verstehen, was hier genau passiert, könnten wir nun etwas im Code herumpfuschen. Wenn ich den Namen der Applikation ändern möchte, kann ich das tun, indem ich der Methode getApplicationName() folgendermaßen ändere:

const String getApplicationName() override {
        //return ProjectInfo::projectName; 
        return "It's JUCE Adventure Time!"; }

Damit ich meinen neuen Titel auch gut lesen kann, wäre es sinnvoll das Fenster etwas größer darzustellen. Dazu gehe ich in den Konstruktor der MainWindow Klasse und ändere die Dimensionen beim Aufruf der centreWithSize() Funktion.

centreWithSize(600, 400);

Der graue Hintergrund ist jetzt auch nicht so doll. Dem Konstruktor von DocumentWindow kann ich natürlich auch andere Farben mitgeben. Es gibt verschiedene Möglichkeiten das zu tun. Genaueres findet man in der Referenz der Klasse Colour. Aber wir beschränken uns jetzt mal auf die Farben, die hier ausgeschrieben werden.

In Visual Studio kann man hierzu ein Rechtsklick auf das hier angegebene lightgrey ausführen und dann im Kontextmenü auf „Definition einsehen“ klicken. Schon bekommt man eine Liste der verfügbaren Farben. Aber wie gesagt, man ist auf diese Farben nicht beschränkt.

Ich wähle jetzt einfach mal „peachpuff„, weil das so bescheuert klingt. Als letztes will ich noch versuchen das native Look’n’Feel von Windows für das Fenster einzustellen. Wenn ich ehrlich bin, mag ich das Aussehen der Fensterleiste von JUCE nicht so sehr. DocumentWindow hat hierfür eine Methode namens setUsingNativeTitleBar(), die ich einfach in den MainWindow Konstruktor einfüge und als Argument ein true übergebe.

Zu guter letzt will ich noch, dass mein Fenster auch vergrößert werden kann und dafür füge rufe ich die Funktion setResizable() mit den beiden Parametern true, true auf. Diese besagen, dass zum einen das Fenster vergrößert und verkleinert werden kann und zum anderen, dass mir dazu eine Art Anfasser unten rechts an der Ecke dargestellt wird. Wenn ich allerdings das native Look’n’Feel von Windows benutze, dann sehe ich keinen Anfasser!

MainWindow(String name) : DocumentWindow(name,
            Colours::peachpuff,
            DocumentWindow::allButtons)
        {
            centreWithSize(600, 400);
            setUsingNativeTitleBar(true);
            setResizable(true, true);
            setVisible(true);
        }

Ja gut, die Farbe ist jetzt auch nicht gerade besser … Aber ich habe meine erste GUI Anwendung erzeugt und ein wenig mit den Parametern herumgespielt! Im nächsten Tutorial wollen wir auch mal etwas in das Fenster packen…