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.
- What is SmartWin++ ?
- Hello SmartWin++, an explained example program
- Compilers & Environment Specific Information
- Windows
- Widgets
- C++ as applied
What is SmartWin++ ?
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 WIN32 API, but presents the 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; WidgetButton * itsButton; WidgetCheckBox * itsCheckBox; public: void menuSayHello( WidgetMenu * menu, unsigned item ) { createMessageBox().show( _T("Hello !" ), menu->getText( item ) ); } void menuClose( WidgetMenu * menu, unsigned item ) { close(); } void buttonClicked( WidgetButton * 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() ); setSize( desktop.top(0.2).left(0.3) ); itsButton = createButton(); itsButton->setText( _T("Hello from a button") ); itsButton->onClicked( &HelloWinClass::buttonClicked ); itsButton->setSize( Point( 200, 30 ) ); itsCheckBox = createCheckBox(); itsCheckBox->setText( _T("&Global") ); itsCheckBox->setSize( Point( 200, 30 ) ); // 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 WindowSizedEventResult & sz ) { layout(); } void layout() { SmartWin::Place p; SmartWin::Rectangle r( getSizeClientArea().size ); p.setBoundsBorders( r, 4, 4 ); itsCheckBox->setPositionPerPlace( p ); itsButton->setPositionPerPlace( p ); } }; int main( 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 wrote !) 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 Debug and Release.
Then set Linker | Input | Additional Dependencies to SmartWind.lib (Debug) and SmartWind.lib (Release).
Now would be a good time to build the debug and release versions to check your entries.
Troubleshooting
Compiler ErrorsProblem - 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 * in the declaration of WidgetXXX me; instead of WidgetXXX *me;
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
Dev-C++
Go to http://www.bloodshed.net/dev/devcpp.html.
Download 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 libsmartwin.dev project, and Execute | rebuild all.
Copy the libsmartwin.a file into the /lib subdirectory.
Tools | Compiler Options | Directories | C++ includes , and browse for the SmartWin++ directory.
(Its the little File icon to the right of the blank line.) Then click Add, and then OK.
Tools | Compiler Options | Directories | Libraries, and browse for the SmartWin++ directory plus \lib
Open any .dev file (I tried Notepad) from the SmartWin++ unit tests projects.
Execute | compile and run . You should get the SmartWin++ Notepad running !
« Return to Table of ContentsLinux/Wine
Placeholder...
« Return to Table of ContentsWindows
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 getSizeClientArea() you will be returned a rectangle with a position relative to the upper left of the window, and 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( getSizeClientArea().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 widow 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().size );
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.
Widgets
LibraryLoader
This class helps to abstract the loading of dlls.
Look at the "sample" usage in WidgetRichTextBox. Basically you can instantiate it either with default CTOR and later call "load" or instantiate it with CTOR taking tstring to load a given dll.
It reference counts libraries (unless you slightly change parameter) and unloads when last DTOR
with given library loaded is reached...
It is designed to be thread safe...
LibraryLoader makes it t possible to load libraries on demand instead of garbaging" up the application
instance with all libraries we may later need...
The "Normal use" is instantiation as static object in function needing dll which ensures object
instantiation is done once, but can also be used as a normal stack object as long as the last DTOR with
given library is not called before there's no need for the given library. It should probably be extended
later to retrieve e.g. function pointers inside dlls.
C++ as applied in Smartwin++
Coding standards used in SmartWin++
class Zoo // We capitalize Classes. { Human itsKeeper; // We prefix with "its" to indicate member variables. void setKeeper( Human keeper ) // We start with lower case for member functions. { itsKeeper= keeper; } };