[litwindow-users] Re: Adding Dialog handlers to RapidUI
Status: Alpha
Brought to you by:
hajokirchhoff
|
From: Hajo K. <mai...@ha...> - 2005-04-18 14:19:42
|
yrs90 wrote:
> The documentation gives the example of
>
> m_rapidUI.AddWindow(this);
> m_rapidUI.AddData(make_accessor(g_data));
> m_rapidUI.AddRules(g_rules);
> m_rapidUI.Start();
>
> For dialog boxes, should one declare a new RapidUI instance or can the
> dialog binding rules be added to the existing RapidUI instance? If added to
> the existing instance, does one just call all of these functions again?
Both is possible. RapidUI is a "mediator". It simply ensures that all
the rules evaluate to true all the time. When you display a dialog, you
can either add the dialog window, data and rules to an existing RapidUI
object and remove the windows/data/rules before you destroy the dialog.
But I think a cleaner design would be to create a new RapidUI object and
use that. Here is what I usually do:
{
MyDialogClass dlg(this);
// create a new RapidUI mediator
RapidUI dlg_mediator;
// add the dialog
dlg_mediator.AddWindow(&dlg);
// add the data
dlg_mediator.AddData(make_accessor(some_data));
// add rules if implicit rules are not enough
dlg_mediator.AddRules...
// now start the mediation
dlg_mediator.Start();
// and show the dialog
if (dlg.ShowModal()==wxOK) {
}
}
> Is the BEGIN_RULES() declaration always at global scope? Must it always
> take an instantiated global as a parameter? Can it be placed in a class and
> declared with class member data?
Think of rules as a "function" (actually it is a function).
BEGIN_RULES() is the "function" declaration. C++ does not allow nested
functions, so BEGIN_RULES cannot be nested either. (Might change in the
future).
BEGIN_RULES can take an instantiated global as a parameter, but you are
much better of if you pass names rather than instances. I explained this
in one of my previous posts, when I wrote about it being similar to
static vs. dynamic linking.
If you use make_expr<wxString>("m_member") instead of
make_expr<wxString>(m_member), IOW if you pass the name of a member
variable as a char* instead of the variable itself, the variable itself
need not be instantiated when you write the rule. Instead each rule
looks up the name in the window/data set you pass to the RapidUI object
whenever neccessary.
> I notice that litwindow::ShowModalDialog doesn't exist yet. Does this mean
> it is not currently suited for use with dialogs?
Not at all. In fact, litwindow::ShowModalDialog will do the steps I
described above. Dialogs are probably the area where the current
implementation will help you the most.
A very useful feature especially for Dialogs are the implicit rules.
When all you have is a 1:1 relation between a widget and a member
variables, you need not bother with rules at all. Before RapidUI::Start
activates the constraint solver, it iterates over all windows and
children you passed in with AddWindow. For every window it tries to find
a data element of the same name in the data objects you passed in with
AddData. If there is one, it creates a TWOWAY rule.
If you have
struct SomeStruct
{
long m_long;
bool m_bool;
};
construct a dialog with a checkbox and an editctrl. Name the checkbox
"m_bool" and the editctrl "m_long".
SomeStruct myStruct;
SomeDialog dlg;
RapidUI rui;
rui.AddWindow(&dlg);
rui.AddData(myStruct);
rui.Start();
dlg.ShowModal();
Upon initialization the values of myStruct are transferred to the
widgets and are kept in sync. Everytime the user changes a value, the
values are copied back to myStruct. So there is no need to call
"TransferToData", because once the constraint solver is active, the
widgets and myStruct member variables will always be in sync.
> If there are rules governing data transfer from the dialog widgets to a data
> structure, by whom and where does the NotifyChanged get called?
The window adapters (wxListBoxAdapter, wxTextCtrlAdapter, etc...) are
also event handlers and react to the window events. They call
NotifyChanged automatically.
You can also call NotifyChanged manually if you want. So if your Dialog
has a button '+' that should increment the m_long value, write an
OnButtonClicked function as usual.
void SomeDialog::OnButtonClicked(...)
{
// change the data element
++myStruct.m_long;
// and tell the rapidui mechanism it has changed
// this will automatically update the edit control
// and all other widgets that depend on this
NotifyChanged(myStruct.m_long);
}
Have a look at the lwwx samples. There is a "generic_ui" sample which is
currently broken for a couple of reasons, but it shows how to use the
principles quite well.
> Would a wxWizard be treated like a series of dialog boxes with a rule set
> for each page?
Even simpler. A wxWizard is really a special kind of notebook which does
not have tabs, but still switches between the pages. The point here is
that the wxWizard is the top window and the pages themselves are child
windows. So you can treat the wxWizard just as you would a single
dialog. Only one RapidUI object is neccessary and you would call
AddWindow once only and pass the wxWizard window to it. And you only
need one huge struct containing all elements you want to show in the
wizard. The struct can of course have member structs like this:
struct PageOne { ... };
struct PageTwo { ... };
struct WizardData
{
PageOne m_one;
PageTwo m_two;
};
WizardData myData;
wxWizard myWizard;
RapidUI rui;
rui << myWizard << myData << RapidUI::start; // shorthand notation
myWizard.ShowWizard...
Ah, no, wait. There is a catch caused by the wxWizard design, if I
remember correctly. The problem is, you cannot start the RapidUI
mechanism before you Start the Wizard because unlike the Dialog, the
Wizard creates its child pages only after Start. So you have to override
the Start method and call RapidUI::Start there, before Wizard::Show.
Name lookup is recursively, which does have the side effect though that
the XRC names must be unique. Hm, actually thats not even true. Lookup
resembles C++ scopes. Think of the wxWizard as a multiple inheritance
class. Each page is a base class. If two pages "one" and "two" each have
a widget named "m_checkme", you can refer to "one.m_checkme" and
"two.checkme" in the rules. The only drawback is that implicit rules
won't work then.
>
> So many questions... :)
Yes, I love it :) Means someone is using this thing ;) I think I will
compile a document from them.
I really wished I had more spare time to enhance this. Any idea where I
might get some funding? That would speed up things greatly.
But even with the current snail pace it will get there eventually.
Hajo
|