Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A C++ Win32 GUI library for Java-AWT-like source code

0.00/5 (No votes)
15 Dec 2005 1  
This library provides a simple set of classes for creating GUIs. It uses only the Win32 API with some common controls. The development is at the beginning and many changes are to be done.

The source files contain two workspaces:

  • jlWindows itself, generating a .lib.
  • Testor, having jlWindows as a dependency.

Please visit jlWindows hosted by MtpForge here.

Introduction

The global aim of the library is to provide a really easy to use GUI API. Creating a small GUI for a simple piece of software should be very fast and clean, only few code lines, with the behavior of components isolated in classes.

In future, this library could allow the creation of remote GUIs, i.e., new jlWindow("192.168.xx.xx"); or something like that. Another idea is to make jlWindows GUIs dynamically modifiable, for example, allowing to cut/paste buttons or textfields over tabs, and making all the components resizable and even renamable. I also would like to port it to X.

For now, I'm trying to override the native Win32 functions and data structures, which are, in my mind, pretty messy. So jlWindows tends to give a consistent view of the existing API. Then I want to provide more functionalities, like layout managers that auto-organize components inside a window.

The library was created in April 2002, however it is very far from its final version.

Today, it allows to:

  • create some basic components: windows, buttons, textfields, listviews...
  • set some properties of the windows like parent, position, size, window title...
  • set some specific properties like listviews' icons set, buttons' caption or icon...
  • set a callback handler by the component with some default behavior.

How you can use it

There are two base classes:

  • jlComponent
  • jlCallbackHandler

Components are visible stuff, like windows with title bar, buttons and textfields. Each kind of component has a class that inherits -directly or not- jlComponent. You can create a component passing no argument at all to the constructor, but you can also pass the parent as a parameter and the styles, which are Win32 styles. After a component's creation, you cannot change its styles anymore. But you can change any other property, by calling the right method. You can even change the parent of a window, thus moving a component from one place to another.

You can give a component a callback handler, which will receive the Win32 messages and do stuff with it. Each type of component has an associated type of callback handler, because, for instance, a button does not do the same things when it's clicked, as a listview. Each interactive component of your graphical user interface will have a callback handler. You can use the default behavior of jlWindows' callback handlers, like jlWindowCallback that calls PostQuitMessage() when it receives the message WM_DESTROY. But most probably you will have to override the callback handlers to give your application the behavior you want.

The job of a callback handler is to call a particular method (on itself) depending on the message it received. It has one method which is called each time the associated component(s) receives a message. That method should return true if the message was not handled, or more generally, if you want the default callback function of the component to be called.

Here is the callback handler's dispatch function of jlTextField:

bool jlTextFieldCallback::componentCallback(HWND WindowHandle, 
                   UINT Message, WPARAM WParam, LPARAM LParam)
{
    switch (Message)
    {
        case WM_DROPFILES:
            return filesDropped(WParam);
            break;
    }

    return true;
}

When you override a callback handler, your job is to fill those methods called by the dispatch function of the callback handler, like, in the example above, the method filesDropped().

Example

The following code creates the sample app (whose screenshot and source code is above):

-- main.cpp --

#include <windows.h>

#include <stdio.h>


#include "jlWindows.h"

#include "resource.h"

#include "mytoolbarcallback.h"


int WINAPI WinMain(HINSTANCE hInstance, 
     HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    //     jlComponent  ( jlComponent * parentcomponent = NULL ,

    //                   DWORD style = 0 , DWORD exstyle = 0 );

    jlWindow * win = new jlWindow(NULL, WS_OVERLAPPEDWINDOW);
    jlToolbar * tb = new jlToolbar();

    win->setTitle("jlWindows Testor");
    win->setSize(400,300);
    win->show();
  // sets a default callback handler for the window

  // this callback handler calls PostQuitMessage(0) on WM_DESTROY

    win->setCallbackHandler(new jlWindowCallback());

    tb->setButtonSize(16, 16);
    tb->setImageList(IDB_BITMAP1);
    tb->addButtons(2, 0); // addButtons (int count, int start_image)

    tb->addButtons(2, 0);
    // I created a simple class MyToolbarCallback to handle

    // the messages the tb receives. It just pops a messagebox up

    // with the zero-based number of the button of the toolbar

    tb->setCallbackHandler(new MyToolbarCallback());

    // just an example of listview that does nothing

    jlListView * lv = new jlListView();
    lv->setPosition(30,30);
    lv->setSize(200,200);
    lv->addColumn("second");
    lv->addColumn("third");
    lv->setImageList(IDB_BITMAP1);
    lv->addItem(0, "blah1"); // addItem (U32 line_index, char * text)

    lv->addItem(1, "blah2");
    lv->addItem(2, "blah3");
    lv->setMasterColumn("first");

    win->addToolbar(tb);
    win->addComponent(lv);

  // receives and dispatches messages to jlWindows Components

  // this function returns when the message WM_QUIT

    // (sent by PostQuitMessage()) is received

    jlComponent::loop();

    return 0;
}

-- mytoolbarcallback.h --

#ifndef MYTOOLBARCALLBACK
#define MYTOOLBARCALLBACK


#include <windows.h>

#include "jlWindows.h"



class MyToolbarCallback : public jlToolbarCallback
{
    virtual bool buttonClicked(int button_id);
};


#endif // MYTOOLBARCALLBACK

-- mytoolbarcallback.cpp --

#include <windows.h>

#include <stdio.h>

#include "mytoolbarcallback.h"



bool MyToolbarCallback::buttonClicked(int button_id)
{
    char msg[10];
    sprintf(msg, "%d", button_id);

    MessageBox(NULL, msg, "toolbar", MB_OK);

    return false;
}

How it works

Each jlComponent has the HANDLE of the Win32 item (usually called window, whatever it is, button, menu or listview) it is associated with. When creating a jlComponent, a call to CreateWindowEx() is performed, with the styles and parent you gave in the parameters or with the default ones. Buttons, listviews and stuff like that use predefined window classes, with predefined callback functions that make them behave the standard way. But just after their creation, the windows are given a new callback function, jlWindows' one, which is common to every jlComponent. The windows are also given particular "user data". Each window receives as user data, a pointer to the jlComponent associated with it. Those properties are set by calling SetWindowLong(). Then, in jlWindows' global callback function, that user data is retrieved from the window that received a message. That way, jlWindows knows what jlComponent a message is destined to. It just has to call a method on it, and that jlComponent will forward the message to its callback handler, if any.

Note that if jlWindows' global callback function detects that a jlComponent did not handle a message, it calls the standard callback function of the window associated with it, which has been saved when setting jlWindows' callback function. Most of the times, one should let jlWindows call the standard callback function of a component, because it generally deals with its display. For example, overriding the behavior of a button when it's clicked without letting jlWindows call its standard callback function, will prevent it from being displayed pushed.

After creating the components, setting their properties, their callback handlers and showing them, you have to call jlComponent::loop();. That static method will receive and dispatch the messages that the graphic interface you designed receives. The code of that method is typical and always the same in every C++/Win32 application. That loop ends only when it receives the WM_QUIT message:

void jlComponent::loop()
{
    MSG Message;   
    int Status;

    while ((Status = GetMessage(&Message, NULL, 0, 0)))
    {                        
        if (Status == -1)
             break;

        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here