Thursday, October 12, 2006

Lit Window Library Design Thoughts #1

Abstract

Summary: Explains how to call C++ methods from the Lit Window Library rules system.

Audience: C++ GUI Developer

Difficulty: Advanced

Introduction

Hello, this is the first in a series of articles detailing the ideas behind the Lit Window Library. I want to share my thoughts about the design process and also answer questions I am getting via eMail. If you have not heard of the Lit Window Library yet, you might want to head over to www.litwindow.com/library and browse the official site. This blog keeps you informed about what is going on behind the scenes.

In short: The Lit Window Library extends the decade old 'Method, Property, Event' paradigm in GUI programming with a fourth concept: 'Rules', sometimes also called 'Constraints'. A 'Constraint' is a condition, such as 'frame.title = listbox.selection', which you set at design/compile time. The Lit Window Library constraints solver will ensure that the condition will always be true and update the values - frame.title in this example - whenever a dependent value changes. This greatly reduces the amount of bookkeeping code and together with 'data adapters' (please see www.litwindow.com/library) allows creating a library of frequently used user interface mechanisms. I hope to be able to reduce GUI coding times by up to 90% while at the same time allow tight integration between the new 'rules' mechanism and traditional C++ coding methods.

Which leads me to my first topic, which I received as a question via email:

Q. How do I tell the Lit Window Library to call my C++ code whenever a value changes? I want to play a sound everytime the selection in a list box changes.

Let's assume you have a list box and a frame window and you want to display the value of the current list box selection in the title of the frame. The rule would be 'my_frame.title = my_listbox.selection'. Once set, the Lit Window Library will update the frame's title automatically whenever the list box selection changes. Now let's assume you want to play a sound everytime the selection changes. Or call any other of your own C++ code.

Note: what follows is my design idea, which has not been implemented yet.

There are three different ways at different abstraction level to achive this. Actions, named rules and change notifications.

Actions

Actually I stole the term from Delphi. If you've ever used Delphi, you will be familiar with the concept. An action object is something that is fired by a trigger. A trigger is usually either a button or a menu event. The action calls a method. The advantage of this over other C++ libraries is simple: if you disable the action, all connected triggers will be disabled automatically. If you have, say, a 'Save As...' action and disable it, all toolbar buttons, menu entries, popup menus etc... that are connected to this action will appear disabled without any further work on your part. I like this approach a lot better than the usual OnSaveAsUpdate event where you have to query the SaveAs state for every menu entry, every button and every toolbar separately. A lot of work just to reflect the state of the application in a handful controls.

In the Lit Window Library you create an action object and connect a C++ method to it using a 'function adapter'. A 'function adapter' is a simple piece of code that reflects the C++ method into the rules world, making the method known by name. Think of it as a language binding, much like the language binding between Python and C++. Syntax in the rule system could look like this:

Action SaveAsAction;

SaveAsAction.method = MyCppMethod;

SaveAsAction = SaveAsButton + SaveAsMenu + SaveAsToolbar;

SaveAsAction.Enabled = somedata.state == IsDirty;

You declare the object SaveAsAction, assign the C++ method "MyCppMethod" to it, connect a button, a menu entry and a toolbar button and tell the Lit Window Library system to enable the action - and thus the button, menu and toolbar, whenever somedata.state equals IsDirty.

How does this help to play a sound whenever the list box selection changes? Simple. A changing selection is a trigger, just like a button that is pressed. Here is the code:

Action PlaySoundOnSelectChanges;

PlaySoundOnSelectChanges.method = MyCppPlaySoundMethod;

PlaySoundOnSelectChanges = my_listbox.OnSelectionChanging;

This looks very similar to the 'OnEvent' mechanism used by GUI class libraries. Not suprising, because it is the same concept, spiced up using the action idea. PlaySoundOnSelectChanges is an action object that calls 'MyCppPlaySoundMethod' whenever it is enabled and triggered. my_listbox.OnSelectionChanging is a trigger that fires everytime the selection changes.

Thats the first method to play a sound whenever the selection is changing.

Named rules

Somewhat lower in the hierarchy are named rules and rule evaluation events. The Lit Window Library uses a constraints solver to update values when dependent values change. The constraints solver uses rules as its input. You've seen examples of rules above. All of these rules where unnamed rules. The Lit Window Library rules syntax allows named rules, giving a rule or a set of rules a name by which they can be accessed. Here is the syntax:

selection_changing: frame.title = listbox.selection;

The general syntax is {name :} rule, where the name and colon are optional. If they are omitted you are specifying an unnamed rule. You can also group several rules together and give the whole group a name. { name : } '{' rule '}'.

The constraints solver publishes boost::signals which your code can connect to. Different signals are fired for different actions and events. One signal is the 'value(s) changing' signal. It is fired everytime the value or values of a rule or group of rules are changing. To play a sound whenever the list box selection changes, you need to write your OnCppPlaySoundMethod and connect it to the constraints solver 'value(s) changing' signal.

RapidUI m_rapidUI;

// init rapidui object with

// data and window objects

// now connect the event

m_rapidUI.get_rule("selection_changing").value_changing.connect(boost::bind(&OnCppPlaySoundMethod));

get_rule(string) returns the named rule object. It has a member 'value_changing', which is a boost::signal (see www.boost.org for more info). connect takes a function object which gets called everytime the signal is fired. The rest is taken care of by the constraints solver. Whenever the value of my_listbox.selection changes, the constraints solver will reevaluate the rule and will change the value of the frame.title. Before it does that, it fires the 'values_changing' signal of this rule, giving your C++ code a chance to act. Not shown here is the actual interface of the OnCppPlaySoundMethod, but the interface also allows your C++ code to change the value before it is assigned or otherwise modify the rules behaviour. This allows very complex interactions between the Lit Window Library rules system and your own C++ code and this is also one of the very important and strong points of the system: I wanted to create system that allows a programmer to switch between both world very, very easily - the rules world and the classic C++ sequential world.

Change notifications

Change notifications are at the lowest level of the abstraction. For the rules system to work, it needs to be told whenever a value changes. Values can be C++ members, in which case you - the programmer - must call litwindow::NotifyChanged(value) every time you assign a new value to the member variable. Lit Window Library predefined objects, such as Listbox and other GUI objects already do this behind the scenes everytime you call SetSelection or a user changes the selection via mouse or keyboard for example. litwindow::NotifyChanged sends the information that object 'x' at location 'y' has changed to all active constraints solvers. The constraints solvers in turn search their lists for rules that are affected by this change and reevaluate them, possibly updating other values, sending more change notifications and updating yet other values until all rules have been reevaluated and a new set of values has been assigned - or until a conflict has been found.

These notifications are low level change notifications. They are not sent for rules, they are sent for individual objects. Like with rules you can connect your own C++ code to a change notification.

RapidUI m_rapidUI;

m_rapidUI.get_data("frame.title").value_changing.connect(boost::bind(&OnCppPlaySoundMethod));

Note that I connected the OnCppPlaySoundMethod to the frames title object here. I could have connected it to my_listbox.selection. But since a change in the selection (actually a change in the value of the selection - the string value of the currently selected list box entry) will automatically change the title as well, OnCppPlaySoundMethod will still be called when the selection changes. The change is propagated to the frame.title object by the constraints system.

Thats all from me for tonight. I hope you found my first public rambling on the Lit Window Library ideas interesting and perhaps you want to send me some feedback. I always like to hear other ideas, thoughts, critisism and support.

Best regards

Hajo Kirchhoff (www.litwindow.com)

1 comment:

Christof said...

Good to read that you continue. I had watched your articles on litwindow.com and due to lack of updates assumed, that the project had fell asleep.
Sounds promising.
Keep on
Christof