I would be curious about VisualWorks programmers' reactions to the following: http://objology.blogspot.com/2011/10/sounding-out-view-tree.html -- Travis Griggs Objologist Simplicity is the Ultimate Sophistication -- Leonardo da Vinci _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
Although the code examples look nice, IMO it's making things even more complicated. Why not just use a simple IdentityDictionary (or an API that fakes it), nested by sub builders? I am all for a straight API with a minimum set of methods that fit 90% of all common use cases, for example self widgetAt: #id do: [ :w | ... ]. self widgetAt: #id in: #subcanvas do: [ :w | ... ]. A powerful query system seems smart, but is really not needed here. Logic like "all widgets in the lower half of the window" is pretty useless. A UI is designed for a specific purpose and the application knows about that design in every detail. Identifying widgets by name is just fine and sufficient. If dynamically separating widgets in groups and collections was of any importance, it would have been incorporated into the application and UI from the beginning (composites, subcanvases, collections of widget names, etc): #( input1 input2 input3 ) do: [ :id | self widgetAt: id do: [ :w | ... ]]. Please keep it simple! Andre _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Travis Griggs-4
Travis,
Well, there certainly is a need to tidy these patterns up. Did you consider XPath queries as a model, as an alternative to jQuery? They have a nice, generalized, declarative syntax which could be applied to a UI component tree. Without putting nearly as much thought into it as you have, the following mapping springs to mind: / = topComponent //* = any component //TypeName = any component of type TypeName */* = the parent-child relationship (we could ignore wrappers on our way down) TypeName[@id='Name'] = a specific type and its ID matching 'Name' (i.e. those widget names in the UI editor) *[@id='Name'] = any type whose ID matches 'Name' ../ = the upward path step in the tree ./ = the path starting with "me" and so on. Here is a silly example: '../*[@id="CancelButton"]' That would get you the widget named "CancelButton" which is your sibling in the tree. This way, the search criterion is very easy to read in your source code (though it has the slight disadvantage of being a string which the search engine must first parse into objects). You can either search from the top of the view tree, or from the current component (sender). If you observe that the / and @ symbols are syntactic sugar for the "axes" which include "parent", "sibling", "attribute" and so on, which could also be explicitly used in the XPath syntax, you would get pretty much complete expressive control over which items (in relation to the sender) you are trying to locate. Regards. Gregory Bourassa On 10/06/11 23:39, Travis Griggs wrote: > I would be curious about VisualWorks programmers' reactions to the > following: > > http://objology.blogspot.com/2011/10/sounding-out-view-tree.html > > -- > Travis Griggs > Objologist > Simplicity is the Ultimate Sophistication -- Leonardo da Vinci > ----------------------------------------- Key Technology, Inc. Disclaimer Notice - The information and attachment(s) contained in this communication are intended for the addressee only, and may be confidential and/or legally privileged. If you have received this communication in error, please contact the sender immediately, and delete this communication from any computer or network system. Any interception, review, printing, copying, re-transmission, dissemination, or other use of, or taking of any action upon this information by persons or entities other than the intended recipient is strictly prohibited by law and may subject them to criminal or civil liability. Key Technology, Inc. is not liable for the improper and/or incomplete transmission of the information contained in this communication or for any delay in its receipt. _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Travis Griggs-4
Attachment componentNamed.st contains a few methods I wrote to find a widget by name in the view tree: #name -> SpecWrapper, VisualPart #componentNamed: -> SpecWrapper, Wrapper, CompositePart, VisualComponent, ScheduledWindow, ApplicationModel This allows my appModel to simply do: self componentNamed: #widgetID This particular implementation has the weakness of answering only the first encountered component with the specified name (see CompositePart>>componentNamed:). An improvement might add an alternate selector to answer all components with that name (#componentsNamed:). But the point is, I don't want to have to create a Query directly in my client code and then start it searching. I'd much rather just send a message to the appModel. Behind the scenes, if aQuery is constructed, I don't care so much. But I've shown that a few methods added to the existing view classes can handle the traversal of the tree without a separate traversal object. [hidden email] From: Travis Griggs <[hidden email]> To: VWNC NC <[hidden email]> Sent: Fri, October 7, 2011 1:39:18 AM Subject: [vwnc] Sounding Out the View Tree I would be curious about VisualWorks programmers' reactions to the following:
http://objology.blogspot.com/2011/10/sounding-out-view-tree.html -- Travis Griggs Objologist Simplicity is the Ultimate Sophistication -- Leonardo da Vinci _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc componentNamed.st (3K) Download Attachment |
On Oct 7, 2011, at 8:30 AM, Dave Stevenson wrote:
If you were trying to approximate the jQuery/CSS notion of Id with the Spec name, then matching the first one is the right thing to do. This is a nice utility Dave, 8 extension methods. You're currently going to miss PartPort children and SimpleTextSelectionStatus, but if you change your code to use childrenDo:, that'll solve that problem for you. I think we have different goals, with some overlap. You are interested in finding methods by one means only: id. I wanted to be able to find them by principles similar to what is found in CSS, which include id. If I follow the approach you did, I'll add another 8 methods to implement componentsTyped:. And if I want to do tagging (css classes), which is very useful in jQuery/Web UIs, I'll add another 8 methods to implement componentsTagged:. And maybe it'd be better to just add a componentsSatisfying: aBlock do: bBlock. I can pivot those 24 methods to use that, and end up with 8 methods for the componentsSatisfying:do: and then helper sugar in 3x3 spots, which gets me back to 17. I wanted to use the same mechanism to go up the tree as well though. Not just down. So now you get to multiply that 17 x 2 if you want to have similar named API. And I wanted to be able to stack them. I also wanted to be able to do more interesting matches. I liked that I could generally do batch sorts of things like (self ? #(#cancel #maybe #ok)) enable And I didn't want to be tied to SpecWrapper. Part of the whole game here, is that we'd like to figure out a way to decouple the process of assembling UIs from specs from their actual realization. Maybe there's another way to put what I was/am after here. I've done what you've done here. I've done it lots of times. We've tried to add some useful generic patterns (like childrenDo:) to the base. But the basic problem is that every single time I have a new and interesting way that I want to interact with the view tree, I have to write a method that looks awfully familiar. If I'm going down, it looks something like doSomethingInteresting self doTheInterestingThingForMe. self childrenDo: [:each | each doSomethingInteresting] And if I'm going upwards it looks something like doSomethingInteresting self doTheInterestingThingForMe. self parent ifNotNil: #doSomethingInteresting It's great and all, but it gets old. After 20 years, I've done enough of them. Want to add a #layout method so that normal views can get at their layout? You know the drill. Want to have a #bottomComponent, or a #topComponent, or #isOpen, or #flushCoordinateCache, or.. you get the picture. For some of these you really want to follow the pattern, because subclasses can do interesting things. But for a lot of them, you're just making another recursive tree traverser by yet another name. What I was really after was an algebra for generalized access to the tree. I find it cool in a meta sort of way, that I don't need topComponent any more, I can just write (self ?? [true]) last It should have about the same efficiency as well (or close) . Sure #topComponent is more clear, and maybe I'd write a helper just for that. But we can do just about any sort of tree processing we want without having to repeat the mechanics of tree traversal all over the place. And you could still write componentNamed: in the 3 top level places as componentNamed: anID ^(self ? #id) first -- Travis Griggs Objologist Light travels faster than sound. This is why some people appear bright until you hear them speak... _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Travis Griggs-4
>This allows my appModel to simply do:
> self componentNamed: #widgetID Sounds like it is done in VS(E), VA and some of the javascript libraries. Honestly, I always liked this approach way better than anything else. You look for a GUI-Element in the tree and do not need to know where it is. If you have a large tree of GUI components, you can always expose often-needed ones directly via getters. _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Travis Griggs-4
Hello Travis.
Just my 2cents... I like your Query approach very much. I would call the package WidgetQuery instead of WidgetPath. I dislike #? and #?? shorthands and the rest of the suggested "syntactic sugar". I prefer readability over reduction in character count. Furthemore I would constantly confuse the meaning of the two question mark selectors. I would call them #childQuery: and #parentQuery: instead. But when I look at the sample examples on your blog post I think the encapsulation is still broken. I have to know the Wrapper internals to get the job done: * show/hide? => SpecWrapper * layout? => BoundedWrapper * #takeKeyboardFocus? => WidgetWrapper * #invalidate? => BorderDecorator * ... I suggest to introduce "implicit tag names" for all these capabilities (such as #layout). With these the programmer no longer has to know anything about the internal Wrapper hierarchy. This would enable you to change the Wrapper implementation (like eliminating most of the wrappers ;-) )... Ladislav Lenart ----- PŮVODNÍ ZPRÁVA ----- Od: "Travis Griggs" <[hidden email]> Komu: "VWNC NC" <[hidden email]> Předmět: Re: [vwnc] Sounding Out the View Tree Datum: 8.10.2011 - 1:55:30 > On Oct 7, 2011, at 8:30 AM, Dave Stevenson wrote: > > > Attachment componentNamed.st contains a few > > methods I wrote to find a widget by name in the > > view tree: > > > #name -> SpecWrapper, VisualPart > > #componentNamed: -> SpecWrapper, Wrapper, > > CompositePart, VisualComponent, > > ScheduledWindow, ApplicationModel > > > > > This allows my appModel to simply do: > > self componentNamed: #widgetID > > > > This particular implementation has the weakness > > of answering only the first encountered > > component with the specified name (see > > CompositePart>>componentNamed:). An improvement > > might add an alternate selector to answer all > > components with that name (#componentsNamed:). > > > > > But the point is, I don't want to have to create > > a Query directly in my client code and then > > start it searching. I'd much rather just send a > > message to the appModel. Behind the scenes, if > > aQuery is constructed, I don't care so much. But > > I've shown that a few methods added to the > > existing view classes can handle the traversal > > of the tree without a separate traversal object. > > > If you were trying to approximate the jQuery/CSS > notion of Id with the Spec name, then matching the > first one is the right thing to do. > > This is a nice utility Dave, 8 extension methods. > You're currently going to miss PartPort children > and SimpleTextSelectionStatus, but if you change > your code to use childrenDo:, that'll solve that > problem for you. > > I think we have different goals, with some > overlap. > > You are interested in finding methods by one means > only: id. I wanted to be able to find them by > principles similar to what is found in CSS, which > include id. If I follow the approach you did, I'll > add another 8 methods to implement > componentsTyped:. And if I want to do tagging (css > classes), which is very useful in jQuery/Web UIs, > I'll add another 8 methods to implement > componentsTagged:. And maybe it'd be better to > just add a componentsSatisfying: aBlock do: > bBlock. I can pivot those 24 methods to use that, > and end up with 8 methods for the > componentsSatisfying:do: and then helper sugar in > 3x3 spots, which gets me back to 17. > > I wanted to use the same mechanism to go up the > tree as well though. Not just down. So now you get > to multiply that 17 x 2 if you want to have > similar named API. > > And I wanted to be able to stack them. > > I also wanted to be able to do more interesting > matches. I liked that I could generally do batch > sorts of things like > > (self ? #(#cancel #maybe #ok)) enable > > And I didn't want to be tied to SpecWrapper. Part > of the whole game here, is that we'd like to > figure out a way to decouple the process of > assembling UIs from specs from their actual > realization. > > Maybe there's another way to put what I was/am > after here. I've done what you've done here. I've > done it lots of times. We've tried to add some > useful generic patterns (like childrenDo:) to the > base. But the basic problem is that every single > time I have a new and interesting way that I want > to interact with the view tree, I have to write a > method that looks awfully familiar. If I'm going > down, it looks something like > > doSomethingInteresting > self doTheInterestingThingForMe. > self childrenDo: [:each | each > doSomethingInteresting] > > And if I'm going upwards it looks something like > > doSomethingInteresting > self doTheInterestingThingForMe. > self parent ifNotNil: #doSomethingInteresting > > It's great and all, but it gets old. After 20 > years, I've done enough of them. Want to add a > #layout method so that normal views can get at > their layout? You know the drill. Want to have a > #bottomComponent, or a #topComponent, or #isOpen, > or #flushCoordinateCache, or.. you get the > picture. For some of these you really want to > follow the pattern, because subclasses can do > interesting things. But for a lot of them, you're > just making another recursive tree traverser by > yet another name. > > What I was really after was an algebra for > generalized access to the tree. I find it cool in > a meta sort of way, that I don't need topComponent > any more, I can just write > > (self ?? [true]) last > > It should have about the same efficiency as well > (or close) . > > Sure #topComponent is more clear, and maybe I'd > write a helper just for that. But we can do just > about any sort of tree processing we want without > having to repeat the mechanics of tree traversal > all over the place. > > And you could still write componentNamed: in the 3 > top level places as > > componentNamed: anID > ^(self ? #id) first > > -- > Travis Griggs > Objologist > Light travels faster than sound. This is why some > people appear bright until you hear them speak... > > > > > _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Travis Griggs-4
> componentNamed: anID > ^(self ? #id) first Sorry, but proposals like this frighten the hell out of me, because for me it feels like a move backwards. That's way too much overkill for a GUI framework that is supposed to be simple and straightforward and efficient. Apart from the smart logic behind it (no question, I like it), what is this supposed to be good for in practice? I see the point that tree traversal could be generalized, but I wouldn't do it only for the mere handfull of methods that a GUI needs. I suspect that the reason for so many traversal things already being there is just bad and redundant design. I might be missing something, but according to my current experience, there is only one chain needed in upwards direction for events (input), and one downwards for display. It would certainly be more rewarding to try and eliminate all other needs for traversal by cleaning up the framework, instead of adding to the overall complexity with a general query system. I also doubt that it could be effcient enough for higher framerates (frequent updates). The user interfaces I've done so far are really complex, involving real-time animation in deeply nested scrollable components that display thousands of immediately editable items, dozens of nested tabs, heavy use of dynamical disablement, etc. I don't see where such a query system could have helped me to make things easier or better in any way. Smalltalk is so tempting in that it always suggests us to think about how many cool things we could do instead of forcing us to rethink and improve our immature ad-hoc designs in order to make them work with a strict, clean and minimal framework. Andre _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Travis Griggs-4
Admittedly the wording in my last post was a bit exaggerated. The
point I was trying to make is that it is worth questioning whether we need more "intelligence" to help us deal with the current complexity, or whether is is more desirable to reduce and/or hide that complexity instead. Since a couple years I am using a simple API to control visibility and enablement of widgets. The application model implements the following methods (examples): widgetsDisabled "Answer a Set of widget names that are supposed to be disabled according to the current state of the application." disabled := super widgetsDisabled. self canPaste ifFalse:[ disabled add: #doPaste ]. self canCommit ifFalse:[ disabled add: #OK ]. self selectedProduct isOptedForMaintenance ifFalse:[ disabled addAll: self widgetsForMaintenance ]. ^disabled widgetsHidden "Answer a Set of widget names that are supposed to be hidden according to the current state of the application." hidden := super widgetsHidden. self selectedCustomer isEligibleForUpgrades ifFalse:[ hidden addAll: self widgetsForUpgrade ]. ^hidden updateVisibility "... straightforward code that uses the above to change widget states by #id" Each time the application changes something that affects visibility or enablement, I just send #updateVisibility to the app. The general #updateAll message also invokes this, so everything is always up to date. The advantages of this approach compared to sending messages to individual widgets are: 1. Only a single place is reponsible. 2. Declarative and descriptive code, easy to understand, maintain and extend. 3. Various #canDelete, #canCommit, #canDoWhatever methods are self- explanatory, inheritance-friendly and contain domain-specific code only. 4. No need to deal with Wrapper in customer code. This scheme could be easily enhanced by adding #widgetsContinouslyAccepted, #widgetsWhatever. It should not be too difficult to come up with equivalent hooks hat accept arguments (e.g. layouts). The point I am trying to make is: I strongly doubt there are actually that many use cases that would justify adding a query type of API. It seems more rewarding to me to just identify the most common application-level uses (merely a dozen, I would guess) and provide declarative hooks similar to the above that are easy to understand and maintain. This way Wrapper and its quirks could be kept out of custom code entirely. Andre _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Travis Griggs-4
Ouch, I missed to declare #hidden and #disabled as temporaries in my
examples: widgetsDisabled "Answer a Set of widget names that are supposed to be disabled according to the current state of the application." | disabled | disabled := super widgetsDisabled. self canPaste ifFalse:[ disabled add: #doPaste ]. self canCommit ifFalse:[ disabled add: #OK ]. self selectedProduct isOptedForMaintenance ifFalse:[ disabled addAll: self widgetsForMaintenance ]. ^disabled widgetsHidden "Answer a Set of widget names that are supposed to be hidden according to the current state of the application." | hidden | hidden := super widgetsHidden. self selectedCustomer isEligibleForUpgrades ifFalse:[ hidden addAll: self widgetsForUpgrade ]. ^hidden _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by andre
Actually, let me throw this out there: Menus have a hook for defining an enablement state message. Why not do something similar for the widget set?
On Oct 9, 2011, at 5:01 AM, andre wrote: > Admittedly the wording in my last post was a bit exaggerated. The > point I was trying to make is that it is worth questioning whether we > need more "intelligence" to help us deal with the current complexity, > or whether is is more desirable to reduce and/or hide that complexity > instead. > > Since a couple years I am using a simple API to control visibility and > enablement of widgets. The application model implements the following > methods (examples): > > widgetsDisabled > "Answer a Set of widget names that are supposed to be disabled > according to the current state of the application." > > disabled := super widgetsDisabled. > self canPaste > ifFalse:[ disabled add: #doPaste ]. > self canCommit > ifFalse:[ disabled add: #OK ]. > self selectedProduct isOptedForMaintenance > ifFalse:[ disabled addAll: self widgetsForMaintenance ]. > ^disabled > > widgetsHidden > "Answer a Set of widget names that are supposed to be hidden > according to the current state of the application." > > hidden := super widgetsHidden. > self selectedCustomer isEligibleForUpgrades > ifFalse:[ hidden addAll: self widgetsForUpgrade ]. > ^hidden > > updateVisibility > "... straightforward code that uses the above to change widget states > by #id" > > Each time the application changes something that affects visibility or > enablement, I just send #updateVisibility to the app. The general > #updateAll message also invokes this, so everything is always up to > date. The advantages of this approach compared to sending messages to > individual widgets are: > > 1. Only a single place is reponsible. > 2. Declarative and descriptive code, easy to understand, maintain and > extend. > 3. Various #canDelete, #canCommit, #canDoWhatever methods are self- > explanatory, inheritance-friendly and contain domain-specific code only. > 4. No need to deal with Wrapper in customer code. > > This scheme could be easily enhanced by adding > #widgetsContinouslyAccepted, #widgetsWhatever. It should not be too > difficult to come up with equivalent hooks hat accept arguments (e.g. > layouts). > > The point I am trying to make is: I strongly doubt there are actually > that many use cases that would justify adding a query type of API. It > seems more rewarding to me to just identify the most common > application-level uses (merely a dozen, I would guess) and provide > declarative hooks similar to the above that are easy to understand and > maintain. This way Wrapper and its quirks could be kept out of custom > code entirely. > > Andre > > _______________________________________________ > vwnc mailing list > [hidden email] > http://lists.cs.uiuc.edu/mailman/listinfo/vwnc James Robertson http://www.jarober.com [hidden email] _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
Am 09.10.2011 um 18:12 schrieb James Robertson: > Actually, let me throw this out there: Menus have a hook for > defining an enablement state message. Why not do something similar > for the widget set? It won't work that way round. One widget is usually not tied to a single application state. There may be multiple application states that enabled/disable a widget. To cope with this, we would end up with lots of methods like #isInputAEnabled, #isInputBEnabled and such, each of which query the same application state again and again (which is potentially expensive). The nice thing with the proposed #widgetsDisabled / #widgetsHidden scheme is that the application state needs to be queried only once and all widgets react to it at once. It is also more easy to understand and maintain, because the entire "business logic" of the UI is found in a single method source. I always found this to be very natural and comfortable. It was much different if all state dependencies were cluttered across a large number of widgets. Even more so as this information is hidden in the UI painter's GUI or lengthy specs. Andre _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
The same thing goes for menu items; in the apps I've been involved with, the same state triggers multiple menu item states. The simplest way to deal with that is to give those menu items the same state check method.
On Oct 9, 2011, at 3:46 PM, andre wrote: > > Am 09.10.2011 um 18:12 schrieb James Robertson: > >> Actually, let me throw this out there: Menus have a hook for defining an enablement state message. Why not do something similar for the widget set? > > It won't work that way round. One widget is usually not tied to a single application state. There may be multiple application states that enabled/disable a widget. To cope with this, we would end up with lots of methods like #isInputAEnabled, #isInputBEnabled and such, each of which query the same application state again and again (which is potentially expensive). > > The nice thing with the proposed #widgetsDisabled / #widgetsHidden scheme is that the application state needs to be queried only once and all widgets react to it at once. > > It is also more easy to understand and maintain, because the entire "business logic" of the UI is found in a single method source. I always found this to be very natural and comfortable. > > It was much different if all state dependencies were cluttered across a large number of widgets. Even more so as this information is hidden in the UI painter's GUI or lengthy specs. > > Andre > > James Robertson http://www.jarober.com [hidden email] _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
> The same thing goes for menu items; in the apps I've been involved > with, the same state triggers multiple menu item states. The > simplest way to deal with that is to give those menu items the same > state check method. Sure, in theory that works, but mapping widgets to states (1) is more complex and less maintainable than mapping states to widgets (2): (1) Widget A -> State1 Widget B -> State2 Widget C -> State1, State2, State3 (requires an artificial state checker method) Widget D -> State1, State2 (ditto) Widget E -> State3 (2) State1 -> #( A B C D ) State2 -> #( C D ) State3 -> #( C E ) For basic widgets like a "delete" button, a simple checker like #canDelete is fine. Also most menu items map to a single action only (and usually there is a relatively small number of menu items anyway). It's much different with forms that use a wide variety of widgets that depend on more than one state each. You might perhaps think this is uncommon, but it is not. I have it in almost every UI. The most important point however is that (2) is driven by business logic, telling the UI to behave accordingly, whereas (1) is widget- driven: widgets have to know in advance the state(s) they are tied to. That's asking for trouble. In (1), if the business logic changes, I have to review a single self-explanatory method. In (2) I always end up looking into the UI builder examining individual widgets, adding artificial checker methods, etc. Business-driven declarative code is far superior compared to widget- driven hard wired stuff. Because it's not the widget that knows about my business - it's the app that knows. Dynamic enablement and visibility is not a property of the widget. It belongs to the behavior of the application! I am following this principle since a couple of years now, and it has proven to be very successful for me. Keeping all business logic away from the immediate UI definition really pays. Andre _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by jarober
It's not so easy for widgets, because a widget is displayed all the
time, whereas a menu is displayed on demand. It's thus easy for the menu itself to check whether it should be enabled, just before it is displayed. But a widget needs an application to explicitly tell it when the answer to the enablement message has changed, or then some dependency or event mechanism. > -----Original Message----- > From: [hidden email] [mailto:[hidden email]] On > Behalf Of James Robertson > Sent: 9. lokakuuta 2011 19:12 > To: VWNC NC > Subject: Re: [vwnc] Sounding Out the View Tree > > Actually, let me throw this out there: Menus have a hook for defining > an enablement state message. Why not do something similar for the > widget set? > > On Oct 9, 2011, at 5:01 AM, andre wrote: > > > Admittedly the wording in my last post was a bit exaggerated. The > > point I was trying to make is that it is worth questioning whether > > need more "intelligence" to help us deal with the current complexity, > > or whether is is more desirable to reduce and/or hide that complexity > > instead. > > > > Since a couple years I am using a simple API to control visibility > and > > enablement of widgets. The application model implements the following > > methods (examples): > > > > widgetsDisabled > > "Answer a Set of widget names that are supposed to be > disabled > > according to the current state of the application." > > > > disabled := super widgetsDisabled. > > self canPaste > > ifFalse:[ disabled add: #doPaste ]. > > self canCommit > > ifFalse:[ disabled add: #OK ]. > > self selectedProduct isOptedForMaintenance > > ifFalse:[ disabled addAll: self > widgetsForMaintenance ]. > > ^disabled > > > > widgetsHidden > > "Answer a Set of widget names that are supposed to be hidden > > according to the current state of the application." > > > > hidden := super widgetsHidden. > > self selectedCustomer isEligibleForUpgrades > > ifFalse:[ hidden addAll: self widgetsForUpgrade > ]. > > ^hidden > > > > updateVisibility > > "... straightforward code that uses the above to change > widget states > > by #id" > > > > Each time the application changes something that affects visibility > or > > enablement, I just send #updateVisibility to the app. The general > > #updateAll message also invokes this, so everything is always up to > > date. The advantages of this approach compared to sending messages > > individual widgets are: > > > > 1. Only a single place is reponsible. > > 2. Declarative and descriptive code, easy to understand, maintain and > > extend. > > 3. Various #canDelete, #canCommit, #canDoWhatever methods are self- > > explanatory, inheritance-friendly and contain domain-specific code > only. > > 4. No need to deal with Wrapper in customer code. > > > > This scheme could be easily enhanced by adding > > #widgetsContinouslyAccepted, #widgetsWhatever. It should not be too > > difficult to come up with equivalent hooks hat accept arguments (e.g. > > layouts). > > > > The point I am trying to make is: I strongly doubt there are actually > > that many use cases that would justify adding a query type of API. It > > seems more rewarding to me to just identify the most common > > application-level uses (merely a dozen, I would guess) and provide > > declarative hooks similar to the above that are easy to understand > and > > maintain. This way Wrapper and its quirks could be kept out of custom > > code entirely. > > > > Andre > > > > _______________________________________________ > > vwnc mailing list > > [hidden email] > > http://lists.cs.uiuc.edu/mailman/listinfo/vwnc > > James Robertson > http://www.jarober.com > [hidden email] > > > > > _______________________________________________ > vwnc mailing list > [hidden email] > http://lists.cs.uiuc.edu/mailman/listinfo/vwnc _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
doh, never mind then. Why I didn't think of that is beyond me :)
On Oct 10, 2011, at 6:30 AM, Steven Kelly wrote: > It's not so easy for widgets, because a widget is displayed all the > time, whereas a menu is displayed on demand. It's thus easy for the menu > itself to check whether it should be enabled, just before it is > displayed. But a widget needs an application to explicitly tell it when > the answer to the enablement message has changed, or then some > dependency or event mechanism. > >> -----Original Message----- >> From: [hidden email] [mailto:[hidden email]] On >> Behalf Of James Robertson >> Sent: 9. lokakuuta 2011 19:12 >> To: VWNC NC >> Subject: Re: [vwnc] Sounding Out the View Tree >> >> Actually, let me throw this out there: Menus have a hook for defining >> an enablement state message. Why not do something similar for the >> widget set? >> >> On Oct 9, 2011, at 5:01 AM, andre wrote: >> >>> Admittedly the wording in my last post was a bit exaggerated. The >>> point I was trying to make is that it is worth questioning whether > we >>> need more "intelligence" to help us deal with the current > complexity, >>> or whether is is more desirable to reduce and/or hide that > complexity >>> instead. >>> >>> Since a couple years I am using a simple API to control visibility >> and >>> enablement of widgets. The application model implements the > following >>> methods (examples): >>> >>> widgetsDisabled >>> "Answer a Set of widget names that are supposed to be >> disabled >>> according to the current state of the application." >>> >>> disabled := super widgetsDisabled. >>> self canPaste >>> ifFalse:[ disabled add: #doPaste ]. >>> self canCommit >>> ifFalse:[ disabled add: #OK ]. >>> self selectedProduct isOptedForMaintenance >>> ifFalse:[ disabled addAll: self >> widgetsForMaintenance ]. >>> ^disabled >>> >>> widgetsHidden >>> "Answer a Set of widget names that are supposed to be hidden >>> according to the current state of the application." >>> >>> hidden := super widgetsHidden. >>> self selectedCustomer isEligibleForUpgrades >>> ifFalse:[ hidden addAll: self widgetsForUpgrade >> ]. >>> ^hidden >>> >>> updateVisibility >>> "... straightforward code that uses the above to change >> widget states >>> by #id" >>> >>> Each time the application changes something that affects visibility >> or >>> enablement, I just send #updateVisibility to the app. The general >>> #updateAll message also invokes this, so everything is always up to >>> date. The advantages of this approach compared to sending messages > to >>> individual widgets are: >>> >>> 1. Only a single place is reponsible. >>> 2. Declarative and descriptive code, easy to understand, maintain > and >>> extend. >>> 3. Various #canDelete, #canCommit, #canDoWhatever methods are self- >>> explanatory, inheritance-friendly and contain domain-specific code >> only. >>> 4. No need to deal with Wrapper in customer code. >>> >>> This scheme could be easily enhanced by adding >>> #widgetsContinouslyAccepted, #widgetsWhatever. It should not be too >>> difficult to come up with equivalent hooks hat accept arguments > (e.g. >>> layouts). >>> >>> The point I am trying to make is: I strongly doubt there are > actually >>> that many use cases that would justify adding a query type of API. > It >>> seems more rewarding to me to just identify the most common >>> application-level uses (merely a dozen, I would guess) and provide >>> declarative hooks similar to the above that are easy to understand >> and >>> maintain. This way Wrapper and its quirks could be kept out of > custom >>> code entirely. >>> >>> Andre >>> >>> _______________________________________________ >>> vwnc mailing list >>> [hidden email] >>> http://lists.cs.uiuc.edu/mailman/listinfo/vwnc >> >> James Robertson >> http://www.jarober.com >> [hidden email] >> >> >> >> >> _______________________________________________ >> vwnc mailing list >> [hidden email] >> http://lists.cs.uiuc.edu/mailman/listinfo/vwnc > > _______________________________________________ > vwnc mailing list > [hidden email] > http://lists.cs.uiuc.edu/mailman/listinfo/vwnc James Robertson http://www.jarober.com [hidden email] _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by andre
One other thought: Suppose a subCanvas is reused multiple times in the same UI. How can I get to widget #foo in subCanvas #A as opposed to the similarly named widget in subCanvas #B? Perhaps we need a way to identify widgets not only by id, but by parentage as well: self componentAt: #(#A #foo) [hidden email] _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Steven Kelly
On Oct 10, 2011, at 3:30 AM, Steven Kelly wrote: It's not so easy for widgets, because a widget is displayed all the Yeah, what Steven said. -- Travis Griggs Objologist "The project was so plagued by politics and ego that when the engineers requested technical oversight, our manager hired a psychologist instead." -- Ron Avitzur _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Dave Stevenson-3
Dave,
That's why I suggested an XPath syntax. :-) Seems no one is interested in that, though. I would let you crawl multi-directionally in the tree, and through attribute relations as well as parentage relations or type relations. Gregory Bourassa On 10/10/11 07:41, Dave Stevenson wrote:
----------------------------------------- Key Technology, Inc. Disclaimer Notice - The information and attachment(s) contained in this communication are intended for the addressee only, and may be confidential and/or legally privileged. If you have received this communication in error, please contact the sender immediately, and delete this communication from any computer or network system. Any interception, review, printing, copying, re-transmission, dissemination, or other use of, or taking of any action upon this information by persons or entities other than the intended recipient is strictly prohibited by law and may subject them to criminal or civil liability. Key Technology, Inc. is not liable for the improper and/or incomplete transmission of the information contained in this communication or for any delay in its receipt. _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
In reply to this post by Dave Stevenson-3
On Oct 10, 2011, at 7:41 AM, Dave Stevenson wrote:
Don't you have this already? (self componentAt: #A) componentAt: #foo I'm getting it as self ? #A ? #foo I didn't show this in the lengthy article I wrote, but the ClassCreationDialog rewrite I did to use this, had exactly that pattern in it, quite a bit actually. -- Travis Griggs Objologist My Other Machine runs OSX. But then... so does this one. _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
Free forum by Nabble | Edit this page |