litwindow-users Mailing List for Lit Window Library (Page 3)
Status: Alpha
Brought to you by:
hajokirchhoff
You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(13) |
Dec
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
|
Feb
|
Mar
|
Apr
(62) |
May
(10) |
Jun
(1) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2008 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(2) |
2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(1) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
From: Hajo K. <mai...@ha...> - 2005-04-19 08:19:22
|
Hi, > I used RapidUI with wxWizard as suggested. It worked great, even > interfacing to the class data through member functions. I am glad to hear that. > Since the UI and data are updated without regard to whether the user cancels > the action, I passed a new instance of the data structure to RapidUI. If the > user "Finish"es the wizard, the data is copied into the real data structure. > I presume that this is the expected usage pattern? For the moment being, yes. Later I'll introduce the notion of "commit levels". Commit levels are a classification of when the relevant rules will be evaluated. For GUI elements there could be + immediate - useful for buttons of all kind including checkboxes etc... + focus lost - useful for textcontrols etc... + explicit - commit must be triggered explicitly, such as with an apply/ok button. > After copying the wizard changes, I called NotifyChanged(g_data). Nothing > happened. Not until I did an insert operation (which also triggers a > NotifyChanged) did the UI update and show the changes introduced by the > wizard. Please post some code. NotifyChanged accepts three parameters. 1. An accessor pointing to the element that has changed. 2. bool recursive - true meaning that a subelement (member, inherited etc...) has changed, false meaning only the topmost view of the element will be evaluated. useful for aggregates and containers only. 3. bool immediate - if true, triggers reevaluation immediately, if false, wait for another call to NotifyChanged with immediate=true If you've set up everything correctly and have Start()ed the rapidUI mechanism, then NotifyChanged should update all widgets immediately. > At first I thought that the NotifyChanged might be interacting with my local > instance of RapidUI, wiz_mediator, instead of m_rapidUI, but none of the NotifyChanged sends its events to all RapidUI objects currently instantiated. > variations I've tried has yielded any updates. I even tried creating an > accessor and using m_rapidUI->ValueChanged(..). (I used recursion=false, > but I even tried naming the particular attribute that changed.) How do I > convince RapidUI to reevaluate the rules? If it does not reevaluate the rules it probably means that you are passing in an object that does not appear in the rules anywhere. No rule depends on it. For example, if you pass a local copy of the data to RapidUI, then call NotifyChanged(g_data) for the gobal instance, nothing will change. > I also found myself asking these questions: Can NotifyChanged be called on > any sub-element of the data structure? If I call NotifyChanged on some leaf Yes. Either call NotifyChanged(g_data.this_has_changed); or use the other version: NotifyChanged(g_data, "this_has_changed"); > of data will RapidUI know that a Rule referencing a branch containing the > changed data needs to be reevaluated? Aahh, now we are coming to the juicy details :) Suppose you have a data structure: Top.Middle.Leaf and these rules: Rule_1: widget.property = Leaf Rule_2: widget2.property = Middle Your question is, if I understand it correctly, if you call NotifyChanged for leaf, will Rule_2 be reevaluated as well, since if Leaf has changed, Middle has changed implicitly as well. This situation can get very complex, especially if you consider side effects where the value of one leaf is calculated from the value of a different leaf such as bool IsEnabled() const { return m_admin_rights && m_logged_in; } If you have Data.Enabled Data.AdminRights Data.LoggedIn properties and change LoggedIn, Enabled will change as well. For now the library ignores these side effects if you call NotifyChanged for the Leaf only. In such situations you must call NotifyChanged for the node above the changes and set the 'recursive' parameter to true. NotifyChanged(Data, true, true); RapidUI expands this to NotifyChanged(Data.Enabled, false, false); NotifyChanged(Data.AdminRights, false, false); NotifyChanged(Data.LoggedIn, false, false); NotifyChanged(Data, false, true); > By the way, the rule that I am expecting to update is of the form: > RULE("ID_FRAME.Title", make_const<wxString>("MyProg: ") + > make_expr<wxString>("Name")) > > where Name resolves to GetName(). Looks fine to me. But please post some more code. Best regards Hajo |
From: yrs90 <yr...@ya...> - 2005-04-19 07:46:05
|
> No requirements for adding a constructor/destructor. Must be a problem > on your side. Ah, found it. Indeed a problem on my side. In my constructor I called an internal clear function in which I call NotifyChanged(). :/ Obviously, it is hard to notify a half-constructed object. Thanks, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: yrs90 <yr...@ya...> - 2005-04-19 07:16:16
|
> > This code returns 'valid but not a container'. Before I put the > > is_container() check in, it generated a runtime error. > What do you pass to "Items"? I am passing a vector<class MyListData> to Items. Specifically, RULE("xrcMainList.Items", make_expr<accessor>("m_ListData")) Where vector<MyListData> m_ListData; BEGIN_ADAPTER(DataClass) PROP(m_ListData) END_ADAPTER() IMPLEMENT_ADAPTER_CONTAINER(vector<MyListData>) At least, m_items.is_container() == true, because the FillList() function is populating the wxListCtrl and it only populates the list if m_items.is_container() is true. > No requirements for adding a constructor/destructor. Must be a problem > on your side. Have you tried rebuilding? Yes I made sure to do a clean rebuild. I'll look at it more closely after I get further along. > Not having a debugger is not really an option IMHO for something so new > as the lit window library. Yes, I agree. I only have this problem building debug versions if I use LitWindow and consequently STL. Using a debugger is possible as it is, but it is quite tedious to use without symbol tables. Unfortunately I have to factor in the cost of buying new tools for a couple of C-runtime libraries into the decision about whether to commit to using LitWindow. I'll take a look at the VC 2005 Express Beta to see if that will resolve the immediate need. Thanks, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-19 06:17:24
|
yrs90 wrote: > This code returns 'valid but not a container'. Before I put the > is_container() check in, it generated a runtime error. What do you pass to "Items"? > In a separate problem, I added a constructor/destructor to an otherwise > unchanged class that I pass to the framework and suddenly I started getting > runtime access violations. Is there some type of change to the ADAPTER > declaration required when there is a constructor in the class? No requirements for adding a constructor/destructor. Must be a problem on your side. Have you tried rebuilding? BTW, you will get lost very quickly without a debugger. If you cannot afford the Visual Studio Standard edition and if you are adventureous you could download the visual studio .NET 2005 C++ Express version and use that debugger there (I think it has one). It should be able to debug code generated by the free MS tools, provided the free tools let you switch on debugging information. Not having a debugger is not really an option IMHO for something so new as the lit window library. Best regards Hajo |
From: yrs90 <yr...@ya...> - 2005-04-19 06:02:31
|
I used RapidUI with wxWizard as suggested. It worked great, even interfacing to the class data through member functions. Since the UI and data are updated without regard to whether the user cancels the action, I passed a new instance of the data structure to RapidUI. If the user "Finish"es the wizard, the data is copied into the real data structure. I presume that this is the expected usage pattern? After copying the wizard changes, I called NotifyChanged(g_data). Nothing happened. Not until I did an insert operation (which also triggers a NotifyChanged) did the UI update and show the changes introduced by the wizard. At first I thought that the NotifyChanged might be interacting with my local instance of RapidUI, wiz_mediator, instead of m_rapidUI, but none of the variations I've tried has yielded any updates. I even tried creating an accessor and using m_rapidUI->ValueChanged(..). (I used recursion=false, but I even tried naming the particular attribute that changed.) How do I convince RapidUI to reevaluate the rules? I also found myself asking these questions: Can NotifyChanged be called on any sub-element of the data structure? If I call NotifyChanged on some leaf of data will RapidUI know that a Rule referencing a branch containing the changed data needs to be reevaluated? By the way, the rule that I am expecting to update is of the form: RULE("ID_FRAME.Title", make_const<wxString>("MyProg: ") + make_expr<wxString>("Name")) where Name resolves to GetName(). Regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: yrs90 <yr...@ya...> - 2005-04-19 05:19:05
|
On accessing items: I tried the following code. Too bad, I can't create a debug build and use a debugger. wxListCtrl *pList = wxDynamicCast(event.GetEventObject(), wxListCtrl); aggregate ag(make_aggregate(*pList)); accessor ac(ag["Items"]); if (ac.is_valid() && ac.is_container()) { container c = ac.get_container(); // yeah, it worked... etc. } else if(ac.is_valid() && ac.is_aggregate()) { wxMessageBox("is an aggregate"); } else if(ac.is_valid()) { wxMessageBox("valid but not a container"); } else { wxMessageBox("not valid"); } This code returns 'valid but not a container'. Before I put the is_container() check in, it generated a runtime error. In a separate problem, I added a constructor/destructor to an otherwise unchanged class that I pass to the framework and suddenly I started getting runtime access violations. Is there some type of change to the ADAPTER declaration required when there is a constructor in the class? I'll send additional details if the answer solution isn't immediately apparent. Best regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Jake S. <ja...@za...> - 2005-04-19 05:09:43
|
Hajo Kirchhoff wrote: > > My questions: > > ? Do you know the boost library? Yes > ? Do you think downloading and installing another library is a major > obstacle? Not a big obstacle for anyone who is actually interested. > ? Should a Lit Window Library distribution include boost, even if it > will increase in size by a couple of MB. I think to help the spread and acceptance of Lit, everything should be done to make the installation as painless and easy as possible - this would apply to both the development version and the user version. wxWidgets is a good example - it gives you everything you need in one install > ? Should a Lit Window Library distribution include only those parts of > boost that it needs rather than the entire boost library? This is a bit trickier, but I don't think disk space is really an issue any more. I think that a development version should include the entire boost library. > ? Should a binary - prebuild version for the Lit Window Library - > including Boost binaries - be available? If so, for what compilers? Yes, this will help people that just want the power of the library to start working straight away (this would be the 'user' version I was referring to above). I think that a windows compiler (msvc 7 or .net), a linux compiler (gcc) and a mac compiler (i don't know what - wxWidget boys will know) would be best. Yes there are plenty of others but these seem to be the ones that crop up the most - I'm sure that eventually versions for more compilers will appear. > > Finally I'd like to phase out support for MSVC version 6. The Lit > Window Library requires templates and it is getting increasingly > difficult to work around the MSVC 6 limitations. I think that this is a good idea. Choose your compilers from above and stick with them. If a compiler has limitations that hamper the forward progression of the library then I think they need to be ignored. This library is trying to make some massive changes to the way gui's are constructed and maintained - this is something that has never really gained any attention, yet it is one of the main ways we interact with computers! I think what you are trying to do here is too important to worry about backward compatibility with an old compiler! Not only that this library combined with wxWidgets and Boost, people have been dreaming about this for ......... well just about ever! jake :) |
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 |
From: yrs90 <yr...@ya...> - 2005-04-18 12:54:39
|
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? Does the dialog need to be deregistered after it is destroyed? 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? I notice that litwindow::ShowModalDialog doesn't exist yet. Does this mean it is not currently suited for use with dialogs? If there are rules governing data transfer from the dialog widgets to a data structure, by whom and where does the NotifyChanged get called? Would a wxWizard be treated like a series of dialog boxes with a rule set for each page? So many questions... :) Regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-18 10:37:51
|
yrs90 wrote: > Very helpful explanations. And very helpful questions for me too. I'll certainly go back to this email exchange when I continue writing the documentation for the library. > I am having trouble with using an enum as the data type in a property. I > get an unresolved external symbol. How do I declare the enum? > BEGIN_ADAPTER_ENUM is an empty macro. Not implemented yet. Currently you must treat enums as ints when it comes to the library. The idea behind BEGIN_ADAPTER_ENUM was to allow to provide the list of enums and also a list of strings representing the enums. > I want to try the following rule. I hope to enable a menu entry only if my > state is equal to idle which is an enum value. > > class myClass > { > enum State m_state; > } > BEGIN_ADAPTER(myClass) > PROP(m_state) > END_ADAPTER() > > RULE("MyMenuXRCID.Enable", make_expr<enum State>(m_state) == make_const<enum > State>(IDLE)) > > Is this valid? The rule is perfectly valid, except that you must currently use ints. So RULE("My...", make_expr<int>(m_state)==make_const<int>(IDLE)) Note that you are creating a rule that directly references m_state. This is okay, but limits the possibilities you have with rules. The rule above binds the .Enable property to the variable m_state, which must exist at compile time. Alternatively you could write RULE("My...", make_expr<int>("m_state")==(int)IDLE)) Two things: a) make_const<int> is not neccessary, because after make_expr<int> at the beginning of the == expression, the compiler should be able to deduce the type automatically. See lwbase\src\unittest\constraintstests.cpp for more examples. b) By using "m_state" rather than m_state you pass in the name instead of the reference. The difference is similar to the difference between static and dynamic linking. Passing m_state is like static linking. The name is resolved at compile time. Passing "m_state" is dynamic linking. The name is looked up at runtime and searched for in the data collection you pass to RapidUI with AddData or operator <<. The later allows writing reusable rules. The only problem you probably will run into in the example above is the fact that you are trying to enable/disable a menu item. Other than windows, menu items are created on the fly when they are needed and are not really windows. I am not sure if they can be accessed by the RapidUI mechanism. Or, hm, in fact, I am almost certain they cannot. The name lookup works as follows: for all windows you pass to RapidUI with AddWindow if window.name == name return found for all children of window (recursively) if children.name == name return found But since menu items are not children of windows they will not be found. This might be added, but my design vision aims at a different direction. I would like to create action items, like delphi has them. An action can be enabled and disabled and be triggered. Action *elements* are elements that can trigger an action. Menu items are action elements, as are buttons. RapidUI will bind action elements to actions and automatically enable/disable all action elements depending on the enabled state of the action itself. So a listbox might expose an Add and a Delete action. Several buttons, menu elements etc... will be bound to these actions. And several rules govern the enabled/disabled state of the action. Summary: For menu items I would stick with the traditional "OnUpdate" method for now. For all other elements (buttons, textboxes etc...), creating a Enable rule works fine. Regards Hajo |
From: yrs90 <yr...@ya...> - 2005-04-18 10:14:34
|
Very helpful explanations. I am having trouble with using an enum as the data type in a property. I get an unresolved external symbol. How do I declare the enum? BEGIN_ADAPTER_ENUM is an empty macro. I want to try the following rule. I hope to enable a menu entry only if my state is equal to idle which is an enum value. class myClass { enum State m_state; } BEGIN_ADAPTER(myClass) PROP(m_state) END_ADAPTER() RULE("MyMenuXRCID.Enable", make_expr<enum State>(m_state) == make_const<enum State>(IDLE)) Is this valid? Thanks, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-18 09:52:30
|
Hi > Are there any provisions for allowing user code to access the RapidUI lookup > mechanism? If I want to create a general event handler for all lists, it > would be nice to retrieve the Items property for the list that generated the > event. I forgot to say: have a look at wxTextCtrlAdapter. The adapter demonstrates what you are trying to do. wxTextCtrlAdapter extends the wxTextCtrl so that it accepts a double. When it contains a double, the adapter will copy the value back from the text control to the double when the focus is lost. The KillFocus event accesses the "Double" property of the underlying TextControl, much as you plan with the "Items" property. In fact, your generic handler should probably be part of your wxListCtrlAdapter. That way you don't even have to worry about attaching/detaching it. Best regards Hajo |
From: Hajo K. <mai...@ha...> - 2005-04-18 09:44:26
|
yrs90 wrote: > Are there any provisions for allowing user code to access the RapidUI lookup > mechanism? That is not neccessary, because your code passes all the objects to RapidUI in the first place, so you already have access to the objects directly. > If I want to create a general event handler for all lists, it > would be nice to retrieve the Items property for the list that generated the > event. You can do that. Its extremely easy. wxListCtrl *m_ctrl; aggregate ag(*m_ctrl); accessor the_items=ag["Items"]; The common denomiator between RapidUI and your code are the accessors. They are the basis for everything! Just as RapidUI uses accessors to access your data, so can you. The above is equivalent to saying m_ctrl->Items, only that there is no C++ property Items of course. And in addition to that, Items exists only in the Lit Window Library enhanced version of wxListCtrl. But "Items" is available to C++ through the data adapters. aggregate ag(*m_ctrl) builds a new aggregate adapter from the list control. ag["Items"] returns an accessor to the "Items" property of the aggregate if there is one or throws an exception if there is no such property. Have a look at the aggregate documentation for other useful functions, such as iterators, find etc... So with accessor the_items=ag["Items"]; you now have an accessor for the items property for the list that generated the event. You can now, for example, container the_items_container=the_items.get_container(); get a container adapter, which is valid if "Items" is indeed a container, and iterate over all elements or insert and delete elements in the container. Note that this is not a copy of the container but a direct reference to the original container you passed to the Items property. > Eventually I guess the rules mechanism will be sufficient. But for now I > have to insert or delete manually. Right now I am referencing the > underlying data directly in a fixed manner. Example: Let this be the underlying data: vector<MyStruct> my_data; You pass the vector to your control with this rule: RULE("MyList.Items", make_expr<accessor>("my_data")) If you modify my_data in any way you must call NotifyChanged(my_data) to trigger rule evaluation and update the control. Now instead of directly referencing my_data in your handler, if you follow the aggregate approach above: void MyHandler::OnSomething(wxEvent &evt) { wxListCtrl *the_list=wxDynamicCast(evt.GetWindow(), wxListCtrl); aggregate ag(*the_list); // get an aggregate accessor ac(ag["Items"]); // get an accessor for "Items" container c(ac.get_container()); // get a container for "Items" // this ASSERT will evaluate to true in your case because // the container you obtained in this manner references the // same underlying object as make_container(my_data) wxASSERT(c.is_alias(make_container(my_data))); } make_aggregate... makes the transition between plain C++ runtime and aggregates/RapidUI properties. The reverse is also possible. accessor ac(ag["Items"]); typed_accessor<vector<MyData> > direct_ac=dynamic_cast_accessor<vector<MyData> >; Now you are casting the accessor back to the real thing, a pointer to vector<MyData>. typed_accessors are access objects you can use when the type is known at compile time. Use them to access the underlying object directly like in the following example: vector<MyData> *my_ptr=direct_ac.get_ptr(); Now you have a typed, C++ pointer to the data. NOTE: get_ptr() can return 0! Not all typed_accessors can return a pointer. If the accessor points to a property that is not a member, but a get/set method pair, there is no object for which a pointer could be returned. So get_ptr() returns 0. But you can use typed_accessor::get and typed_accessor::set to still access the underlying object. Hope this helped. Hajo |
From: yrs90 <yr...@ya...> - 2005-04-18 09:24:12
|
Hi, Are there any provisions for allowing user code to access the RapidUI lookup mechanism? If I want to create a general event handler for all lists, it would be nice to retrieve the Items property for the list that generated the event. Eventually I guess the rules mechanism will be sufficient. But for now I have to insert or delete manually. Right now I am referencing the underlying data directly in a fixed manner. Thanks, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-18 09:03:20
|
Hi > I notice that PROP_R_FUNC is commented out, which also breaks PROP_Get. I > also have had problems getting PROP_Set or PROP_W_FUNC to compile without > errors. What are the plans with these? these will become read-only or write-only adapters eventually. When I get around to fixing them. Currently, if you want to have a read only property you still have to write a Set method which is nonsense of course. > Are there any other PROP_* macros that are used in user ADAPTER > declarations? PROP(member) for member variables PROP_I(inherited) for inheritance PROP_CSTR(char*) for char* member variables although using wxString and string is really preferred. I included PROP_CSTR(nnn) only for my current odbc work to be able to bind to a VARCHAR column of a database using adapters. and for really advanced use: PROP_CO(coobject) for co-objects Regards Hajo |
From: Joel S. <joe...@ym...> - 2005-04-18 08:50:33
|
(Hmmm, second try. I accidentally sent this to mailinglists account at first.) Thanks for the explanation. Rather than direct member access, I see that functional access is possible if declared in the ADAPTER. Actually you alluded to this in your RULE explanation. I've been using the get/set function declarations: PROP_GetSet(type, name) PROP_getset(type, name) PROP_get_set_(type, name) In a RULE, the function is referred to as 'name' and I guess that the l-value or r-value determines whether the framework calls the get or set function. This seems to work well. For case where the names don't start with a get/set, the read and write functions can be manually entered using PROP_RW_FUNC. I notice that PROP_R_FUNC is commented out, which also breaks PROP_Get. I also have had problems getting PROP_Set or PROP_W_FUNC to compile without errors. What are the plans with these? Are there any other PROP_* macros that are used in user ADAPTER declarations? Thanks, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-18 08:12:07
|
Hi all, for future of the lit window library I almost see no way around using the boost library (www.boost.org). There are a couple of reasons for this: + boost contains a small and very good parser which is needed for the RapidUI expression language. This will replace the clumsy make_expr<type> and make_const<type> syntax and allow very clean and readable rules such as wnd::mainwindow.caption = "Document: " + data::mycurrentwindow.document.filename + boost contains many useful macros that hide the quirks and idiosyncrasies of the different compilers. This would make life a lot easier for me to expand support for other compilers. + boost comes with a couple of very useful pointer classes: smart pointers, scoped pointers etc... + boost has a easy to use and powerful c++ signal implementation The downside of this is that the lit window library then depends on yet another library which makes installation a bit more complicated. My questions: ? Do you know the boost library? ? Do you think downloading and installing another library is a major obstacle? ? Should a Lit Window Library distribution include boost, even if it will increase in size by a couple of MB. ? Should a Lit Window Library distribution include only those parts of boost that it needs rather than the entire boost library? ? Should a binary - prebuild version for the Lit Window Library - including Boost binaries - be available? If so, for what compilers? Finally I'd like to phase out support for MSVC version 6. The Lit Window Library requires templates and it is getting increasingly difficult to work around the MSVC 6 limitations. BTW, if you haven't heard of the boost library before, I think you really should have a look at it. It is an excellent library and don't be put off by the very abstract and highly technical look of it. Best regards and looking forward to your comments Hajo |
From: Hajo K. <mai...@ha...> - 2005-04-18 07:54:23
|
Hi, > Could you explain the rules for making a RULE? rules generally have the form name = expression and are evaluated by the constraint solver in the lit window base library. The name is the name of an entity passed to the RapidUI mediator object. There are two namespaces, windows and data, that can be explicitly specified with wnd:: or data::. If no namespace is given, the wnd:: namespace is searched first, then the data namespace:: The name search is recursively. If you specify "m_widget" and m_widget is a child of a child of a child of a child of a window you have passed to the RapidUI mediator object, it will be found. Same applies for data objects. Search is always top-down. Entities can have properties, similar to C++ class members. In fact in most cases there will be a 1:1 relationship between the class member and a property. Frame.Caption for example is wxFrame::Get/SetCaption. In some cases the original widget has been enhanced and has properties for which no corresponding C++ function exists. This is done through the Co-Object mechanism we already wrote about. Example:: ListBox.Current contains a reference to the currently object (!) of the list box. Not to be confused with CurrentSelection, which contains the selection index. Properties can be dereferenced as well, just like C++ members. So MyListBox.Current.m_option is a valid name and references the "m_option" property (member variable or get/set functions) of the currently selected object. The most basic rule is a = b This is a one-way rule meaning that when b changes, a will be updated but not vice versa. If you want a two-way rule, you must specify a = b b = a The macro TWOWAY(a,b) does this. To bind a boolean variable "m_option" to a checkbox "mycheckbox", write mycheckbox = m_option and m_option = mycheckbox > The rule seems to consist of strings with a reference to a UI element on the Not neccessarily UI elements, but the window namespace is searched first. But it is possible to use a data entity on the left side as well. > left hand side and some type of data on the right hand side. The left hand > expression consists of a XRC name followed by a .{Name} for which there > exists functions names Get{Name}() and Set{Name}(). There are some RapidUI > defined {Name}s like Current, Items, Column, Title and some others. Most of > the {Name}s seem defined within RapidUI but sometimes it seems like {Name}s > for which there exists a Get{Name} and Set{Name} in the wxWidgets class is > also acceptable. The idea is that there should be a corresponding RapidUI property for every wxWidgets GetName/SetName or member variable. > The right-hand side seems to be a either a constant or a specific data > member. All of the examples use make_const or make_expr to construct the Not neccessarily data member. You can use window properties as well. > data. The data type used for make_const or make_expr on the right hand side > must be equal to the parameter data type for the left-hand side's Set{Name} > function. Correct. The reason behind make_expr and make_const is to let the C++ compiler parse and build the expression tree that is then evaluated at runtime. This will later be done with a parser. Until then make_const<type>(constant_value) must be used when you want to use a constant in the right hand expression. make_expr<type>("name") must be used when you want to access a property - either in the window or in the data namespace - make_expr<wxString>("MyObject.FileName") returns the (fictional) "FileName" property of an object named "MyObject". The expression make_const<wxString>("Document: ") + make_expr<wxString>("MyObject.FileName") will return something like "Document: myfile.txt". The rule "FrameWindow.Caption" = make_const<wxString>(... ) will show the "Document: ..." string in the titlebar. And will always keep the titlebar in sync with the current filename. The <type> specification in make_const/make_expr is neccessary only if the compiler cannot automatically determine the type of the parameter. > is selected, then a new element. Perhaps this means that .Current is only a > right-hand expression name. No. Current is a property like any other property and can be used on the left or right side. > I have also observed that in addition to specific classes, make_expr can > cast to an accessor. Not being very familiar with stl, I'm not sure if > there are any restrictions on what can be cast as an accessor. (Are there > other special casts? Can one cast to an aggregate too?) accessors have nothing to do with the STL. They are a fundamental part of the litwindow library and are documented there. Basically an accessor is a pointer with runtime type information. > So for lists, it is necessary to know that the predefined 'Items' {Name} > exists and is an accessor. Then it follows that the Yes. What is still missing is a class-like documentation of the properties of a class. The Lit Window libary introduces a new view on the usual widgets. The C++ documentation contains class wxListBox { void SetSelection(int i); int GetSelection() const; }; The RapidUI object documentation would read class wxListBox: property(int) Selection property(accessor) Items Contains the list of items shown in the list box. If Items is a container, all elements of the container will be shown. If Items is an aggregate, all elements of the aggregate will be shown. If Items is neither, the listbox will contain only one element: the "AsString" representation of the element. property(accessor) Current Contains the currently selected object. > make_expr<accessor>("some_rapid_ui_data_member"), where > "some_rapid_ui_data_member" could be a collection or an aggregate depending > on what was expected. Yes. > Is this correct so far? There are suggestions that the syntax will be > extended further. For instance, the .Delete that I've seen on the left hand > side doesn't make sense expanding to GetDelete, SetDelete function calls. Yes. Delete will be an action. I haven't designed actions yet, but Delete will probably expand to a DeleteCurrent call or something. Hajo |
From: Hajo K. <mai...@ha...> - 2005-04-18 06:35:09
|
Sorry to flood your inbox... I found that NotifyChanged(g_data.mycollection); causes the invalidation I was seeking. Unless my view is too narrow, I have realized that the co-object to object link was entirely through the eventhandler. I tried creating a DeleteItem eventhandler in wxList_objects that removes the element from the collection. I am still a little uncomfortable with the idea that the framework should be responsible for deleting underlying data. Is this would you envision? I wanted the deletion to cause a general reevaluation of the rules. I'd prefer to have it called in the framework eventhandler after the underlying data is removed rather than immediately after deleting from the widget so as to avoid any race condition. I tried to call NotifyChanged from within the deleted items event handler, but calling it on m_items or m_items.get_container() or even GetCurrent() isn't enough to cause all g_data.mycollection rules to be reevaluated. Again, feel free to ignore this until you are feeling better. Best regards, |
From: Hajo K. <mai...@ha...> - 2005-04-18 06:27:05
|
Hi Hajo, What triggers the rules to be evaluated? If I delete an item do I delete it from both the collection and the view? Or is there a secret to making it propagate after just deleting it from one of the locations? If I add support for say DeleteCurrent() (deletes element of collection) and DeleteSelection() (deletes element from window) in wxList_objects, how can I access the Adapter for a window from my user code so I can call these functions? Best regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: <ja...@ab...> - 2005-04-17 19:56:45
|
Hi Hajo, Could you explain the rules for making a RULE? I haven't dug through the code too much here. I've just been puzzling over the various examples. The rule seems to consist of strings with a reference to a UI element on the left hand side and some type of data on the right hand side. The left hand expression consists of a XRC name followed by a .{Name} for which there exists functions names Get{Name}() and Set{Name}(). There are some RapidUI defined {Name}s like Current, Items, Column, Title and some others. Most of the {Name}s seem defined within RapidUI but sometimes it seems like {Name}s for which there exists a Get{Name} and Set{Name} in the wxWidgets class is also acceptable. The right-hand side seems to be a either a constant or a specific data member. All of the examples use make_const or make_expr to construct the data. The data type used for make_const or make_expr on the right hand side must be equal to the parameter data type for the left-hand side's Set{Name} function. make_const() is used when no data conversion is required. The parameter is an object instantiation. (?) make_const<class>() is used when a new instance should be created with the make_const parameter as the parameter for the new object. make_expr<>() seems to expect a data member as a parameter. RapidUI searches the Data structure that it received during initialization for the member named. Aggregates can be accessed via the .operator, and collections can be accessed via the special .Current member that RapidUI translates into the data member that corresponds to the currently selected UI element or if none is selected, then a new element. Perhaps this means that .Current is only a right-hand expression name. I have also observed that in addition to specific classes, make_expr can cast to an accessor. Not being very familiar with stl, I'm not sure if there are any restrictions on what can be cast as an accessor. (Are there other special casts? Can one cast to an aggregate too?) So for lists, it is necessary to know that the predefined 'Items' {Name} exists and is an accessor. Then it follows that the make_expr<accessor>("some_rapid_ui_data_member"), where "some_rapid_ui_data_member" could be a collection or an aggregate depending on what was expected. Is this correct so far? There are suggestions that the syntax will be extended further. For instance, the .Delete that I've seen on the left hand side doesn't make sense expanding to GetDelete, SetDelete function calls. Best regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-17 17:55:57
|
yrs90 wrote: > I have left the existing interface the same, making minimal modifications > and additions to handle multi-columns. I am not yet comfortable with how > much of the interface can/should be modified. > > I have no idea either. We'll just have to wait and see. > flawed. I thought it would be nice to pass an aggregate to a function, say > SetColumns(), and have the columns created from the struct elements. Take a look at wxTextCtrl. It accepts an accessor as a parameter, much like you are planning to do with SetColumns. As for the internal names I would suggest a new properties: ColumnTitles, perhaps even a combination of title-member variable name. Example: ListCtrl.ColumnTitles:="m_id=ID, m_name=Name" where the string consists of membername=columnheader pairs separated by a , > I made enough progress that my ListCtrl without distinct columns is being > populated. This was a straightforward modeled after wxListBox. When I tried > to extend it to include columns I encountered two problems. First, I tried > to add two functions, SetColumns and GetColumns. > > Class wxListCtrlAdapter { > ... > void SetColumns(const accessor &newValue); > const accessor &GetColumns() const; > ... > } > > BEGIN_ADAPTER_NO_COPY(wxListCtrlAdapter) > PROP_I(lwListAdapterBase) > PROP_GetSet(accessor, Columns) > END_ADAPTER() > > However, when I create a rule: > > RULE("m_IPListWindow.Columns", make_expr<accessor>("m_targetlist")) > > I get an error thrown stating that the "GetAccessor couldn't find a variable > ' m_IPListWindow.Columns'" > > Yes, you overlooked one important point. You have created a data adapter for the wxListCtrlAdapter, but I guess that you haven't created a data adapter for wxListCtrl itself. I call the concept behind this a 'co-object'. Essentially you are trying to extend wxListCtrl, but without modifying the wxListCtrl sources. So I introduced a wxSomethingAdapter class that contains the extensions. The following code binds this adapter class to the original class: BEGIN_ADAPTER_NO_COPY(wxListCtrl) PROP_CO(wxListCtrlAdapter) PROP_I(wxControl) END_ADAPTER() It is almost as if you are adding a new superclass wxListCtrlAdapter to wxListCtrl. While the C++ inheritance looks like this: class wxListCtrl:public wxControl the data adapter inheritance is this: class wxListCtrl:public wxListCtrlAdapter, public wxControl In other words, when a wxListCtrl object is accessed through a data adapter, it 'inherites' not only wxControl properties but also wxListCtrlAdapter properties. Have a look at base_objects.cpp and how this is done with the wxListBoxAdapter. In order to make this work, the co-object mechanism needs a way to create the co-object and attach it to the original object. This is done through template overloading, which tells the mechanism to call 'new wxListCtrlAdapter' the first time the PROP_CO(wxListCtrlAdapter) is seen. The pointer to this object must then be stored someplace. I currently attach the new adapter object as a handler to the original wxWidgets object and destroy the object when the wxEVT_DESTROY_WINDOW event comes my way. Just take a close look at the connection between BEGIN_ADAPTER_NO_COPY(wxListBox) and BEGIN_ADAPTER_NO_COPY(wxListBoxAdapter). The template overloading function is somewere there. > My second problem, is one related to my original design. I am not sure how > to initialize the columns if I don't have an instance of the aggregate to > pass. Perhaps, though, there is a better way to pass the column names and > associations between the columns and the aggregate contents. Any > suggestions on this front? > > You could create the ColumnHeader property I described above if you want to create the columns before you actually pass in a container. But keep in mind that the columns of the control might change over time. So I'd create them only after I actually have a container and change them everytime the element type of the container changes. Regards, and thanks for your work. It is very appreciated. Hajo |
From: Hajo K. <mai...@ha...> - 2005-04-17 17:42:07
|
Hi Hajo, Thanks for the prompt reply. I spent some time today trying to add ListCtrl support. I was using the 0.3 code but now I've updated to CVS head. I have left the existing interface the same, making minimal modifications and additions to handle multi-columns. I am not yet comfortable with how much of the interface can/should be modified. Originally, I was also trying to leave FillList() unchanged, but I finally realized that since it calls ClearList internally (destroying any columns), that I had to modify FillList. So I've made appropriate modifications in my wxListCtrlAdapter class. My initial strategy seemed elegant but may be flawed. I thought it would be nice to pass an aggregate to a function, say SetColumns(), and have the columns created from the struct elements. Then one would merely pass a container of that aggregate and the elements of each aggregate would be placed into the column associated the respective structure element. The problem, of course, is that the internal names may not be appropriate for use in the UI. I made enough progress that my ListCtrl without distinct columns is being populated. This was a straightforward modeled after wxListBox. When I tried to extend it to include columns I encountered two problems. First, I tried to add two functions, SetColumns and GetColumns. Class wxListCtrlAdapter { ... void SetColumns(const accessor &newValue); const accessor &GetColumns() const; ... } BEGIN_ADAPTER_NO_COPY(wxListCtrlAdapter) PROP_I(lwListAdapterBase) PROP_GetSet(accessor, Columns) END_ADAPTER() However, when I create a rule: RULE("m_IPListWindow.Columns", make_expr<accessor>("m_targetlist")) I get an error thrown stating that the "GetAccessor couldn't find a variable ' m_IPListWindow.Columns'" When I compare to the Items and Column property, I only find a PROP_GetSet declaration. I must be missing something obvious. How do I create a new property? My second problem, is one related to my original design. I am not sure how to initialize the columns if I don't have an instance of the aggregate to pass. Perhaps, though, there is a better way to pass the column names and associations between the columns and the aggregate contents. Any suggestions on this front? Thanks, Joel |
From: Hajo K. <mai...@ha...> - 2005-04-17 17:31:06
|
Hi Joel, thanks for your comments. I am using the library myself for contract-based work I am doing for a client. It greatly reduces the time for anything related to data exchange between widgets and struct/class. Adding new properties to a settings dialog for example is very easy and works well. My project also contains a couple of interdependencies - buttons that need to be enabled only in certain situations, dynamic window captions etc... and I am using the library for that. My approach is currently to use a mix of both. I use the library whenever possible and fall back to more traditional methods elsewhere, simply because I don't have the time to enhance the library at the moment. But the development is still very much alive, if quiet. Now for your questions: > I understand that the RapidUI code will be streamlined in the future, but as > the entire package stands now is it well-behaved enough to use in a real > project (even if it might require some immature syntax)? > > I would say yes. I use it in a commercial project. And if you need help with the syntax, just ask. I am happy to help out and also discuss any ideas with you. > My UI design requires a multicolumn wxListCtrl. I was looking at > base_objects.* in lwwx and started to add wxListCtrl by modeling changes > after the support for wxListBox. Am I duplicating existing efforts? > > No. I haven't yet startet with wxListCtrl. > My first thought is that the number of columns in the listctrl should be > determined by the number of elements in the associated structure, but I am > not sure if this fits your conception. Also, I have not dug deep enough to > see how to walk though the elements of a structure. > > It fits perfectly. Additionally I'd also add a filter property, which would simply be string containing a ',' separated list of members (names) to omit. > I have been wavering between wanting to jump in using the Lit Window library > and deciding I should do it the traditional approach one more time. > > Like I said, you could do both. Use the library if possible, fall back to the traditional approach elsewhere. Works fine for me. And if you contribute some of your work - wxListCtrl for example - you'll ensure that the library will continue to grow. My current projects are a graphical user interface for a custom made hardware device and Version 2 of my bug tracking tool 'BugLister'. BTW, are you using the version from the installer or did you check out the latest sources from sourceforge? The sources haven't changed much since last december, but contain a few bugfixes and documentation enhancements. Best regards Hajo |
From: Hajo K. <mai...@ha...> - 2005-04-17 17:18:24
|
Hi all, I am forwarding this email conversation between Joel and me to the list in hope it is useful for the others here. Best regards Hajo -------------------- Dear Hajo, You have a very exciting approach started. I hope to see it develop further. I am in the early stages of understanding how to design for Lit Window use. I have compiled the software using the free MSVS Toolkit. I understand that the RapidUI code will be streamlined in the future, but as the entire package stands now is it well-behaved enough to use in a real project (even if it might require some immature syntax)? My UI design requires a multicolumn wxListCtrl. I was looking at base_objects.* in lwwx and started to add wxListCtrl by modeling changes after the support for wxListBox. Am I duplicating existing efforts? My first thought is that the number of columns in the listctrl should be determined by the number of elements in the associated structure, but I am not sure if this fits your conception. Also, I have not dug deep enough to see how to walk though the elements of a structure. I have been wavering between wanting to jump in using the Lit Window library and deciding I should do it the traditional approach one more time. Any suggestions? Best regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |