Basic "starting point" tutorial code


#1

[size=75]UPDATE: THIS IS VERY OLD SAMPLE CODE.[/size]
[size=50]
It is also redundant, as the Introjucer can auto-generate a shell app to suit this purpose just fine.
Therefore, don’t bother using this! Just create a new app with a main window using the introjucer!

I’m leaving the post below for historical purposes :slight_smile:

[/size]

I’ve still not got any new web hosting yet, so I’ve not got anywhere to upload any files. I still get regular emails asking about my tutorial files, and how to fix them etc… as they’re out of date. Well, in the mean time, I thought I’d copy/paste the code from the updated starting point files to keep newcomers happy. Thus…

[size=150]Starting Point guide[/size]

Here is a guide to creating your own blank project for juce, along with the starting point code to get you up and running. This guide in particular is for Visual C++ Express Edition, although the code (at the end) doesn’t care about that (if that’s all you’re after).

First of all, follow the instructions in the juce readme docs to make sure you’ve got your compiler directories all configured properly.

[size=150]Creating a suitable blank Juce project in VC++e[/size]

Create a new project, choosing ‘Win32 Console application’ and giving it an appropriate name. Click ‘OK’.

In the next window, click ‘Application Settings’ on the left hand side, and then make the following changes:

  • Select ‘Windows application’
  • Enable the ‘Empty project’ option
    Then, click ‘Finish’.

You now have a blank project with no files in it. It’s not quite ready for linking up with Juce yet, so you need to also make some adjustments to the run-time library settings. However, VC++e doesn’t make this setting available until it knows your project is using the C++ language; you need to first add a C++ code file to the project.

Your blank project starts with three ‘filters’ (folders) visible in the ‘solution explorer’ on the left hand side. These help you organise your code files in the project, they don’t correspond to the actual directory structure. Personally, I don’t really like those ones (header, resource and source files), so I delete them and create more appropriate ones. You may wish to use them as they are.

I’m going to give you some ‘starting point’ code here. Each code section below should belong in a separate file, with the files named as indicated.

Before I give you the code though, I’ll just explain how to add a new file to the project…

[size=150]Adding a new file…[/size]

To add a new file to the project, right-click on where you want it to go in the solution explorer (either on a filter or on the project above them), and select “Add->New item”.

Choose ‘Code’ on the left, then the type of file you want (.cpp or .h). Give it a name (and choose a specific location if you like - I make a subdirectory called ‘src’ out of habit) and click ‘Add’. Your new (blank) file will now be ready to edit, and will appear in the solution explorer.

Note that if you left the default filters in, and the item you right-clicked on was the project itself, the file will automatically be placed into the relevant filter (i.e. header or source).

Now you know how to add new files, create four new files with the following names:

ApplicationStartup.cpp
MainAppWindow.cpp
MainAppWindow.h
MainComponent.h

These will be the ‘starting point’ files.

Now that VC++e knows what language you’re using, you can make the required adjustments to the project properties…

[size=150]Configuring the project’s Runtime Library[/size]

Go to the ‘Project’ menu at the top, and click ‘Properties’.
In the left hand side, you need to locate the following item:
Configuration Properties->C/C+±>Code Generation

You need to change the ‘Runtime library’ item to the ‘non-DLL’ version of whatever is selected for both DEBUG and RELEASE configurations.

At the top-left, there is a ‘Configuration’ combo-box, which chooses the configuration you’re adjusting. Make the following changes:

For ‘DEBUG’ config, set Runtime Library to “MultiThreaded Debug”. Click 'Apply’
For ‘RELEASE’ config, set Runtime Library to “MultiThreaded”. Click ‘Apply’ (and then of course ‘Ok’).

Now your project is correctly configured, you can paste the following code blocks into their corresponding files.

[size=150]Starting point code[/size]

ApplicationStartup.cpp

/*
  ==============================================================================

   JUCE library : Starting point code, v1.26
   Copyright 2005 by Julian Storer. [edited by haydxn, 3rd April 2007]

  ------------------------------------------------------------------------------

  ApplicationStartup.cpp :

  This file describes how the application will be brought to life within the
  operating system. The basic order of things is...

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  The [OS] creates the 'AppClass', which is a shell for the program,
  and is responsible for bringing everything to life...
 
 	 ... the [AppClass] creates the MainAppWindow and puts it on the screen...

		... the [MainAppWindow] is a visible base for the program, and it
 		    creates the program's MainComponent upon itself...

			... the [MainComponent] then 'does' the main 'program stuff'

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  If you're only writing simple applications, there's very little that you'd need
  to do in here, as the app shell is only really responsible for creating the window
  and getting everything running.

  You will, however, probably want to set the size of the window to something suitable.
  This is done in the initialise function, after the window has been created.

  ------------------------------------------------------------------------------

  Please feel free to do whatever you like with this code, bearing in mind that
  it's not guaranteed to be bug-free!

  ==============================================================================
*/

#include "MainAppWindow.h"


//==============================================================================
class AppClass : public JUCEApplication
{
    /* Important! NEVER embed objects directly inside your JUCEApplication class! Use
       ONLY pointers to objects, which you should create during the initialise() method 
       (NOT in the constructor!) and delete in the shutdown() method (NOT in the 
       destructor!)

       This is because the application object gets created before Juce has been properly
       initialised, so any embedded objects would also get constructed too soon.
   */
    MainAppWindow* theMainWindow;

public:
    //==============================================================================
    AppClass()
        : theMainWindow (0)
    {
		// This is where the application itself is created. It's a bit like a shell, which
		// all your real app stuff gets born into. At this point (where the app pops into
		// being), Juce doesn't really 'exist' yet, so we can't do anything with it here.
		// Once this application shell has been established, Juce will be awake so we can
		// create instances of its classes willy-nilly.
		
		// [Jules says...]
		// NEVER do anything in here that could involve any Juce function being called
        // - leave all your startup tasks until the initialise() method.
    }

    ~AppClass()
    {
		// This is where the application body is destroyed, and making any Juce calls in 
		// here could be very dangerous. 
		
		// Just as Juce wasn't awake when the shell was created, by this stage it's been 
		// tidied away and put to sleep. This is the bit that happens AFTER your program
		// code has ended (and hopefully put all its toys away).

		// [Jules says...]
        // Your shutdown() method should already have done all the things necessary to 
        // clean up this app object, so you should never need to put anything in 
        // the destructor.
    }

    //==============================================================================
    void initialise (const String& commandLine)
    {
		// This is called automatically when the application is ready to launch.
		// So far, it just exists in memory as an empty pocket of potential waiting
		// to burst into life as a program. Nothing yet exists to act or be displayed.

		// All we want to do here is create the main window. This instantiates an object
		// of 'MainAppWindow' - which we have defined in MainAppWindow(.h/.cpp). The app's
		// behaviour comes from that, so all we need is to bring it to life...
		theMainWindow = new MainAppWindow();
		// ... and plonk it onto the display...
		theMainWindow->centreWithSize (300, 300);	// [*] (see below for a tip on this)
		// ... (of course making sure that it is visible!)
        theMainWindow->setVisible (true);

		// That's all we have to do here. Once this function has ended, the Juce
		// application will start firing its event loop. This is basically the
		// engine that powers the Juce app classes (giving life to the Component 
		// and messaging model), and is something that will just happen by itself.

		// The event dispatch loop will keep the app alive until something calls
		// JUCEApplication::quit() - which could be windows closing the application,
		// or the user clicking the window's close button.

		// [*] When you set the size of the window, there is something important to bear
		//     in mind: The dimensions you set here are for the window component itself.
		//     If this is a DocumentWindow (which is the most obvious thing for it to be),
		//     then it will have a titlebar and a border. The content component sits inside
		//     this arrangement of parts - which means that your program's main component
		//     dimensions will actually be slightly smaller than the values you set here.
		//     This only matters if you're relying on positioning your program's various
		//     widgets using constant values instead of calculating relative positions from
		//     the available dimensions (i.e. saying 'this button is 50 pixels wide' instead
		//     of 'this button is 1/3 of the width of its parent component').
    }

    void shutdown()
    {
		// This gets called when the application is ready to shut down.
		// Anything that we created in the initialise() function should be destroyed,
		// so that nothing is left hanging around when the app shell ceases to exist.

		// All we need to do here is delete the MainAppWindow we created...
        deleteAndZero (theMainWindow);
    }

    //==============================================================================
    const String getApplicationName()
    {
		// The name for the application
		return T("Juce tutorial application");
    }

    const String getApplicationVersion()
    {
		// Here we can give a 'version' indicator, to distinguish one build from
		// another. If you update your program, it's a good idea to also update
		// the string returned here.
		return T("0.0");
    }

    bool moreThanOneInstanceAllowed()
    {
		// We can prevent multiple instances of the application here by returning false.
		return true;
    }

    void anotherInstanceStarted (const String& commandLine)
    {
        // This will get called if the user launches another copy of the application.
    }
};


//==============================================================================
// This macro creates the application's main() function..
START_JUCE_APPLICATION(AppClass)

MainAppWindow.cpp

/*
  ==============================================================================

   JUCE library : Starting point code, v1.26
   Copyright 2005 by Julian Storer. [edited by haydxn, 3rd April 2007]

  ------------------------------------------------------------------------------

  MainAppWindow.cpp :

  This file defines the configuration of the main application window, which
  is the class MainAppWindow.

  The JUCE class 'DocumentWindow' is the base for this class, so examine the
  documentation for any functions that you may wish to use, or any other
  configuration options you may wish to change.

  The 'window' is the main bit that the program sits within. That means 'the
  bit with the title bar and the overall size/shape of the program, but without
  the actual program on it yet'. Your program lives in the 'MainComponent' class,
  and is added to this window as the 'content component'.

  ------------------------------------------------------------------------------

  Please feel free to do whatever you like with this code, bearing in mind that
  it's not guaranteed to be bug-free!

  ==============================================================================
*/

#include "MainAppWindow.h"
#include "MainComponent.h"

//==============================================================================
MainAppWindow::MainAppWindow()
	:	
		// Initialise the base 'DocumentWindow'...
		DocumentWindow (
			T("Tutorial app"),			// Set the text to use for the title
			Colours::azure,				// Set the colour of the window
			DocumentWindow::allButtons,	// Set which buttons are displayed
			true						// This window should be added to the desktop
		)
{
    setResizable (true, false); // resizability is a property of ResizableWindow, which is
								// a parent class of DocumentWindow (which is our base class), 
								// so we have access to this setting here.

	setTitleBarHeight (20);		// Set the height of the titlebar on our window. 

	// create the main component, which is described in MainComponent.h
    MainComponent* contentComponent = new MainComponent ();

    // This sets the main content component for the window to be whatever MainComponent
    // is. The nature of DocumentWindow means that the contentComponent will fill the main
	// area of the window, and will be deleted automatically when the window is deleted.
    setContentComponent (contentComponent);
}

MainAppWindow::~MainAppWindow()
{
    // Our 'content component' will get deleted by the destructor in the DialogWindow 
	// base class, and that will, in turn (assuming the MainComponent has been coded 
	// properly), clean up the other components contained inside it. Therefore, we have
	// nothing much to do here!
}

void MainAppWindow::closeButtonPressed()
{
	// This is a virtual function provided by the DocumentWindow class, allowing us
	// to define the action taken when the window's 'close' button is pressed.

    // The correct thing to do when you want the app to quit is to call the
    // JUCEApplication::systemRequestedQuit() method.
    
    // That means that requests to quit that come from your own UI, or from other 
    // OS-specific sources (e.g. the dock menu on the mac) all get handled in the 
    // same way.

	// So, here, we get the application instance (from the static getInstance()
	// function in JUCEApplication), and call the function we need from it...
    JUCEApplication::getInstance()->systemRequestedQuit();
}

MainAppWindow.h

/*
  ==============================================================================

   JUCE library : Starting point code, v1.26
   Copyright 2005 by Julian Storer. [edited by haydxn, 3rd April 2007]

  ------------------------------------------------------------------------------

  MainAppWindow.h :

  This file is just a declaration of the MainAppWindow class. For more
  detailed information of its purpose, examine the MainAppWindow.cpp
  file.

  Having it as a separate header file may seem pointless, but it could
  save a bit of effort should you be embarking on a complex project and
  wish to still use this fileset as a starting point.

  ------------------------------------------------------------------------------

  Please feel free to do whatever you like with this code, bearing in mind that
  it's not guaranteed to be bug-free!

  ==============================================================================
*/

#ifndef _MainAppWindow_H__
#define _MainAppWindow_H__

#include "juce.h"

//==============================================================================
class MainAppWindow  : public DocumentWindow
{
public:
    //==============================================================================
    MainAppWindow();
    ~MainAppWindow();

    //==============================================================================
    // called when the close button is pressed or esc is pushed
    void closeButtonPressed();

	// It is most likely that your program will be happy thinking of the window's
	// content component as the 'base' level of the application; it can be responsible
	// for storing and maintaining anything considered crucial to the running of the 
	// program. 

	// However, if you want to have anything live above even that, you may
	// want to put it here. You may even wish to step further outside of things and keep
	// some higher management system within the JUCEApplication class that drives the
	// whole shebang, but that's probably not necessary, and can be tricky to maintain!
};


#endif

MainComponent.h

/*
  ==============================================================================

   JUCE library : Starting point code, v1.26
   Copyright 2005 by Julian Storer. [edited by haydxn, 3rd April 2007]

  ------------------------------------------------------------------------------

  MainComponent.h :

  This file defines the behaviour of the application. The main part of the 
  program that the user interacts with IS this MainComponent object. It is
  placed within the MainAppWindow instance, and so exists at whatever size
  the window provides in its content area.

  All of the widgets and controls that your main program window will display
  will be on this component (either directly or somewhere down the children
  hierarchy).

  ------------------------------------------------------------------------------

  Please feel free to do whatever you like with this code, bearing in mind that
  it's not guaranteed to be bug-free!

  ==============================================================================
*/
#ifndef _MAINCOMPONENT_H_
#define _MAINCOMPONENT_H_

#include "juce.h"

class MainComponent  : public Component		// Here we specify any base classes that
											// give this type predefined characteristics.
											// Naturally, this is a Component, but we could
											// also inherit other qualities. For example, if
											// we want to respond to button presses, we can
											// inherit 'ButtonListener', by changing it thus:
					// e.g.
					// public Component,
					// public ButtonListener
									
					// (Notice that they're separated by a comma, and nothing comes after the
					//  final one in the list - i.e. no semicolon, because the next character
					//  must be the '{' denoting the start of the class body).

					// We can inherit many different classes from Juce (or classes we make
					// ourselves), but be aware that some base classes require you to define
					// some function bodies before it will allow your app to compile. These
					// functions are called 'pure virtual' functions - an example would be
					// 'buttonClicked' in ButtonListener. Some base classes will provide many
					// other virtual functions - not just pure virtual ones - which you can
					// define if you choose, but you do not have to do so.
{
private:
    //==============================================================================

	// Here are some members that are useful to have in any application...

    TooltipWindow tooltipWindow;	// To add tooltips to an application, you
									// just need to create one of these and leave it
									// there to do its work.

	// Your app will obviously have some kind of member variables, and so this
	// is the place you can declare them. For example, if you wanted to have a
	// widget or some component you've made, you'd have a pointer for it here;
	// you'd then instantiate the object in the constructor, and use the pointer
	// to access it elsewhere (e.g. to position it, update it, or respond to it).

	// e.g.
	// TextButton* myButton;

public:
    //==============================================================================
    MainComponent ()
    {
		// This is where the main component is created, so we initialise and
		// configure it according to our needs.

		// One thing that covers is creating any widgets and components we want to
		// display. Also, if any widgets will need responding to, we must hook them up 
		// to their listeners here too (and it's likely that this class itself will be
		// the listener in question, providing we've inherited the appropriate class!)

		// Create and add the rest of your components here!
		// e.g.
		//myButton = new TextButton (T("my button"), T("Click me!"));
		//addAndMakeVisible (myButton);
    }

    ~MainComponent ()
    {
		// Be sure to destroy any objects you've created using 'new' here. If your objects
		// are on the stack (i.e. they were created without pointers or the 'new' operator,
		// then they die automatically. If you've created them
		// manually on the heap (for example, if you've got a pointer and you've created a new
		// object for it) then it must be deleted.

		// However, juce has a nice neat function that will destroy all Components that have
		// been added to a Component...

		deleteAllChildren();	// This will remove all the contained components, and delete
								// the objects for you.
    }

    //==============================================================================
    void resized ()
    {
		// This is called whenever this component's size changes. We could respond
		// to this in a number of ways, but the most obvious thing to do is reposition
		// all our widgets, using their 'setBounds()' function.
		// It's nice to position them relative to the size of this Component. That means
		// making use of the 'getWidth()' and 'getHeight()' functions to determine where
		// to put them and how big they should be.
    }

	void paint (Graphics& g)
	{
		// This does any drawing required on this Component's face. You can simply
		// call functions on the provided Graphics object - although you may want to
		// make use of 'getWidth()' and 'getHeight()' (members of this Component) to
		// make sure you're drawing in the right place!
	}


    //==============================================================================
};

#endif//_MAINCOMPONENT_H_

You’ll notice that the code is chock-full of comments; this is to help explain what each part is for, as well as provide an overview of the basic structure of a Juce application.

Once you’ve got all this code pasted into your files (and saved them) you should be able to compile the project.

If all goes smoothly, you should have a nice blank boring window come up. If so, congratulations! You can copy this project’s folder somewhere as a template if you like. Rather than set up a new project each time, you can just make a copy of your template project folder, open the project from the copy and rename it. If you do that, you might want to strip the detailed comments out first in your template.

And that’s about it! I hope this is useful to newcomers.


#2

What about adding this to the Jucer (as an “Export basic Application” option on file menu) ?


#3

That’s an excellent idea…

I’m going to add that feature right now!


#4

Heh, just did it and it’s quite a nice feature, although I’m not sure how genuinely useful it is :slight_smile: it’d be better still if it could create a whole VC project too, but that’s not the sort of thing that should be put into a cross platform tool.

I emailed jules the SVN patch, maybe he’ll do something with it, or maybe it is just quite pointless! :slight_smile: nevertheless, it was fun to do.


#5

The jucer should generate “premake” project files, like Juce does.
So, if you have “premake” installed (it’s cross platform), you can generate the project for whatever IDE & OS you’re using.

If you need to update the whole Juce tree with tons of new files, you can do so, easily, with premake, so it shouldn’t be that hard.

Anyway, I’m using vsrename to rename quick and dirty VS projects when I “enhance” (bastardly copy) another, older, one.

We use premake ( http://premake.sf.net ) at work through, and it works flawlessly.


#6

We use cmake (http://www.cmake.org) and it too works very nicely. It can be used to generate VS project files, XCode project files or Linux makefiles, so truly x-platform… plus the cmakelists.txt file is next to trivial to generate and maintain…


#7

This is great, Thanks


#8

I have a question, and it may seem silly… Why are these tutorials not hosted on this site? It seems a little convoluted to have all the JUCE stuff here, but tuts etc elsewhere…


#9

In this tutorial, following steps were forgotten:

Prerequisites: Juce must have been compiled already in Debug and Release modes for generating the Debug and Release static libs located in the Juce\bin folder.

  • Add Juce base path e.g. c:\JUCE (both in Release and in Debug config) to Project->Properties->Configuration Properties->C/C+±>General->Additional Include Directory

  • Add Juce path containing lib files e.g. c:\JUCE\BIN (both in Release and in Debug config) to Project->Properties->Configuration Properties->Linker->General->Additional Library Directories

  • Enable Run-Time Type Information (both in Release and in Debug): Project->Properties->C/C+±>Language->Enable Run-Time Type Info->Yes

After following those steps, everthing should work.


#10

Thanks for pointing that out, although it does say to first follow the readme docs! (which contain that information) I’m sure many people will miss it or not bother!


#11

Hi,

I am trying to change the “Hello World-Demo” starting point to my own Component, but it doesn´t work.

Is this, because “Hello World” Demo is written using the file “juce_amalgamated.cpp”, but my component was developed within haydxn-startup file?

thx


#12

[quote=“pooz”]Hi,

I am trying to change the “Hello World-Demo” starting point to my own Component, but it doesn´t work.

Is this, because “Hello World” Demo is written using the file “juce_amalgamated.cpp”, but my component was developed within haydxn-startup file?

thx[/quote]

no, you sound extremely muddled there. The place that an app starts from has nothing to do with the juce library files - somewhere (anywhere) in your code, you just use the START_JUCE_APPLICATION macro to tell it where to begin. Have a look at the docs for the JUCEApplication class, or any of the demo apps for more info.


#13

Thank you, jules!
Now I got it working. I read all the comments in the startup files and got it now.

Still 2 little Qs:

  1. Would you recommend to use “juce_amalgamated.cpp” or better use “juce.h” and so forth? (I am asking because of performance and/or handling issues)
  2. If using “juce_amalgamated.cpp” file, do I still need all the other files and folders from my “Juce”-Directory, or can I delete them?

Thank you!
greetz


#14

Well, it’s easier to get your project linking if you use the amalgamated stuff, but it shouldn’t make any difference to performance.

Yes, you could delete the other files in juce, but surely you can spare a couple of megabytes of disk space!


#15

Ok, thanks.

But the folder “extras” should be kept, if one want to use “Quicktime” , “ASIO” …etc. and such things?

How does it come, that the Console-Window won´t appear anymore, if using amalgamated stuff? Or is this a settings-option of my project (curiously I can´t find any hint in VC++Express) ?


#16

Yes, there’s a project setting in there somewhere that lets you change whether it’s a command-line app.


#17

Since this is a ‘sticky’, would it be possible to update the info in the first post to apply to the current location of the tutorial?

This is a fantastic resource, and even with the code changes I was able to complete it easily in an evening. What fun!! The out of date stuff is easy to work around if you are familiar with OOP and have code-completion in your IDE–and there are also tips here, if needed.

The only problem I’ve had–I can’t get my Console subclass to compile. Does someone have working code to that? The description gets a bit fragmented, and I’m still trying to get my head around the header syntax…

Thanks for Creating this, Hadyxn!

-e


#18

I’ll have a look at updating it this week if i get time. Work and the holiday period have kept me busy for a fair while, but i’m eager to get it back up to speed.

I’m glad it’s still useful even in its current state of archeological significance!


#19

That would be great. You really do have a gift for explaining things–that was one of the most readable tutorials I’ve ever done…

Of course, it doesn’t hurt that Jules’ code is extremely well thought-out and the comments are helpful and well-written.

cheers!
-eric


#20

Thanks very much for this haydxn. If anyone feels like providing an version of this setup guide focused on Xcode, that would be excellent. Otherwise I’ll post one up if/when i’ve puzzled it through.