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