Your first SmartWin application
From SmartWin Wiki
Getting Started with SmartWin++
By Andrew Webb, SmartWin++ Developer
This document is intended to help you get started using the SmartWin++ library today.
If you have any comments, corrections, or other questions, please post in our support forum.
This tutorial builds upon the code from the example project HelloSmartWinWorld which can be found in the example directory of your SmartWin++ installation.
| Table of contents |
Contents
SmartWin.lib and its associated headers are a C++ GUI class library. Its executables are small and do not need external DLLs. It uses the Windows API (former WIN32 API) but presents its controls as high level templated C++ classes.
Hello SmartWin++ World - an introductory program
#include "SmartWin.h"
using namespace SmartWin;
class HelloWinClass
: public WidgetFactory< WidgetWindow, HelloWinClass >
{
private:
WidgetMenuPtr itsMainMenu;
WidgetButtonPtr itsButton;
WidgetCheckBoxPtr itsCheckBox;
public:
void menuSayHello( WidgetMenuPtr menu, unsigned item )
{
createMessageBox().show( _T("Hello !" ), menu->getText( item ) );
}
void menuClose( WidgetMenuPtr menu, unsigned item )
{
close();
}
void buttonClicked( WidgetButtonPtr button )
{
if ( itsCheckBox->getChecked() ) {
createMessageBox().show( _T("Hello World!" ), button->getText() );
} else {
createMessageBox().show( _T("Hello !" ), button->getText() );
}
}
void initAndCreate()
{
createWindow();
setText( _T("Hello SmartWin") ); // Title
SmartWin::Rectangle desktop( getDesktopSize() );
setBounds( desktop.top(0.2).left(0.3) , true );
itsButton = createButton();
itsButton->setText( _T("Hello from a button") );
itsButton->onClicked( &HelloWinClass::buttonClicked );
itsButton->setBounds( itsButton->getPosition(), Point( 200, 30 ), true );
itsCheckBox = createCheckBox();
itsCheckBox->setText( _T("&Global") );
itsCheckBox->setBounds( itsButton->getPosition(), Point( 200, 30 ), true );
// Creating main menu
itsMainMenu = createMenu();
WidgetMenuPtr file = itsMainMenu->appendPopup( _T("&MenuCommands") );
int m = 1;
file->appendItem( m++, _T("Hello from the menu"), &HelloWinClass::menuSayHello );
file->appendItem( m++, _T("Close"), &HelloWinClass::menuClose );
#ifndef WINCE
itsMainMenu->attach( this );
#endif
layout();
onSized( &HelloWinClass::isResized );
}
void isResized( const WidgetSizedEventResult & sz )
{
layout();
}
void layout()
{
SmartWin::Place p;
SmartWin::Rectangle r( getClientAreaSize() );
p.setBoundsBorders( r, 4, 4 );
itsCheckBox->setPositionPerPlace( p );
itsButton->setPositionPerPlace( p );
}
};
int SmartWinMain( Application & app )
{
HelloWinClass * testHello = new HelloWinClass;
testHello->initAndCreate();
return app.run();
}
Compilers & Environment Specific Information
Microsoft Visual C++ 7.1
Configure for SmartWin++
Put the SmartWin directory into Tools | Options | Projects | Directories | Include
Put the SmartWin lib directory into Tools | Options | Projects | Directories | linker
Creating a new SmartWin++ Project =
Use File | New | Project | Visual C++ Projects, and then select the Win32 Project template close to the bottom. Select a directory and then enter a project name. In Application Settings, check "Empty project", and press Finish.
After you have added (and written !) a .cpp file, right click on the project for Properties.
Then set C/C++ | Language | Enable Run-Time Type Info to Yes (/GR) for both the Debug and the Release configuration.
Then set Linker | Input | Additional Dependencies to SmartWinD.lib (Debug) and SmartWin.lib (Release).
Now would be a good time to build the debug and release versions to check your entries.
Troubleshooting
Compiler Errors
Problem - C1083: Cannot open include file: 'SmartWin.h': No such file or directory
Solution - Put the SmartWin directory into Tools | Options | Projects | Directories | Include
Problem - C2512:SmartWin::WidgetXXX<...> no appropriate default constructor available
Solution - Perhaps you forgot the Ptr in the declaration of 'WidgetXXX me;' instead of 'WidgetXXXPtr me;'
Linker Errors
Problem - warning C4541: 'typeid' used on polymorphic type ... with /GR.
Solution - You forgot to specify to use runtime type information.
Problem - error LNK2019: unresolved external symbol "public: int __thiscall SmartWin::Application::run .....
Solution - You forgot to add the SmartWin++ library when you created the project.
Problem - LNK1104: cannot open file 'SmartWinD.lib'
Solution - Put the SmartWin lib directory into Tools | Options | Projects | Directories | linker
Problem - LNK2001: unresolved external symbol _main
Solution - Link your application using /SUBSYSTEM:WINDOWS (Project | Properties | Linker | System | SubSystem)
Dev-C++
Download (http://www.bloodshed.net/dev/devcpp.html) Dev-C++ 5.0 beta 9.2 (4.9.9.2) (9.0 MB) with Mingw/GCC 3.4.2
Open the example hello project from \dev-c++\examples\hello just to check your basic installation.
Compile and run from the Execute menu.
Open the SmartWin.dev project, and choose Execute | rebuild all.
Copy the libsmartwin.a file into the /lib subdirectory.
Configure DEV-C++ for SmartWin++
Tools | Compiler Options | Directories | C++ includes , and browse for the SmartWin++ directory.
(It's the little file icon to the right of the blank line.) Then click Add, and then OK.
Choose Tools | Compiler Options | Directories | Libraries, and browse for the SmartWin++ directory plus \lib
Run an existing project
Open any .dev file (I tried Notepad) from the SmartWin++ unit tests projects.
Execute | compile and run . You should get the SmartWin++ Notepad running !
Windows
Layout of Widgets in windows
The fundamental sizing and positioning function is setSize( const Rectangle & rect).
The x,y position of the Widget is set to rect.pos, and the size of the Widget is set from rect.size.
If you like, you can just fix the position and size of each Widget at compile time.
Coordinate systems (X,Y relative to what ?)
Here are some things to keep in mind when you are dealing with X,Y coordinates.
- When you do getClientAreaSize() you will be returned a Point with the size of the client area
- If you do MyWidget->setSize( r ), the position in r is relative to the upper left point of the client area of the window.
- So if you want to generate a Rectangle that covers the client area, do SmartWin::Rectangle r( getClientAreaSize().size ); By passing only the size of the client area, the Rectangle constructor sets the position to be 0,0.
- If you use a WidgetChildWindow or a WidgetGroupBox with WidgetRadioButtons, then the button positions are relative to the GroupBox or WidgetChildWindow rather than relative to the client area of the window.
Why do Layout?
But fixed positions and size become tedious to implement, especially as the program design changes, and they do not adapt well to a changing runtime situation. The main issue is: How should the position and size of the Widget change as the application state changes?
The application state considers:
- The total window size. (can be User determined)
- Changes in what and how much the user wants to see.
- Changes in the size of graphics or text shown in the Widget.
There are three general approaches:
- Have fixed sizes for the Widgets, but position the Widgets as the window size and proportions change.
- Let the contents of the Widget dictate the size of the Widget. (but this may crowd out the rest of the Widgets)
- Partition the available space to each Widget present.
How to divide up a window into different areas
You divide the Rectangle by generating smaller Rectangles using Rectangle generator functions.
Start with the client rectangle of the window, which can be retrieved with getSizeClientArea(). Then use the Rectangle member functions to return other smaller Rectangles. For example;
SmartWin::Rectangle r( getSizeClientArea() ); Rectangle lowerHalf = r.bottom( 0.5 ); Rectangle lowright_quarter = lowerHalf.right( 0.5 );
Notice that lowright_quarter has 1/4 the area of r, and is its upper left is positioned at the center of r. This achieves the same result:
Rectangle lowright_quarter = r.bottom( 0.5 ).right( 0.5 );
Some other Rectangle functions are:
Rectangle left( double portion= 0.5 ); Rectangle right( double portion= 0.5 ); Rectangle top( double portion= 0.5 ); Rectangle bottom( double portion = 0.5 );
Rectangle row( int row, int rows ); Rectangle col( int column, int columns );
Or the more general:
void subRect( double x_fraction, double y_fraction, double width_fraction, double height_fraction );
If just one Widget fits, then just do setSize() on the Widget.
If you have a a row or a column of Widgets to fit in, then use void setSizeAsCol( const Rectangle & rect, int rows, int rownum ); void setSizeAsRow( const Rectangle & rect, int cols, int colnum );
If you keep dividing the rectangles into smaller rectangles until you have the desired position for each Widget, you will have the #3 layout scheme.
How to place multiple Widgets into an area defined by a Rectangle.
Either use the AspectSizable.h functions or the Place.h functions.
Here is my thinking behind the Place class and the Rectangle functions.
This is a recursive process until you get to a rectangle that will contain Widgets.
At any layout event you have a window of some size and its rectangle.
returning functions: However, if multiple Widgets are intended to fit in the Rectangle then you use the Place class to put them in either;
void setSizeAsGridPerPlace( SmartWin::Place & bound, int rows, int cols ); void setPositionPerPlace( SmartWin::Place &bound );
The difference being that setSizeAsGridPerPlace() resizes the Widget into a grid cell first, while setPositionPerPlace() preserves the existing size, and puts the Widget into the rectangle in a teletype manner: left to right, and then a new row as needed.
Note that repeated calls for Widgets of different sizes works with setPositionPerPlace().
Notice that the Rectangle division functions are pure math, the Place class knows nothing about Widgets; it only sets a Rectangle position, and then updates per the Rectangle size it was just given.
The Widget knowledgable portion remains in AspectSizeable.
The idea though is to use the Rectangle division process to get to the Rectangle you want to "place" Widgets into.
C++ as applied in SmartWin++
Templates
SmartWin++ heavily utilizes templates to ensure typesafety, small overhead and small footprint.
This makes the library code itself appear "funny" for non C++ experts but at the same time makes the library small, fast, typesafe and easy to extend.
