UIについては公式Tutorialを以下の順番にやるのが良いと思います。
以下はJUCEApplicationを継承したApplication classの最小コードです。
class MainComponentTutorialApplication: public juce::JUCEApplication
{
public:
MainComponentTutorialApplication() {}
const juce::String getApplicationName() override
{
return ProjectInfo::projectName;
}
const juce::String getApplicationVersion() override
{
return ProjectInfo::versionString;
}
bool moreThanOneInstanceAllowed() override { return true; }
void initialize (const juce::String& commandLine) override
{
// [3] initializing the mainWindow instance.
mainWindow.reset(new MainWindow(getApplicationName()));
}
void shutdown() override
{
// [4] nullifying the pointer
mainWindow = nullptr;
}
void systemRequestedQuit() override
{
quit();
}
// [1] nested class inside the application class
class MainWindow: public juce::DocumentWindow {
public:
MainWindow(juce::String name): DocumentWindow(name,
juce::Colours::lightgrey,
DocumentWindow::allButtons)
{
// [1] setup
setUsingNativeTitleBar(true);
// [5] resize will work automatically when setResizable is set
// you can write custom layout logic inside resized()
// virtual func of Component class
setResizable(true, false);
centreWithSize(300, 200);
setVisible(true);
}
void closeButtonPressed() override
{
JUCEApplication::getInstance()->systemRequestedQuit();
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
};
private:
// [2] declare as a private member of the class.
// unique_ptr is a scoped pointer.
// It gets deleted automatically when it becomes out of scope.
std::unique_ptr<MainWindow> mainWindow;
};
この状態からComponentを追加していきます。
JUCEでは、全てのGUIエレメント(button, slider, textFieldなど)がComponentクラスを継承(derive, inherit)しています。
ApplicationにまずmainComponentを追加し、その他のGUIエレメントはmainComponentのchildrenとして追加されます。
ProjucerのFile Explorerの+ボタンをクリックし、Add New Component class(split between a CPP & header)を選択し、ファイル名をMainComponentとします。
// MainComponent.h
class MainComponent : public juce::Component
{
public:
MainComponent();
~MainComponent() override;
// [5] two important virtual functions
void paint (juce::Graphics&) override;
void resized() override;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
Componentベースクラスには2つ重要なvirtual function(subclassでoverrideするfunction)があり、全てのcomponentで定義される必要があります。
Component::paint() はこのcomponentがスクリーン上にどのように描画されるかを定義し、
Component::resized() は画面リサイズ時の挙動を定義します。
// MainComponent.cpp
void MainComponent::paint (juce::Graphics& g)
{
g.fillAll (juce::Colours::lightblue);
g.setColour (juce::Colours::darkblue);
g.setFont (20.0f);
g.drawText ("Hello, World!",
getLocalBounds(),
juce::Justification::centred,
true);
}
// Main.cpp
#include "MainComponent.h"
// MainWindow constructor
MainWindow(juce::String name): DocumentWindow(name,
juce::Colours::lightgrey,
DocumentWindow::allButtons)
{
setUsingNativeTitleBar(true);
// assigning MainComponent instance in setContentOwned()
setContentOwned(new MainComponent(), true);
// [6 ] in order for getWidth() and getHeight() to work,
// MainComponent needs to have setSize() set in it's constructor
centreWithSize(getWidth(), getHeight());
setVisible(true);
}
// MainComponent.cpp
MainComponent::MainComponent()
{
setSize(400, 300);
}
[6] componentのサイズ設定し忘れはJUCEでよくあるbugの原因、、、とのことです。
これらvirtual functions はresized()、paint()の順で、componentのinitialize時に一度呼ばれます。これらは必要に応じてシステムがコールするので、プログラマーが自分でコールしてはいけません。
[5] MainWindowのconstructorでsetResizable()を設定すると、Component::resized()に何も記述しなくてもリサイズは動作します。child elementなどのレイアウトに関することをresized()に記述出来ます。
paint(juce::Graphics& g) にはGraphicsクラスのインスタンスのアドレス g が渡されています。この g を使って色々なUIエレメントを描画出来ます。ほぼ全ての場合、Graphicsクラスはpaint()内でのみ使用されます。
Fontの設定
g.setFont(20.0f); // set size of the font
juce::Font mainComponentFont ("Times New Roman", 20.0f, juce::Font::italic);
// font styles can be used as a bitmask
juce::Font mainComponentFont ("Times New Roman", 20.0f, juce::Font::bold | juce::Font::italic);
g.setFont(mainComponentFont); // can pass a Font object
// using getLocalBounds()
g.drawText(currentSizeAsString, getLocalBounds(), juce::Justification::centred, true);
// using Justification::Flags
g.drawText(currentSizeAsString, getLocalBounds(), juce::Justification::topLeft, true);
// using explicit size and position
g.drawText(currentSizeAsString, 20, 40, 200, 40, juce::Justification::centred, true);
// draws a line with 5 pixels width from (10, 300) to (590, 300),
g.drawLine (10, 300, 590, 300, 5);
// draws a rectangle with origin (300, 120), width 200, height 170
g.drawRect (300, 120, 200, 170);
// draws a ellipse inside a rectangle with origin (530, 120), width and height 60 pixels
g.drawEllipse (530, 120, 60, 60, 3);