The source files contain two workspaces:
- jlWindows itself, generating a .lib.
- Testor, having jlWindows as a dependency.
Please visit jlWindows hosted by MtpForge here.
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:
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
<PRE lang=c++>bool jlTextFieldCallback::componentCallback(HWND WindowHandle,
UINT Message, WPARAM WParam, LPARAM LParam)
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
The following code creates the sample app (whose screenshot and source code is above):
-- main.cpp --
<PRE lang=c++>#include <windows.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();
// sets a default callback handler for the window
// this callback handler calls PostQuitMessage(0) on WM_DESTROY
tb->addButtons(2, 0); // addButtons (int count, int start_image)
// 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
// just an example of listview that does nothing
jlListView * lv = new jlListView();
lv->addItem(0, "blah1"); // addItem (U32 line_index, char * text)
// receives and dispatches messages to jlWindows Components
// this function returns when the message WM_QUIT
// (sent by PostQuitMessage()) is received
-- mytoolbarcallback.h --
<PRE lang=c++>#ifndef MYTOOLBARCALLBACK
class MyToolbarCallback : public jlToolbarCallback
virtual bool buttonClicked(int button_id);
#endif // MYTOOLBARCALLBACK
-- mytoolbarcallback.cpp --
<PRE lang=c++>#include <windows.h>
bool MyToolbarCallback::buttonClicked(int button_id)
sprintf(msg, "%d", button_id);
MessageBox(NULL, msg, "toolbar", MB_OK);
How it works
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
<PRE lang=c++>void jlComponent::loop()
while ((Status = GetMessage(&Message, NULL, 0, 0)))
if (Status == -1)