SmartWin - C++ Application Development made easy!

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++ ?

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.

« Return to Table of Contents

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 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 * in the declaration of WidgetXXX me; instead of WidgetXXX *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

« Return to Table of Contents

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 Contents

Linux/Wine

Placeholder...

« Return to Table of Contents

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 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:

  1. Have fixed sizes for the widgets, but position the widgets as the widow size and proportions change.
  2. Let the contents of the widget dictate the size of the widget. (but this may crowd out the rest of the widgets)
  3. 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.

« Return to Table of Contents

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.

« Return to Table of Contents

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;
    }
};

« Return to Table of Contents

 
SourceForge.net Logo