Sourceforge.net - The VCF's Project Host
   The VCF Website Home   |   Online Discussion Forums   |   Sourceforge.net Project Page   

5.3. ApplicationKit

5.3.1. Hello World

5.3.1.1. A Hello World application

This article will explain the very basics of creating a Hello World application with the Visual Component Framework. This simple application will demonstrate creating a VCF::Application instance and displaying a main window, with "Hello World" in it's caption bar.

The simplest full VCF application, in other words, one that uses the Application class as it's starting point, as opposed to just using certain pieces of it, needs only a few lines of code in a standard C main function, like the following:


int main(int argc, char *argv[])
{
	Application app( argc, argv );

	Application::main();
}

Those two lines of code are all that is need to create your app instance on the stack, and then start the app running, by calling the static Application::main() function. If you are using the VCF Application class features, like we are here, then there can only be one instance of a Application derived class for the running process, much like the restrictions that apply to MFC's CWinApp. Once you have this instance it is neccessary to start the app up by calling Application::main() function, passing in the number of arguments and the argument array of strings. This is processed internally and calls the applications initRunningApplication() and then the run loop, which starts the message pump up. From there on in you are processing windows messages.

Well this by itself is not that interesting, so lets add a window.


int main(int argc, char *argv[])
{
	Application app( argc, argv );
	
	Window* mainWindow = new Window();

	Application::main();
}

This creates our new window on the heap. This is the preferred way of creating most objects in the VCF, with a few exceptions. Next we need to set the application's main window. In doing this the application is able to register itself as a listener to the windows window events (via a call to addWindowListener()), and will be notified when the window closes itself. At this point the application will in turn terminate it self cleanly and shut down. Failure to do this will prevent the Application of being notified to close correctly, and it will not be able to free up the heap based memory for the Window object.


int main(int argc, char *argv[])
{
	Application app;
	
	Window* mainWindow = new Window();
	
	app.setMainWindow( mainWindow );

	Application::appMain( argc, argv );
}

Well this is still pretty boring so lets set the windows caption and then display it.



int main(int argc, char *argv[])
{
	Application app;
	
	Window* mainWindow = new Window();
	
	app.setMainWindow( mainWindow );

	mainWindow->setCaption( "Hello World" );

	mainWindow->show();

	Application::appMain( argc, argv );
}

And there you have it: a window will magically display itself, with a caption that reads "Hello World". The caption is set via the setCaption() method, and the windows is made visible via the show() method. Alternatively, we could have made the window visible by calling the setVisible() with a true bool value.

Most of this was pretty easy, with the only odd thing the missing WinMain(). This is circumvented by setting some custom linker settings: in the output section of the project setting's Link tab, you specify mainCRTStartup as the Entry-point symbol, and make sure your /subsystem flag is set to /subsystem:windows not /subsystem:console. With this set you can go about your merry way and still use main() just like normal.

5.3.1.2. Hello World Part 2

This example builds on our work in the first example, HelloWorld. This will demonstrate customizing the VCF::Application class and positioning the window where we want it.

The first step is to create an new application class, and derive it from VCF::Application. We'll call this class HelloWorld2Application. We'll override a single method from VCF::Application, initRunningApplication(). The initRunningApplication() method is called just after the application kit has initialized itself and the rest of the VCF. You custom startup code can safely call any VCF method, or create any neccessary VCF object that you may need. Please note that if you have succesfully initialized your application then you should return true from initRunningApplication(), otherwise return false, ot have the ApplicationKit gracefully shut the application and itself down. As in other methods you override you should make sure to call the super classes initRunningApplication(), failure to do this will result in an undefined state for the Application.

In our custom initRunningApplication() method we'll create a new instance of a VCF::Window.


class HelloWorld2Application : public Application {
public:
	//remainder of code ommitted for clarity

	virtual bool initRunningApplication(){ 
		//call the super classes initRunningApplication()
		bool result = Application::initRunningApplication();
		
		
		//create the window as before
		Window* mainWindow = new Window();
	}


Next we'll set application's main window. This is accomplished by calling the VCF::APplication's setMainWindow() method, passing in our new window instance.


	//remainder of code ommitted for clarity

	virtual bool initRunningApplication(){ 
		//remainder of code ommitted for clarity
		
		setMainWindow( mainWindow );
	}


	

Next we'll prepare to position the window. This is done by passing in a pointer to a VCF::Rect instance to the VCF::Window::setBounds() method. We can create the Rect on the stack and then just pass in the pointer to it. Alternately, we could have created a temporary Rect instance on the stack and passed the temporary in as well.


	//remainder of code ommitted for clarity

	virtual bool initRunningApplication(){ 
		//remainder of code ommitted for clarity
		
		Rect mainWindowBounds( 100.0, 100.0, 500.0, 500.0 );
		mainWindow->setBounds( &mainWindowBounds );
	}


  

The rectangular bounds is defined as 4 coordinates, an X and Y coordinate for the top left corner, and another X and Y coordinate for the bottom right corner of the Window. These coordinates are always in the coordinate space of the parent control, and since a Window doesn't have a parent, the coordinates are in the screen coordinate space.

Finally, we'll actually show the window - remember to do this step or your window will not be visible (there are alternate ways of handling this, but we are not going to cover them here).


	//remainder of code ommitted for clarity

	virtual bool initRunningApplication(){ 
		//remainder of code ommitted for clarity
		mainWindow->show();
	}


	

So we can see that creating a new window is quite easy, and we have also learned to customize the start up behaviour of our application.

5.3.1.3. HelloWorld3

The purpose of this example is to demonstrate creating not only our own custom application class, but our first case of deriving a new window class. As before, we'll build on what we learned in the HelloWorld and HelloWorld2 examples.

We'll create another class in this example, this time deriving from VCF::Window. The only methods we'll implement are the constructor, and we'll put the code that was previously in the application's initRunningApplication() method, and move it to our window's constructor. So let's create a window class:


class HelloWorld3Window : public VCF::Window {
public:
	HelloWorld3Window() {
	}
};

Next we'll start setting various properties of the window. First let's change the window's title, or caption. To do this we just have to call the VCF::Window::setCaption() method and pass in a string.


class HelloWorld3Window : public VCF::Window {
public:
	HelloWorld3Window() {
	  //get the current running application
	  Application* runningApp = Application::getRunningInstance();
	  
	  //get the app's name
	  String appname = runningApp->getName();
	  
	  //set the caption!
	  setCaption( appname + " - Hello World!" );
	}
};

	

Now we'll set the bounds, just like we did in the HelloWorld2 example.


class HelloWorld3Window : public VCF::Window {
public:
	HelloWorld3Window() {
	  //code omitted for clarity
	  Rect bounds( 100.0, 100.0, 500.0, 500.0 );
	  setBounds( &bounds );
	}
};

Finally we'll show the window. Note that this time we are going to use the setVisible() method of the Window class, as opposed to the show() method - for our purposes they perform the same functionality.


class HelloWorld3Window : public VCF::Window {
public:
	HelloWorld3Window() {
	  //code omitted for clarity
	  setVisible( true );
	}
};

	

5.3.2. Heavyweight vs. Lightweight Controls

5.3.2.1. HeavyAndLight

Your content here, explaining the usage/functioning of your example.

5.3.3. Alignment, Anchors, and Layouts

5.3.3.1. Alignment

Your content here, explaining the usage/functioning of your example.

5.3.3.2. AdvancedAlignment

This tutorial will explain and demonstrate how to easily customize and create container classes that implement your own layout algorithms. We will create two container classes that do this, one which will lay out it's child controls from left to right, taking up all the vertical space, and evenly dividing the horizontal space. The second container will demonstrate how to simulate a paged layout container, conceptually similar to Java's CardLayout class.

When creating a container, we generally only need to implement a single method: the Container::resizeChildren(). The rest of the methods of the Container interface are implemented in the AbstractContainer class, which we can derive from to save ourselves some time. When implementing resizeChildren() we may be passed in a Control pointer. If the control has just been added to the container, it will be passed in to resizeChildren(), but will not be in the Container's child control list. We need to test for this and act accordingly. Testing can be done like so:


virtual void resizeChildren( Control* control ) {
//rest of code omitted
bool controlJustAdded = false;
std::vector<Control*>::iterator found = std::find( controls_.begin(), controls_.end(), control );
controlJustAdded = (found == controls_.end());

}

	

The variable controlJustAdded will now indicate whether or not the passed in control has just been added to the container.

For our first container we'll simply layout all the child controls from left to right, dividing the width of the container control equally among them. So, to get started lets look at the class declaration:


class LeftToRightContainer : public AbstractContainer {
public:
  virtual void resizeChildren( Control* control );
};

	

That's it! That's all we need to declare to implement a container. We could have chosen to derive from StandardContainer, but we'll save that for our next container. Next we just need to implement resizeChildren(). It will look like this:



virtual void resizeChildren( Control* control ) {
	Rect clientBounds = controlContainer_->getClientBounds();
	
	if ( clientBounds.isEmpty() ) {
		return; //nothing to do, so just exit the function
	}
	
	bool controlJustAdded = false;
	if ( NULL != control ) {
		std::vector<Control*>::iterator found = std::find( controls_.begin(), controls_.end(), control );		
		controlJustAdded = (found == controls_.end());
	}
	
	double childCount = AbstractContainer::getChildCount();
	if ( controlJustAdded ) {
		childCount = childCount + 1.0;
	}

	double width = clientBounds.getWidth() / childCount ;

	Enumerator<Control*>* children = AbstractContainer::getChildren();
	Rect childBounds;
	double left = clientBounds.left_;
	while ( children->hasMoreElements() ) {
		Control* child = children->nextElement();
		
		childBounds.setRect( left, clientBounds.top_, left + width, clientBounds.bottom_ );
		child->setBounds( &childBounds );
		
		left += width;
	}

	if ( controlJustAdded ) {
		childBounds.setRect( left, clientBounds.top_, left + width, clientBounds.bottom_ );
		control->setBounds( &childBounds );
	}
}


	

The first thing we do is get the client bounds, by calling the controlContainer_->getClientBounds(). The controlContainer_ is a member variable that points to the containers current Control that it is attached to. The Rect that is returned takes into account any border that may be part of the control. If the rect is empty, we exit method immediately, as there is no need for any forther processing.

Next we determine if the control passed in is being added to the container for the first time. As we discussed before, we search through the controls_ vector using std::find. Next we determine the width that each control will be assigned. This is calculated by dividing the number of controls currently in the container by the width of client bounds (the clientBounds Rect variable). We store the count of controls ins childCount, and if the control that was passed in has just been added we increment the childCount by one. The childCount variable is incremented because the newlky added control has not yet been added to the controls_ vector (the framework will take care of this later).

Finally, we iterate through the list of child controls by calling getChildren() to get the enumerator, and then adjusting each child control accordingly.

5.3.3.3. Anchors

Your content here, explaining the usage/functioning of your example.

5.3.4. MVC Basics

5.3.4.1. MVC Basics

This example will discuss the basics of using and creating a custom model, view, and control classes that make use of the VCF's MVC architecture. Our example will create a simple model class that contains a series of cirlce shapes. We'll demonstrate 5 different ways to work with and put it together with the VCF's control's, custom views, and a custom controller.

Defining the CircleModel.  The first thing we'll start with is the model. Our model will be a collection for circles, and we'll define it's interface like so:


class CircleModel  {
public:

	void addCircle( const CircleShape& circle ) {
		circles_.push_back( circle );
	}

	void addCircle( const Point& pt, const double& radius ) {
		addCircle( CircleShape(pt,radius) );
	}
	
	void removeCircle( const CircleShape& circle ) {
		std::vector<CircleShape>::iterator found = std::find( circles_.begin(), circles_.end(), circle );
		if ( found != circles_.end() ) {
			circles_.erase( found );
		}
	}

	CircleShape& getCircle( int index ) {
		return circles_.at(index);
	}

	const CircleShape& getCircle( int index ) const {
		return circles_.at(index);
	}

	std::vector<CircleShape>& getCircles() {
		return circles_;
	}

	const std::vector<CircleShape>& getCircles() const {
		return circles_;
	}

	int size() const {
		return circles_.size();
	}
protected:
	std::vector<CircleShape> circles_;
};

	
	

Our model has a collection (an std::vector to be precise) of CircleShapes. The CircleShape is defined as


class CircleShape {
public:

	CircleShape( const Point& pt, const double& radius ) :  center_(pt), radius_(radius) {}

	CircleShape():radius_(0.0){}
	
	bool operator==( const CircleShape& rhs ) const{
		return 	center_ == rhs.center_ && radius_ == rhs.radius_;
	}

	Point center_;
	double radius_;
};

	

At this point we have specified our custom pieces for the model and how we can alter (add or remove) shapes from the model.

Next we'll actually turn the class into a VCF compatible model. For this we need to derive directly, or indirectly, from the VCF::Model class. In our case we'll derive from the VCF::AbstractModel, which provides some basic implementation of the various virtual methods of a Model.


class CircleModel : public AbstractModel  {
... //rest of implemention omitted
};
	
	

Next we need to add support for emptying the model by implementing the Model::empty() method. We'll be sure to call the super class's implementation as well, so that the ModelChanged event is fired.


class CircleModel : public AbstractModel  {
... //rest of implemention omitted

	virtual void empty() {
		//remove all the circles!
		circles_.clear();

		//make sure to call the super class's implementation
		AbstractModel::empty();
	}
};
		
	

At this point we have a our custom model almost ready to go. The one missing feature is the ability to notify others when our circle model changes, i.e. when a circle shape is added or removed. To implement this we need to make some subtle changes to our implementation of CircleModel::addCircle() and CircleModel::removeCircle().


class CircleModel : public AbstractModel  {
... //rest of implemention omitted

	enum {
		CircleAdded = Model::MODEL_LAST_EVENT + 100,
		CircleRemoved
	};
	
	
	void addCircle( const CircleShape& circle ) {
		circles_.push_back( circle );

		ModelEvent e( this, CircleModel::CircleAdded );
		ModelChanged.fireEvent( &e );
		updateAllViews();
	}
	
	void removeCircle( const CircleShape& circle ) {
		std::vector<CircleShape>::iterator found = std::find( circles_.begin(), circles_.end(), circle );
		if ( found != circles_.end() ) {

			circles_.erase( found );
			
			ModelEvent e( this, CircleModel::CircleRemoved );
			ModelChanged.fireEvent( &e );
			updateAllViews();
		}
	}
	
};
	
	

We have added a simple enumeration: a value for adding circles, anda value for removing circles. This value becomes the model changed event's type when the change occurs. This allows an event handle to distinguish what type of event occured.

The extra code that was added in both the addCircle() and removeCircle() method simply creates and event and initializes it with reasonable values, and then fires the event on the ModelChanged delegate. This will notify any registered event handlers. Then the Model::updateAllViews() method is called, which also notifies all the views that are attached to this model. We can simplify this code a bit by changing it like so:


class CircleModel : public AbstractModel  {
... //rest of implemention omitted
	
	void addCircle( const CircleShape& circle ) {
		circles_.push_back( circle );

		ModelEvent e( this, CircleModel::CircleAdded );
		changed( &e );
	}
	
	void removeCircle( const CircleShape& circle ) {
		std::vector<CircleShape>::iterator found = std::find( circles_.begin(), circles_.end(), circle );
		if ( found != circles_.end() ) {

			circles_.erase( found );
			
			ModelEvent e( this, CircleModel::CircleRemoved );
			changed( &e );
		}
	}
};
		
	

Calling the Model::changed() method makes it a bit easier, and prevents the implementer from forgetting to either fire the event on the delegate, or notifying all the models views. At this point we know have a usable model class.

Example 1.  This will demonstrate probably the simplest usage of our model. We'll create a window, a model, and hook the two together.

First we'll define a custom Window class:


class MVCBasicsPart1Window : public Window {
public:
	MVCBasicsPart1Window() {}
};
	
	

This gives us our basic window.

[Note]Note
It's important to remember that a VCF::Window class is derived (indirectly) from VCF::Control. This means that a Window is also a specialized form of a view and we'll take advantage of this later on.

Next we'll fill in the constructor implementation. We will create an instance of our CircleModel class, add three circle shapes, and add our window to the model.


class MVCBasicsPart1Window : public Window {
public:
	MVCBasicsPart1Window() {
		setCaption( "MVCBasics Part 1" );
		setVisible( true );

		CircleModel* model = new CircleModel();

		model->addCircle( Point(100,200), 100 );
		model->addCircle( Point(234,550), 300 );
		model->addCircle( Point(300,400), 80 );
		
		model->addView( this );
	}
};
	
	

Note the call to the model's addView() passing in the window instance. This will add the window as a view to the model. This in turn calls the view's setViewModel() method.

At this point we have our window, we can create and also create a model for it. But we still need to be able to present the view, or draw it. For this, we'll override the window's paint() method, and draw the model's content's as a series of circles.


class MVCBasicsPart1Window : public Window {
public:
	...//rest of code ommitted
	virtual void paint( GraphicsContext* ctx ) {
		Window::paint( ctx );

		CircleModel* model = (CircleModel*)getViewModel();

		const std::vector<CircleShape>& circles = model->getCircles();
		for ( std::vector<CircleShape>::const_iterator it = circles.begin(); it!=circles.end(); it++ ) {
			const CircleShape& circle = *it;
			ctx->circle( circle.center_, circle.radius_ );
		}
		ctx->setColor( Color::getColor("black") );
		ctx->strokePath();
	}
};
	
	

This calls the super class's paint() method first, then retrieve the model. Recall that in the constructor, we added the window to the model, which in turns sets the model on the view. We then iterate through each circle shape, and draw a circle on the GraphicsContext.

Example 2.  This next example will create a custom window, an instance of our CircleModel, and a custom view instance, taking the functionality that was in our custom window's paint() method, and putting it in our custom view.

First we'll define our custom view class. We'll derive from AbstractView, which provides a default implementation for most of the virtual methods of the View interface.


class MVCBasicsPart2View : public AbstractView {
public:
	MVCBasicsPart2View(){}

	virtual void paintView( GraphicsContext* ctx ) {


		Control* control = AbstractView::getViewControl();

		Rect r = control->getClientBounds();

		ctx->rectangle( &r );
		ctx->setColor( Color::getColor("white") );
		ctx->fillPath();
		

		CircleModel* model = (CircleModel*)getViewModel();

		const std::vector<CircleShape>& circles = model->getCircles();
		for ( std::vector<CircleShape>::const_iterator it = circles.begin(); it!=circles.end(); it++ ) {
			const CircleShape& circle = *it;

			ctx->circle( circle.center_, circle.radius_ );

		}
		ctx->setColor( Color::getColor("black") );
		ctx->strokePath();
	}
};
	
	

This is basically the same functionality that we had in our previous window's paint() method. We only need to implement one method (all the others are already implemented by the AbstractView super class), paintView() which takes a GraphicsContext pointer, just like the Window::paint() method does.

Next we'll go ahead an implement our custom window class. This time all we need to do is implement the constructor, where we'll create our model (just like we did last time) and also create our new view class instance.


class MVCBasicsPart2Window : public Window {
public:
	MVCBasicsPart2Window() {
		setCaption( "MVCBasics Part 2" );
		setVisible( true );

		CircleModel* model = new CircleModel();

		model->addCircle( Point(100,200), 100 );
		model->addCircle( Point(234,550), 300 );
		model->addCircle( Point(300,400), 80 );

		View* view = new MVCBasicsPart2View();

		this->setView( view );
		model->addView( view );
	}
};
	
	

We create our model and add circle shapes just like we did the last time. Next we create our MVCBasicsPart2View view instance. Now we set the window's view with a call to setView(), passing in our new view instance. Finally we add our new view instance to the model. Note that we did not add the window instance as a view to the model. Also note that we did not have to override the Window's paint() method, because we set a view for the window, the window (or any control instance) will delegate the painting to be handled by the view.

Example 3.  Our 3rd example will duplicate the functionality found in the previous example and also add the processing of user input. We will add event handlers for processesing key up events, and mouse clicked events.


class MVCBasicsPart3Window : public Window {
public:
	MVCBasicsPart3Window() {
		setCaption( "MVCBasics Part 3" );
		setVisible( true );

		CircleModel* model = new CircleModel();

		model->addCircle( Point(100,200), 100 );
		model->addCircle( Point(234,550), 300 );
		model->addCircle( Point(300,400), 80 );

		View* view = new MVCBasicsPart2View();

		this->setView( view );
		model->addView( view );

		MouseClicked += 
			new MouseEventHandler<MVCBasicsPart3Window>( this, &MVCBasicsPart3Window::onMouseClicked, "MVCBasicsPart3Window::onMouseClicked" );

		KeyUp += 
			new KeyboardEventHandler<MVCBasicsPart3Window>( this, &MVCBasicsPart3Window::onKeyUp, "MVCBasicsPart3Window::onKeyUp" );
	}


	void onMouseClicked( MouseEvent* e ){}

	void onKeyUp( KeyboardEvent* e ) {}
};
	
	

The change, obviously, is the addition of event handlers. We add an event handler (MVCBasicsPart3Window::onMouseClicked) to the window's MouseClicked delegate and another handler (MVCBasicsPart3Window::onKeyUp) to the window's KeyUp delegate. Now let's see what we do for our implementation of MVCBasicsPart3Window::onKeyUp and MVCBasicsPart3Window::onMouseClicked.


class MVCBasicsPart3Window : public Window {
public:
	...//rest of code ommitted

	void onMouseClicked( MouseEvent* e ){
		CircleModel* model = (CircleModel*)getView()->getViewModel();

		model->addCircle( *e->getPoint(), 50 );
	}

	void onKeyUp( KeyboardEvent* e ) {
		if ( e->getVirtualCode() == vkDelete ) {
			CircleModel* model = (CircleModel*)getView()->getViewModel();
			
			model->empty();
		}
	}
};
	
	

When we click on the window we'll add a circle to the circle model. That's all we ahve to do - the update to the window and the repainting will all happen automatically. When we get notified by the KeyUp delegate, if the delete key was pressed, then we will empty the model, clearing the display.

Example 4.  This example will do things a little differently. We won't derive a new Window class. Instead we'll create our own controller class which will handle the mouse click and key up events. We will also add another type of view.

First we'll look at our new view, MVCBasicsPart4View. The difference is simply in the way the model is rendered.


class MVCBasicsPart4View : public AbstractView {
public:
	virtual void paintView( GraphicsContext* ctx ) {
		Control* control = AbstractView::getViewControl();

		Rect r = control->getClientBounds();		

		ctx->rectangle( &r );
		ctx->setColor( Color::getColor("white") );
		ctx->fillPath();		

		CircleModel* model = (CircleModel*)getViewModel();

		const std::vector<CircleShape>& circles = model->getCircles();
		for ( std::vector<CircleShape>::const_iterator it = circles.begin(); it!=circles.end(); it++ ) {
			const CircleShape& circle = *it;
			
			ctx->setColor( Color::getColor("blue") );
			ctx->circle( circle.center_, circle.radius_ );
			ctx->fillPath();

			ctx->setColor( Color::getColor("black") );
			ctx->circle( circle.center_, circle.radius_ );
			ctx->strokePath();

		}	
		
		EtchedBorder bdr;
		bdr.setStyle( GraphicsContext::etSunken );
		bdr.paint( &r, ctx );
	}
};
	
	

As we can see, in this view we first paint a solid white background, then render each circle with a blue fill and a solid black outline. And finally we draw a etched border around the client bounds of the control that "owns" this view.

Next we'll look at the controller class. What we have done here, is simply to migrate the code that we had previously put in our custom window class, and push it into our new controller class. First lets define our controller class:


class MyMVCController : public Component {
public:
	CircleModel* model_;

	void onMouseClicked( MouseEvent* e );

	void onKeyUp( KeyboardEvent* e );
};
	
	

We're deriving from Component so that we can create an instance of our controller, and then add it to some other Component instance, which will automatically delete it when the owner component is deleted. We'll add a member variable that holds a pointer to the current model to work with, which we will set when we create the controller.

Next we'll implement the onMouseClicked() and onKeyUp(). To do this, we just copy over the code that was previously in our MVCBasicsPart3Window.


class MyMVCController : public Component {
public:

	CircleModel* model_;

	void onMouseClicked( MouseEvent* e ) {	

		if ( e->hasLeftButton() ) {
			model_->addCircle( *e->getPoint(), 50 );
		}
		else if ( e->hasRightButton() ) {

			const std::vector<CircleShape>& circles = model_->getCircles();

			std::vector<CircleShape>::const_iterator it = circles.begin();
			Rect r;
			while ( it != circles.end() ) {
				const CircleShape& shape = *it;
				r.left_ = shape.center_.x_ - shape.radius_;
				r.top_ = shape.center_.y_ - shape.radius_;
				r.right_ = shape.center_.x_ + shape.radius_;
				r.bottom_ = shape.center_.y_ + shape.radius_;

				if ( r.containsPt( e->getPoint() ) ) {
					model_->removeCircle( shape );
					break;
				}
				it ++;
			}
			
		}
	}

	void onKeyUp( KeyboardEvent* e ) {

		if ( e->getVirtualCode() == vkDelete ) {
			model_->empty();
		}
	}
};
	
	

The next big change is in how we hook all of this together. Lets look at the code in our example4() function:


void example4( VCF::Event* ) {
	Window* wnd = new Window();
	MyMVCController* controller = new MyMVCController();

	wnd->setCaption( "MVCBasics Part 4" );

	CircleModel* model = new CircleModel();

	model->addCircle( Point(100,200), 100 );
	model->addCircle( Point(234,550), 300 );
	model->addCircle( Point(300,400), 80 );		

	View* view = new MVCBasicsPart4View();

	wnd->setView( view );
	model->addView( view );

	wnd->MouseClicked += 
		new MouseEventHandler<MyMVCController>( controller, &MyMVCController::onMouseClicked, "MyMVCController::onMouseClicked" );

	wnd->KeyUp += 
		new KeyboardEventHandler<MyMVCController>( controller, &MyMVCController::onKeyUp, "MyMVCController::onKeyUp" );

	controller->model_ = model;

	wnd->addComponent( controller );
	wnd->show();
}
	
	

We create our window instance - note that we do not need to derive a new window class. We create our controller, of type MyMVCController. We then create our model and view. We set the window's view to our view instance, which will cause the view to be responsible for painting the window. We also add the view to our model instance. Next we add the the controller's onMouseClicked and onKeyUp functions as event handlers to the window instance's MouseClicked, and KeyUp delegates. Finally we set the controller's model pointer, and we add the controller to our window component list. When the window is destroyed, it will destroy all it's own component's, which in this case is the controller.

Example 5.  Our last example will make a more complex UI. We will create 2 different views of our our circle model. One will present the model as a series of circles, for a graphical representation of the model. The second view will show the circle models state by displaying the number of shapes it currently has as well as the current "action".

5.3.5. Actions

5.3.5.1. Actions

This example will explain how to use the Action class in conjunction with various UI elements. It's a good way to see how the different elements work together. We'll create a simple app that does nothing useful, but will have menus, a toolbar, and a series of "states" that will change as we execute certain actions.

First, we'll add a toolbar:



	

5.3.6. Clipboard

5.3.6.1. Copy And Paste and the VCF Clipboard

The VCF allows you to make use of the clipboard services provided by the underlying OS and/or windowing system. This is done through 3 main objects that interact with each otehr during the copy and paste process. The first is the Clipboard itself, that serves as an interface to the underlying native clipboard. The Clipboard works with data that is wrapped in a DataObject, which can hold multiple data types. Finally, you stream data to and from a DataObject via concrete input or output streams.

In our example we'll create a window with two buttons, and attach an event handler to each, one that will handle coppying the data to the clipboard, and another that will take data from the clipboard and paste in into an edit control.

5.3.7. Drag and drop

5.3.7.1. DragAndDrop

Your content here, explaining the usage/functioning of your example.

5.3.8. Cursor handling

5.3.9. Scrolling

5.3.9.1. Scrolling

Your content here, explaining the usage/functioning of your example.

5.3.10. Image viewer

5.3.10.1. ImageViewer

Your content here, explaining the usage/functioning of your example.

5.3.11. Lists and combos

5.3.12. Tree view control

5.3.13. Tree list control

5.3.14. Table control

5.3.15. List view control

5.3.16. Dialog Usage

5.3.16.1. Dialogs

Your content here, explaining the usage/functioning of your example.

5.3.17. Common dialogs (open, browse dir, select color, select font)

5.3.18. Window handling

5.3.19. Labels

5.3.19.1. Labels

Your content here, explaining the usage/functioning of your example.

5.3.20. Text fields

5.3.21. Progress bar

5.3.21.1. ProgressBars

Your content here, explaining the usage/functioning of your example.

5.3.22. Menus

5.3.23. Toolbars

5.3.23.1. Toolbars

Your content here, explaining the usage/functioning of your example.

5.3.24. Table Control

5.3.24.1. Tables

Your content here, explaining the usage/functioning of your example.

5.3.25. Splitter controls

5.3.25.1. Splitters

This tutorial will explain and demonstrate how to easily customize and create container classes that implement your own layout algorithms. We will create two container classes that do this, one which will lay out it's child controls from left to right, taking up all the vertical space, and evenly dividing the horizontal space. The second container will demonstrate how to simulate a paged layout container, conceptually similar to Java's CardLayout class.

When creating a container, we generally only need to implement a single method: the Container::resizeChildren(). The rest of the methods of the Container interface are implemented in the AbstractContainer class, which we can derive from to save ourselves some time. When implementing resizeChildren() we may be passed in a Control pointer. If the control has just been added to the container, it will be passed in to resizeChildren(), but will not be in the Container's child control list. We need to test for this and act accordingly. Testing can be done like so:


virtual void resizeChildren( Control* control ) {
//rest of code omitted
bool controlJustAdded = false;
std::vector<Control*>::iterator found = std::find( controls_.begin(), controls_.end(), control );
controlJustAdded = (found == controls_.end());

}

	

The variable controlJustAdded will now indicate whether or not the passed in control has just been added to the container.

For our first container we'll simply layout all the child controls from left to right, dividing the width of the container control equally among them. So, to get started lets look at the class declaration:


class LeftToRightContainer : public AbstractContainer {
public:
  virtual void resizeChildren( Control* control );
};

	

That's it! That's all we need to declare to implement a container. We could have chosen to derive from StandardContainer, but we'll save that for our next container. Next we just need to implement resizeChildren(). It will look like this:



virtual void resizeChildren( Control* control ) {
	Rect clientBounds = controlContainer_->getClientBounds();
	
	if ( clientBounds.isEmpty() ) {
		return; //nothing to do, so just exit the function
	}
	
	bool controlJustAdded = false;
	if ( NULL != control ) {
		std::vector<Control*>::iterator found = std::find( controls_.begin(), controls_.end(), control );		
		controlJustAdded = (found == controls_.end());
	}
	
	double childCount = AbstractContainer::getChildCount();
	if ( controlJustAdded ) {
		childCount = childCount + 1.0;
	}

	double width = clientBounds.getWidth() / childCount ;

	Enumerator<Control*>* children = AbstractContainer::getChildren();
	Rect childBounds;
	double left = clientBounds.left_;
	while ( children->hasMoreElements() ) {
		Control* child = children->nextElement();
		
		childBounds.setRect( left, clientBounds.top_, left + width, clientBounds.bottom_ );
		child->setBounds( &childBounds );
		
		left += width;
	}

	if ( controlJustAdded ) {
		childBounds.setRect( left, clientBounds.top_, left + width, clientBounds.bottom_ );
		control->setBounds( &childBounds );
	}
}


	

The first thing we do is get the client bounds, by calling the controlContainer_->getClientBounds(). The controlContainer_ is a member variable that points to the containers current Control that it is attached to. The Rect that is returned takes into account any border that may be part of the control. If the rect is empty, we exit method immediately, as there is no need for any forther processing.

Next we determine if the control passed in is being added to the container for the first time. As we discussed before, we search through the controls_ vector using std::find. Next we determine the width that each control will be assigned. This is calculated by dividing the number of controls currently in the container by the width of client bounds (the clientBounds Rect variable). We store the count of controls ins childCount, and if the control that was passed in has just been added we increment the childCount by one. The childCount variable is incremented because the newlky added control has not yet been added to the controls_ vector (the framework will take care of this later).

Finally, we iterate through the list of child controls by calling getChildren() to get the enumerator, and then adjusting each child control accordingly.

5.3.26. Slider

5.3.26.1. Sliders

Your content here, explaining the usage/functioning of your example.

5.3.27. Application and Window icons

5.3.27.1. ApplicationIcons

Your content here, explaining the usage/functioning of your example.

5.3.28. Dates and Times UI

5.3.28.1. DateTimeUI

Your content here, explaining the usage/functioning of your example.

5.3.29. Visual Form Files

5.3.29.1. VisualFormFiles

Your content here, explaining the usage/functioning of your example.

5.3.30. Advanced User Interface

5.3.30.1. AdvancedUI

Your content here, explaining the usage/functioning of your example.

5.3.31. Basic Document/View techniques

5.3.31.1. DocViewBasics

Your content here, explaining the usage/functioning of your example.

5.3.32. Advanced Document/View

5.3.32.1. DocViewAdvanced

Your content here, explaining the usage/functioning of your example.

5.3.33. System Tray

5.3.33.1. SysTray

Your content here, explaining the usage/functioning of your example.

   Comments or Suggestions?    License Information