Juce Tutorial
Juce Tutorial
Juce Tutorial
Table of Contents
Introduction...................................................................................................................................... 3
Chapter 1 - The Starting Point................................................................................ 5
Chapter 2 –Component Basics................................................................................ 7
What is a Component?.......................................................................................................... 7
How can I actually ‘do stuff’ with a Component?.....................................7
How do I add a Component?......................................................................................... 8
Chapter 3 – Listening for Messages............................................................ 14
What are these message classes?......................................................................... 14
Where does the Listener go?...................................................................................... 15
What about the other message types?............................................................ 20
Chapter 4 – Painting and Decorating........................................................ 23
Where do I paint my Component?........................................................................23
What is a ‘Graphics’?............................................................................................................ 24
How big is my Component?..........................................................................................24
How do I actually draw something?.................................................................... 25
Why am I painting 'under' the widgets?......................................................... 27
Why is everything black?.................................................................................................27
How can I draw text?.......................................................................................................... 30
Chapter 5 – Juce Strings.............................................................................................. 34
What is a String?...................................................................................................................... 34
Where can I use the String?........................................................................................ 35
What can I do to a String?............................................................................................ 38
How can I modify a String?.......................................................................................... 40
How can I combine Strings?........................................................................................ 41
How can I use a number as a String?............................................................... 43
How can i use a String as a number?................................................................ 44
How do I know how long a String is?................................................................47
How can I clear a String?................................................................................................47
Chapter 6 – File handling (part 1)..................................................................49
What is a File?............................................................................................................................. 49
How do I open a File?.........................................................................................................51
How do I read text from a File?............................................................................... 54
How do I choose a file?.................................................................................................... 55
How do I save text to a file?....................................................................................... 58
What else can I do with Files?...................................................................................60
1
Juce Programming Tutorial by haydxn
Chapter 7 – Intermission............................................................................................ 61
How can I use the debugger as a console?................................................ 62
How can I make my own console?....................................................................... 64
How can I make a class that's like another class?.............................. 64
How should I make my new Component?....................................................65
How can I use my new Component?................................................................. 69
How can I make my Component more useful?....................................... 71
Answers to problems........................................................................................................ 73
Chapter 3.......................................................................................................................................... 73
Chapter 4.......................................................................................................................................... 73
Chapter 5.......................................................................................................................................... 73
Chapter 6.......................................................................................................................................... 74
Chapter 7.......................................................................................................................................... 75
2
Juce Programming Tutorial by haydxn
Introduction
Then I discovered Juce. And again I nearly wet my pants. But this
time, I mean that in a good way; all of a sudden I was able to ‘do
stuff’ that looked nice and wasn’t scary! It took me a while to get
my head around it all though, but I can promise you that it’s a far
easier experience than any other libraries I’ve happened upon.
3
Juce Programming Tutorial by haydxn
Juce has many classes that you can use throughout your program.
Pretty much every base is covered (Strings, Arrays, Files, etc) and
you’ll find you can make do without any other libraries. If I started
by teaching you these though, you’d have to take a lot of stuff for
granted when I give you the basic tools to put your results on the
screen.
The aim of this tutorial then is to give you all the knowledge you
need to be able to start programming your applications using the
Juce classes. I won’t cover all of the classes here, as many are
very specialised. With luck, you should understand enough about
how Juce does things by the end that you can easily figure out
how to use these other classes by yourself, by reading the (very
detailed) documentation. I will write additional guides on how to
use certain Components though (for example, the ListBox, the
TreeView, etc…).
With all that said, I think it’s high time we began the tutorial!
4
Juce Programming Tutorial by haydxn
I’m not going to try to teach you how to build a whole Juce project
from scratch. That would baffle you almost immediately. What I’m
going to do is provide you with a set of ‘starting point’ files, and let
you use them as a sort of ‘sandbox’ for learning the Juce way.
These files are slightly modified versions of Jules’ original demo
app source files. They should be slightly easier to begin with.
Provided you have Juce properly setup on your system (with the
correct paths and dependencies all set appropriately), the tutorial
files should compile without a problem. If you do have any
problems, it means that either your project has some incorrect
settings, or your environment does not have all the include/lib
paths set properly. This is the time to discover such things! If you
find yourself scratching your head, go to the forum and make
some enquiries. You may spot what the error is yourself.
5
Juce Programming Tutorial by haydxn
6
Juce Programming Tutorial by haydxn
What is a Component?
7
Juce Programming Tutorial by haydxn
You’ll notice that not a lot is done with it. This explains why the
application is so boring! We only have a constructor and a
destructor. You’ll also spot the important bit – the class ‘is’ a
Component, thanks to the inheritance declaration after the class
name.
8
Juce Programming Tutorial by haydxn
So, we have two simple things to take care of before we can add
these Components.
1) Add a private pointer member (of the desired type) to our class.
Examples...
TextButton* button1;
Slider* slider1;
Label* label;
Examples...
9
Juce Programming Tutorial by haydxn
TextButton* button1;
TextButton* button2;
TextButton* button3;
Slider* slider;
Label* label;
In the constructor...
addAndMakeVisible (myComponent);
Of course, this will also need to happen in the constructor. Add the
following lines after the Components have been created:
addAndMakeVisible (button1);
addAndMakeVisible (button2);
addAndMakeVisible (button3);
addAndMakeVisible (slider);
addAndMakeVisible (label);
10
Juce Programming Tutorial by haydxn
deleteAllChildren ();
This will unregister all of the Components, and also delete them as
it goes along. One call to rule them all. Splendid.
With that, we’re very nearly there. So far we’ve (1) added the child
Components, and (2) made sure that they’re cleared away. If
you’re in ‘sharp’ mode, you’ll perhaps have realised one crucial
omission. The Component, while clever, is not magical enough to
instinctively know how you want your controls arranged, so you
have to do that yourself. This is very simple, and the easiest way
to do this is by using the Component function ‘setBounds (…)’. A
quick check of the docs shows that it has the following form:
11
Juce Programming Tutorial by haydxn
We need to make a call like this for each one of our child
Components, and then we will be all set to compile and see our
wonderful creation! It is possible to arrange these dynamically,
based on the current size of the Component they reside in, but for
now we shall keep things simple. If you look at the file
MainHeader.h, you’ll see some variables set to make the
application configuration easy; two of these define the size of the
app window – it is 300x300. This is actually the size of the
DialogWindow, and the MainComponent sits within that (a little
shorter due to the window’s title bar, and there are a few pixels of
padding around the edges). That’s plenty of guidance though – we
just make sure our controls don’t go too wide or off the bottom of
the window!
’s make the following bounds calls (in the constructor, after the
Components have all been created and added):
12
Juce Programming Tutorial by haydxn
13
Juce Programming Tutorial by haydxn
There are several types of message defined in Juce. For the time
being, we shall ignore the actual types, and just focus on the fact
that they use ‘messages’. I shouldn’t need to explain what a
message is, as it is perfectly named; a message is something one
object sends to another object to notify it that something has
happened. In Juce, there are two categories of message-handling
object you need to know about, and they are very simple. These
are broadcasters and listeners.
14
Juce Programming Tutorial by haydxn
15
Juce Programming Tutorial by haydxn
Rather than trying to figure out where the Listener should go, it’s
better to think what the Listener would be. The Listener object is
what responds to a message. We want our program to respond to
the button messages. Our MainComponent is the program.
Therefore, our MainComponent needs to be a ButtonListener.
16
Juce Programming Tutorial by haydxn
myButton->addButtonListener (ButtonListener*)
button1->addButtonListener (this);
button2->addButtonListener (this);
button3->addButtonListener (this);
17
Juce Programming Tutorial by haydxn
How can we demonstrate that this works? Well, we’ve already got
a Label on our panel, which currently just shows “text here”. We’ll
get it to change the text when we click on the buttons.
If you look at the Juce documentation for the Label class, you’ll
see that it has many functions. If you’re an excitable and
enthusiastic programmer, you’ll probably have had a scan through
several of the class docs. All the ready made Components have a
wealth of specific functions to ‘do stuff’, which is something that
18
Juce Programming Tutorial by haydxn
The parameters may look a bit confusing at first, but we’ll keep it
simple for now. The ‘sendMessage’ parameter should indicate to
you that this class is some kind of broadcaster. It is actually a
ChangeBroadcaster, something which we’ll cover later in this
chapter. For now, we’re going to ignore this fact, and just pass it
‘false’ so that it doesn’t send a message. The first parameter (if
you’re not wholly used to C++ syntax) may look a bit scary, but
the important thing is that it is a String. Again, as earlier, we’re
going to just use a string literal (and the T(“<string>”) macro)
here, as Strings will be covered in a later chapter.
So, to set the text on this Label, we use the following line:
This will cause the Label to change its text to “Text of my choice”.
We want our TextButtons to change the text, so we put a line like
this in the response section for each of our buttons in
buttonClicked (), as follows:
19
Juce Programming Tutorial by haydxn
If you compile this now, you’ll see the Label’s text change when
you click the buttons. This gives you a prime opportunity to try out
various exciting swear-words for each TextButton! Remember that
the documentation lists many functions available for the
Component types we’re using. See if you can carry out the
following tasks:
Task
20
Juce Programming Tutorial by haydxn
In the constructor...
slider->addChangeListener (this);
The first thing we’ll need to do is check that it is indeed the Slider
that has changed. This is a simple pointer check, as with the
Button pointer check we did before.
21
Juce Programming Tutorial by haydxn
With this function now added, a compile should show this program
to work as expected. Now we are able to respond to both Button
messages and Change messages without difficulty.
We’re still not quite ready to delve into the important world of the
Juce core classes. There is one more aspect of Components we
should understand before we can properly do things behind the
scenes, and that is the paint () function. The next chapter will
cover graphics.
22
Juce Programming Tutorial by haydxn
We’re now going to have a look at how we can make use of this
feature ourselves. It’s actually quite simple, and by the end of this
chapter, you'll have the tools to begin to design the appearance of
your own widgets.
23
Juce Programming Tutorial by haydxn
What is a ‘Graphics’?
There are a few things you should know about the canvas you'll be
drawing on. Firstly, it's actually called a 'context'! The next thing is
the coordinate system. All the drawing operations take coordinates
(that should be obvious – you need to specify where these things
will get drawn), and these coordinates start from the top-left
corner of the context. That means that bigger values of 'y' refer to
a point further down the context, and bigger 'x' values are off to
the right.
But hold on a moment! Right now, the only place we know the
exact coordinates for is the top-left corner. How are we going to
know how big to draw things? How do we know the size of the
context, so that we don't go off the edges? For this important
information, we turn to the Component itself.
The Component has had its bounds set (just like we did with the
widgets in chapter 2), so we know it has a size. We can get this
size with two simple calls – getWidth() and getHeight(). The
paint() function is 'inside' the Component, so we have direct
24
Juce Programming Tutorial by haydxn
The 'g' is the Graphics parameter we've been given to play with.
Also, you'll notice that this is the first time we've used a '.' to call a
function. This isn't a big deal, just remember that in this case
25
Juce Programming Tutorial by haydxn
Task
26
Juce Programming Tutorial by haydxn
There are several important things to notice about the result. One
is that the rectangles are both black. The other is that our widgets
appear on top of them!
The rectangles we've drawn are both black, and drawn on top of
grey – the colour of the DialogWindow. You may remember that a
Component starts out completely clear, the grey we see is what
the Component behind looks like. We've not told the rectangles to
use any colour in particular, and black is as good a default as any!
We can of course choose any colour we like, and this is made easy
with the Colour class – and even easier with the Colours class.
For now, we'll skip 'creating' our own Colour objects (because we
don't need to do that just yet!), and instead make use of the
'Colours' class. 'Colours' is basically a palette of predefined colours
that we can get pick from by name.
27
Juce Programming Tutorial by haydxn
Examples of Colours...
Colours::lightblue;
Colours::red;
Colours::seashell;
g.setColour (Colours::hotpink);
g.fillRect (0,0, getWidth(), getHeight());
Notice how the Colour is retrieved from Colours 'on the spot',
using the scope resolution operator '::'. This will draw a big pink
rectangle over the whole context. Note that this can be achieved
in one line using this:
g.fillAll (Colours::hotpink);
Try compiling and running both versions. You'll notice that they
both look the same, like the image below:
28
Juce Programming Tutorial by haydxn
Very pretty indeed! You notice of course that the black rectangles
have gone; this illustrates that everything is drawn in order. If you
draw something you'll draw on top of the previous operation. If
you used a transparent Colour, you'd still see them.
So, use one of these functions to try drawing our pink rectangle
with a transparent pink Colour. This requires setting the alpha
value to a proportion (between 0 (clear) and 1 (opaque)). Alter the
line where you set the Colour, so that your Colour setting looks
like this:
Colours::hotpink.withAlpha (0.9f);
The 'f', in case you didn't know, goes after a decimal value to
indicate that it is a float constant. Build it, and look at the new
panel.
29
Juce Programming Tutorial by haydxn
Lastly, draw some text inside this border. The absolute easiest
way to do this is to use the 'drawFittedText()' function. This takes
30
Juce Programming Tutorial by haydxn
Add these lines after the rounded rectangle has been drawn:
g.setColour (Colours::white);
g.drawFittedText (
T("painted text"),
10, 160, getWidth()-20, getHeight()/3,
Justification::centred, 1 );
Notice that it's given the same rectangle bounds as the rounded
rectangle we just drew. Compiling and running this gives us:
So now we can draw text! But it's really small, compared to the
space it's in. How can we make the text bigger? Well, when you're
using a word processor, to change the size of your text you edit
the font properties. There is a whole Juce class dedicated to fonts
(called 'Font', interestingly enough!) but we're not going to look at
it in detail here. The Graphics class has a shortcut function, which
allows us to simply specify a new font height. Add the following
line before the drawFittedText call:
31
Juce Programming Tutorial by haydxn
g.setFont (40);
One last thing I've not yet mentioned is that all the coordinate
parameters are floating point values. Why would this be, if they're
referring to pixels? Well, Juce draws with sub-pixel accuracy, and
it's all anti-aliased. What that means, to those who aren't robots, is
that everything can be smooth, sharp and precise. You don't have
to restrict your coordinates to heavy 'quantised' blocks!
So, we've seen how Components can be drawn on, using the
Graphics class. We've also seen that there are several other
classes that a 'Graphics' can make use of (including the Colour and
the Font). Perhaps by now you can imagine (and appreciate) the
kind of operations the TextButton uses in its own paint() function.
Don't worry if it seems beyond you – it won't be too long before
you're easily making your own widgets, and in a later chapter we
will do just that!
32
Juce Programming Tutorial by haydxn
33
Juce Programming Tutorial by haydxn
The first core class we shall investigate is the String. We've already
had some encounters with them, but we've never actually held a
String object and done something to it.
What is a String?
34
Juce Programming Tutorial by haydxn
a set of ready made utility classes), and comes with its own String
class. And very handy it is too!
With the String class, you don't have to worry at all about how the
memory is organised, or what might be involved in manipulating
strings of characters. The interface is entirely logical and intuitive,
which I hope to demonstrate in this chapter. You can really use
the String as if it were a built-in C++ type, like an int or a float.
You can create one anywhere and just use it however you need.
Let's try it out. We'll give our MainComponent a String member:
String text;
// or using constructor...
String text (T("Text goes here!"));
Notice that it's perfectly valid to assign a value to the text after the
variable has been created (If you've never made null-terminated-
strings before, that won't be interesting to you!). We're going to
allow our 'text' to start empty.
As just explained, you can assign the String whenever you like.
Our String 'text' is always available to use in any of our
MainComponent member functions.
35
Juce Programming Tutorial by haydxn
In buttonClicked() ...
if (button == button1)
{
text = T("Monkey");
}
else if (button == button2)
{
text = T("Tennis");
}
This will cause button1 and button2 to set the text. Right now
though, this won't appear to do anything (the Label is no longer
connected to our buttons). We need to make use of this String so
we can see the result.
We're not going to use the Label, we're going to use our 'home-
made' text display code (in the paint() function). The
'drawFittedText()' call currently uses a string literal.
36
Juce Programming Tutorial by haydxn
In MainComponent::paint()...
g.drawFittedText (
text,
10, 160, getWidth()-20, getHeight()/3,
Justification::centred, 1 );
repaint ();
}
Compile this, and you should see that the top two buttons will now
change the text on the bottom. The third button still changes the
Label. In case it wasn't entirely clear, what we've done is call
'repaint()' (the Component's function for getting it to update)
37
Juce Programming Tutorial by haydxn
So we've seen how we can assign a String, and we've seen that
we can paint its value on our Component's face. It's now time to
see how the String can be really used.
button3->setClickingTogglesState (true);
button3->getToggleState ();
38
Juce Programming Tutorial by haydxn
We're going to use the state of this button to choose whether the
text is shown in upper or lower case. One thing we could do is set
the value of the String in the button call. This would be like the
assignments for button1 and button2, but would have a
conditional check on the button's state to decide what to do. We
don't want to do that here though. We've already seen that we
can assign a String, we want to try something new!
Before we code the behaviour, let's change the text on the button
to reflect its new purpose. In the constructor, where button3 is
created, change the text to say "display mode" or something to
that effect.
g.drawFittedText (
text,
10, 160, getWidth()-20, getHeight()/3,
Justification::centred, 1 );
This draws the exact contents of text to the panel. We don't want
to change 'text' itself though, we just want to change how the
text is displayed. For this, we're going to use a temporary (local)
String variable.
Just before the above function call, we'll create a String object.
String textToDraw;
39
Juce Programming Tutorial by haydxn
text.toUpperCase ();
text.toLowerCase ();
if (button3->getToggleState ())
{
// button is ON... (getToggleState() == true)
textToDraw = text.toUpperCase ();
}
else
{
// button is OFF...
textToDraw = text.toLowerCase ();
}
40
Juce Programming Tutorial by haydxn
We have now got a local String which exists for the duration of the
function. Its value is set (according to the state of button3), and
then it is painted. Except that it isn't yet! We're still drawing the
main text String! Change the String parameter in drawFittedText()
from text to textToDraw. Compile and run it.
Now button1 and button2 set the text, and button3 just
changes the case of the displayed String. Remember too, that the
String member text is not actually changed by button3.
Many of the other String functions can be used in this way. In the
documentation, any String functions that return a const String will
return a modified version of the String, leaving the original intact.
Also, the fact that they return String objects means we can stack
these functions up. For example, if we wanted to have a
capitalised, quoted version of a String, we could do this:
textToDraw = text.toUpperCase().quoted();
In paint()...
if (button3->getToggleState ())
{
// button is ON... (getToggleState() == true)
41
Juce Programming Tutorial by haydxn
This shows two things; you can add several items together at once
(as many as you like!) and you can also mix string literals and
String objects.
It's important to bear in mind exactly how the T("") macro works
when using them in String assignments. While we've so far found
that we can treat a T("text") string literal as if it were a Juce String
(when calling functions, for example), it's actually not a String.
The reason it matters here is because you can't 'add' two of these
together like you can with Strings. What a T("") actually becomes
in your code is a pointer to a null terminated string. Adding this to
a String is fine because it knows how to deal with them, but you
can't add two together, because they won't understand what want
them to do. In any case, there's little reason to want to put two
together (when you can just combine their contents into one), but
you should be aware of it.
42
Juce Programming Tutorial by haydxn
The String takes care of converting the number into text, so that it
can be manipulated as a series of characters rather than a
number.
Now, when you adjust the Slider, the label is updated with a
rounded version of the value (to two decimal places). Easy!
43
Juce Programming Tutorial by haydxn
Task
The two lines you'll need are in the answers appendix. Have a look
through the Label class documentation and see if you can figure
them out for yourself.
44
Juce Programming Tutorial by haydxn
Once you've converted the Label, try building the app. If you click
on the Label, you'll now be able to type something in! Who'd have
thought we'd have such features built in to our quiet, unassuming
test app!
label->getText ();
45
Juce Programming Tutorial by haydxn
Notice again how we can 'stack up' these function calls. We're
getting the double value of the String retrieved from getText(),
and we're assigning it to a variable named 'value'.
We'll also want to set the value of the slider, using the setValue()
function. Here are the amendments you need to make:
Now if you compile and run this application, try entering a value. It
will happily accept decimal values. If you enter a value outside the
range of the slider, it will be clipped. If you enter text that is not a
number, then you get zero. That's handy – built-in error checking!
46
Juce Programming Tutorial by haydxn
We're almost done with the String class now. Before we move on
to something more interesting, I'll quickly point out two things that
the String class can be which you'll undoubtedly make use of at
some stage.
if (text == String::empty)
{
// there is no text...
}
else text = String::empty // clear the text!
47
Juce Programming Tutorial by haydxn
In the next chapter, we'll learn how to save and load stuff! This
may seem like an odd choice of topic, but the order will make
sense soon enough!
48
Juce Programming Tutorial by haydxn
Now that we have the tools to get input from the user, it's time to
look at how we might go about storing or retrieving that data. This
means that we are going to learn about Juce's File class.
What is a File?
The Juce File class allows you to hold references to files on your
computer. When you create a File object, you create a link to a
single item on a drive. The item may exist, or it may not, and it
may be either a file or a folder.
That's the basic principle behind them. We're going to begin our
study of the class by learning to open a file that already exists.
First, load up notepad (or something similar) and create a plain
text document. Type in some arbitrary nonsense, and save it in a
simple location with a memorable filename (for instance,
"C:\filetest.txt").
Before we actually try to use this file, we'll do some quick spring
cleaning. The MainComponent we've been working on is full of
things we don't need, so we'll empty it out and start fresh. For this
chapter, we'll build upon our first "Starting Point" code (which has
no child Components yet on the interface).
The last chapter introduced a text entry widget, via the Label. The
Label is not the only text entry Component though. In fact, it's
actually not really one all by itself. The reason you can use it like
that is because it can turn itself into a TextEditor – another Juce
Component type. The TextEditor has all the text entry features you
could need!
49
Juce Programming Tutorial by haydxn
TextEditor* text;
void resized ()
{
text->setBounds (10,10,getWidth()-20,getHeight()-20);
}
Note that we're giving it a 10 pixel margin, making use of the size
of MainComponent. Compile and run this.
50
Juce Programming Tutorial by haydxn
In the constructor:
text->setMultiLine (true);
text->setReturnKeyStartsNewLine (true);
Notice also that this editor has a right-click menu built in! Without
even doing anything, we've got copy/paste, and even undo/redo
features.
51
Juce Programming Tutorial by haydxn
We now have a big pad to display and edit text on. What we're
going to do next is learn how to read the file you created earlier,
and display it on our editor.
File myFile;
myFile = T("C:\\filetest.txt");
// or...
File myFile (T("C:\\filetest.txt"));
Notice that the '\\' is not a typo! In C, null terminated strings use
'backslash constants' to indicate characters that you can't type on
the keyboard. For instance, a 'newline' is '\n'. The '\' means "the
next character is a special code", which also means that you can't
type a '\' normally! To actually use a '\', you put two together (to
show that it's not a code after all, and you actually want a slash,
as it were).
52
Juce Programming Tutorial by haydxn
File currentFile;
In the constructor:
currentFile = T("C:\\filetest.txt");
Our program, when it runs, will now have a link to this file. It's not
actually tried to open the file or anything yet though, it just knows
where it is.
If (currentFile.existsAsFile ())
{
...
}
void openFileInEditor ()
{
}
53
Juce Programming Tutorial by haydxn
The editor part of the task is easy. The TextEditor has the same
'setText()' function as Label, so we can use that. We just need to
get a String of the text from the file. How can we do that?
currentFile.loadFileAsString ();
void openFileInEditor ()
{
if (currentFile.existsAsFile ())
{
text->setText (currentFile.loadFileAsString());
}
}
In the constructor:
...
currentFile = T("C:\\filetest.txt");
openFileInEditor ();
54
Juce Programming Tutorial by haydxn
Assuming that you've got the path typed correctly, when you
compile and run the app, you will see the contents of your file in
the TextEditor. Brilliant!
void chooseFileToOpen ()
{
...
}
55
Juce Programming Tutorial by haydxn
statement to quickly obtain the result. The actual File chosen (if
one is chosen) is retrieved with a separate function call.
void chooseFileToOpen ()
{
FileChooser chooser (T("Choose file to open"));
if (chooser.browseForFileToOpen ())
{
currentFile = chooser.getResult ();
openFileInEditor ();
}
}
Run this program, and marvel at how you can now suddenly
choose the file yourself! Notice in particular that you can now
trigger this with just a single function call. From this thought,
move on to the obvious idea that this is something you'll want to
be able to do at will (not just when the program starts). By now
you should know exactly what it might take to have an 'Open file'
button. Time for another simple task then!
56
Juce Programming Tutorial by haydxn
See if you can do this simple task. Example answers are in the
appendix section.
We'll take it to the next logical step, and add a button for saving.
We don't know how to save yet, but we'd do well to have a button
ready to try it out with for when we do!
Next task! Add another button, in the same way as before. Make
sure you have a relevant section within buttonClicked() for it!
57
Juce Programming Tutorial by haydxn
For the time being, let's assume that we've already managed to
have the correct file chosen. The File object currentFile has been
set to the target file, so we know where we need to write to. Our
'saveFileFromEditor' function will use this target and write the text.
How will we write the text? Well, the File class has a function
called 'appendText()', which will write a given String to the end of
the current file. If the file exists already, of course, this would
result in a merge of whatever data it held and the newly written
data. We want to make sure that, when a String is written to our
File, the file is empty. The easiest way to do this is to erase it and
create a new one! If the file doesn't already exist, then we just
need to first create it (remember that a File object is just a
reference to a potential file).
void saveFileFromEditor ()
{
if (currentFile.existsAsFile ())
currentFile.deleteFile ();
currentFile.create ();
currentFile.appendText (text->getText ());
58
Juce Programming Tutorial by haydxn
}
That's our 'save' function. We now need a chooser function, just
like the one for the open button. In fact, it's so much like it that
we can get away with copying and pasting the original, and
making a few slight edits. Below is the new one, with bold type
highlighting the bits that have changed:
void chooseFileToSave ()
{
FileChooser chooser (T("Choose file to save"));
if (chooser.browseForFileToSave ())
{
currentFile = chooser.getResult ();
saveFileFromEditor ();
}
}
Compile and run this, and you'll delight at the power of your
application! You can save and load text files!
59
Juce Programming Tutorial by haydxn
You will, of course, want to spend time making sure it looks the
part too! Get your buttons lined up nicely. There is actually a more
appropriate Component (the MenuBarComponent) but that's a bit
beyond this tutorial for the time being. It should of course go
without saying that any of the other classes can be learned
through experimentation (that's how I learned them), so if you're
feeling adventurous, try some of them out.
We've only covered file selection and text access so far. There is
obviously a bit more to file handling than that though! The File
class gives you functions that allow you to perform pretty much
any OS file operation you need (you can copy, move, create,
delete, run, etc...). As well as 'doing stuff' with (and to) files, you
also have a selection of handy functions to help you navigate your
way around the filing system. Your task is not always as simple as
"ask the user for a file", as you'll no doubt see soon enough!
One last thing before we end this chapter. You'll no doubt find
yourself growing annoyed that your 'text pad' app is a fixed size.
It's time we made things resizable! Just go into the MainHeader.h
file, and change the 'resizable' setting to true. A recompile should
give you a resizable window. This will also indicate whether or not
you've got your resized() function working correctly!
60
Juce Programming Tutorial by haydxn
Chapter 7 – Intermission
In 'the old days', we'd test out the workings of various functions
and classes by using the console. Yes, that relic i mentioned in the
introduction chapter. This provides an ever-present dumping
ground for any old data you felt like generating. You don't need to
add a Component to the screen just to have somewhere to try out
a few processes. All you need to do if you have a console is
perform some test, check the results and output some meaningful
text so you can see for yourself what happened.
That was 'the old days'. In our modern age, we're using Juce, and
it's making us very happy. How can we have the luxury of the
console, without having to actually worry about any other
libraries?
There are two things you can do to get this kind of functionality.
The first is to use a debugger, and the second is to just make your
own console. We're going to try both methods in this chapter.
61
Juce Programming Tutorial by haydxn
I'm not talking about the memory checking tools a debugger gives
you. I'm talking about the 'standard error stream' output. I use
Microsoft Visual C++ (express edition) for my Juce development,
and it provides a handy text output window that pops up when
you compile and run a debug build. There is every chance that you
don't have a debugger, and just code using a compiler and
suitable text editor. If this is the case, then you'll not be able to
test this part of the chapter, but I highly recommend looking into
it.
In Juce, we can very easily write text to this stream. We just need
to use a simple macro (like the T("") macro). The macro is:
62
Juce Programming Tutorial by haydxn
This will send ten lines of text to the debug console, each ending
with a number from 0-9.
The DBG() calls are not function calls, but macros representing
function calls – this means that you can completely omit them
from a release build. In case you don't understand how that is,
read up on macros!
Try it out for yourself. Go into any of the code we've already done,
and add some DBG() messages. You could, for example, do:
void foo ()
{
DBG( T("Function foo() called!"));
...
}
63
Juce Programming Tutorial by haydxn
How much 'like' the TextEditor is our console? Well, it doesn't need
to look any different, as it just needs to hold lines of text (we
already know it can do this well). The only thing different is its
behaviour. We just want to make something we can send lines of
text to. We don't want it to be typed on (it's just for text output).
That's about it, really.
64
Juce Programming Tutorial by haydxn
One thing that we've so far done is put all our coding into one file.
MainComponent.h has the whole class declaration and definition
all together, which a lot of people seem to frown upon. Read up
on header and implementation files if you're not already familiar
with such matters. We'll mostly not bother with creating separate
'.cpp' files, because our headers will generally be small enough to
not warrant them. We will however, at the very least, keep
'unique' classes in separate header files.
#ifndef _TEXTCONSOLE_H_
#define _TEXTCONSOLE_H_
...
#endif//_TEXTCONSOLE_H_
This is something you can read up on, but basically it stops the file
from being included more than once in any compile, using pre-
compiler macros. Traditionally, you use the filename in uppercase
with underscores; you want it to be unique to the file. Any text
between the two blocks gets ignored if it's already been parsed.
Put those lines in, and treat them as the beginning and end of the
file. Any subsequent code we enter will go in place of the ellipsis
(...).
65
Juce Programming Tutorial by haydxn
Now we have a file ready for our new class. We are going to be
using Juce code here, so we must #include it:
#include <juce.h>
public:
class TextConsole : public TextEditor
{
};
Looking through the TextEditor docs, we can see that there are no
'pure virtual' functions, and there are no required parameters in
the constructor. That means our TextConsole is currently ready to
be used – but only as a TextEditor. If there were pure virtual
functions, we'd need to define them first. Also, if any constructors
required parameters, we'd have to set them in our own
constructor. Even though we don't have to, we'll give it a
parameter anyway – just to show how.
TextConsole ()
{
...
}
66
Juce Programming Tutorial by haydxn
SetMultiLine (true);
setReadOnly (true);
67
Juce Programming Tutorial by haydxn
There are two main functions we need. The most obvious is:
Which will put the text at the current cursor position. Our function
will take a String parameter (text), so we'll be using that.
However, we'll be wanting each bit of text to be a new line, so we
must add a 'newline' character to the end. I've already explained
how to make one of these, using a backslash code. We can simply
say (text + T("\n")).
68
Juce Programming Tutorial by haydxn
insertTextAtCursor (text);
}
Now we have a special TextEditor with 'an extra button' that can
do something specific for us. Let's give it a try!
#include "TextConsole.h"
public:
MainComponent ()
{
console = new TextConsole;
addAndMakeVisible (console);
~MainComponent ()
{
deleteAllChildren ();
}
void resized ()
{
console->setBounds (10,10,
69
Juce Programming Tutorial by haydxn
getWidth()-20,getHeight()-20);
}
};
Compile and run this new application, and you should see your
test lines sitting happily on the editor.
So we now have a working 'text output' console. It's not quite the
same as a real console, or using the debugger. Some of you may
be scowling at the page, because you know something I've not
actually pointed out yet – Juce can do console apps just fine. Yes,
that's right, you can make DOS style applications, but we're not
here to learn that, are we? No, we're not.
I've not just wasted your time though. We've just learned how to
adapt a Component into a new type to suit our needs, that we can
now use anywhere. You can do this kind of 'additional'
customisation with any existing class. All you need to do is derive
from it, then configure and enhance to taste. We have, in truth,
been doing something like this the whole time (with
MainComponent), but doing it this way should make the
Component model a little clearer.
70
Juce Programming Tutorial by haydxn
Here is a reasonably simple task for you. Yes, I've stopped holding
your hand for this one!
Task
Do this now (if you do use copy/paste, make sure you change
MainComponent to TestBed in the new file!). Answer code is in the
71
Juce Programming Tutorial by haydxn
appendix (we of course take out the test lines, and we also want
to change the bounds of the console so that we have space for
buttons).
72
Juce Programming Tutorial by haydxn
Answers to problems
Chapter 3
Chapter 4
Chapter 5
label->setEditable (true);
label->addChangeListener (this);
73
Juce Programming Tutorial by haydxn
Chapter 6
{
private:
TextButton* open;
...
MainComponent ()
{
...
open = new TextButton (T("Open"));
addAndMakeVisible (open);
open->addButtonListener (this);
...
}
74
Juce Programming Tutorial by haydxn
void resized ()
{
open->setBounds (10,10,50,15);
text->setBounds (10,35,getWidth()-20,getHeight()-45);
}
Chapter 7
public:
TestBed ()
{
console = new TextConsole;
addAndMakeVisible (console);
}
~TestBed ()
{
deleteAllChildren ();
}
void resized ()
{
console->setBounds (10,30,
getWidth()-20,getHeight()-40);
}
};
75