Return-Path: Delivered-To: apmail-cocoon-dev-archive@www.apache.org Received: (qmail 3710 invoked from network); 19 Sep 2003 22:03:58 -0000 Received: from daedalus.apache.org (HELO mail.apache.org) (208.185.179.12) by minotaur-2.apache.org with SMTP; 19 Sep 2003 22:03:58 -0000 Received: (qmail 90216 invoked by uid 500); 19 Sep 2003 22:03:39 -0000 Delivered-To: apmail-cocoon-dev-archive@cocoon.apache.org Received: (qmail 90174 invoked by uid 500); 19 Sep 2003 22:03:39 -0000 Mailing-List: contact dev-help@cocoon.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: list-post: Reply-To: dev@cocoon.apache.org Delivered-To: mailing list dev@cocoon.apache.org Received: (qmail 90161 invoked from network); 19 Sep 2003 22:03:38 -0000 Received: from unknown (HELO smtp1.xs4all.be) (195.144.64.135) by daedalus.apache.org with SMTP; 19 Sep 2003 22:03:38 -0000 Received: from outerthought.org (195-144-088-036.dyn.adsl.xs4all.be [195.144.88.36]) (authenticated bits=0) by smtp1.xs4all.be (8.12.9/8.12.9) with ESMTP id h8JM3gbS004784 for ; Sat, 20 Sep 2003 00:03:43 +0200 Message-ID: <3F6B7D3E.7000402@outerthought.org> Date: Sat, 20 Sep 2003 00:03:42 +0200 From: Marc Portier Organization: Outerthought User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.5b) Gecko/20030723 Thunderbird/0.1 X-Accept-Language: en-us, en MIME-Version: 1.0 To: dev@cocoon.apache.org Subject: Re: Event handling in Woody References: <3F6A27D8.4010506@anyware-tech.com> <3F6ACFFD.7020400@outerthought.org> <3F6B0466.4050403@anyware-tech.com> <3F6B1F2D.1010704@outerthought.org> <3F6B2977.5010005@anyware-tech.com> In-Reply-To: <3F6B2977.5010005@anyware-tech.com> Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N Sylvain Wallez wrote: > Marc Portier wrote: > >> Sylvain Wallez wrote: > > > > >> hm, this makes these listeners totally stateless, no? >> what I mean: >> - probably woody is going to instantiate them with a Class.forname() >> and newInstance() sequence, correct? >> - as such they have no initial state other then some defaults >> (no-argument-constructor) >> - consequence: they don't have pointers into our use-case controllers >> (be it flowscript or apples) like is the case with swing and the usage >> of anonymous inner classes. Point being: it is going to be hard to >> callback into your business logic with these events, no? >> >> as a way out I see the following approaches: >> 1/ keep like above: >> so the eventListener doesn't have direct access to the use-case >> controller, but at least it has access to the Woody Form model, right? > > > > I understand your point and went through it also. But remember an wow! a historic event (I made myself clear at first attempt) > important point : repeaters dynamically create widgets, meaning you > can't attach an event listener directly on the widget contained in a > repeater, otherwise similar widgets in different rows will not have the > same behaviour. > > Now a WidgetEvent contains the source widget along with the FormContext > that was used when it was generated. And I also added a > FormContext.getForm() to access the root of the widget tree. > sounds good, it's pretty much what I understood/expected >> so it could change state-information in the model (userData area or >> hidden widgets) and as such it becomes available to the >> use-case-controller since it has a point to the form and can expect it. > > > > Yep. But using hidden widgets for this doesn't seem the right solution > for me. I thought of adding get/setAttribute on Widget (or only on > Form?), but did not found a real need for it now. Maybe it's needed > after all... > yes there is: as I explained there is obvious need for 1/ event listeners that only are responsible for dynamic effects on the 'view' (which can be handled by this) 2/ but in the general case there is obviously also need for event-listeners that hook up into the 'controller' for the latter I find get/setAttribute only marginally better then hidden widgets: both add a loose contract where a strong one is possible, thus removing guidance from compile time check and introducing possible errors... >> What I dislike about this is that the active-event-trigger becomes a >> passive data element waiting to be read. >> >> 2/ have the form-model declare named event-dispatchers >> which behave like a sort of event-queue-channels >> >> >> >> >> ... >> >> >> >> and add methods on the form model so the use case controller can >> register it's own instances to receive events from these named >> event-queue-channels: >> >> form.registerValueChangedListener( >> "foo_changed", >> new ValueChangedListener(){ >> >> public void valueChanged(ValueChangedEvent evt){ >> Widget source = evt.getSource(); >> Object newValue = evt.getNewValue(); >> ... >> EnclosingClass.this.whatEverStateVariable = ...; >> } >> }); >> >> >> we avoid the 'dynamic widget instance' problem you mentioned above >> and we can easily have eventListeners that can callback into our use >> case controllers... > > > > I see your point. This IMO fits into the current architecture as it can > simply be a new "DispatcherEventListener" implementation. Isn't this > also somewhat similar to the "broadcasters" in XUL ? sorry don't know XUL, but I do feal you understood what I meant > >> obviously from my text I like 2/ the most, since it could also be >> incrementally added to your current stuff? > > > > Yep. > >> what do you think? > > Sounds good ! > >>> >>> var list = this.parent.getWidget("thelist"); >>> list.selectionList = >>> Packages.com.my.App.getSelectionForTheList(this.value); >>> >>> >>> >>> >>> Similarily, there's an on widgets. >> >> >> >> would probably have chosen on-valueChange and on-action, but that's a >> matter of taste probably >> >>> I reused the JSF patterns for event handling : events are buffered >>> until the current processing phase is finished and fired afterwards, >>> to ensure the widget tree is in a consistent state. >> >> >> >> sounds like you already have something like event-dispatchers in place? > well what I thought I read was: you seemed to 'queue' up events in a channel to be sent out only later but this clarification makes me see you send them back over the originating widget, which would probably not be neccessary if we use the event-dispatchers which are defined on the form level any way, no? then form processing would allow all widgets to post their events in their assigned queue where they are hold untill processing is completed and thus only sent out at a moment where all widgets were in a new state (which is mucho important, didn't realize until you mentioned it, nice one to catch!) > > Not exactly : events are registered on the FormContext and broadcasted > back to their originating widget once the current processing phase is > finished. As we speak, I'm listing these phases and found the following : > - load (binding) > - readFromRequest (may be this should be merged with load in a "get > value" phase ?) > - validate, > - save (binding) > ah, you mean each of these processing-phases could possibly result in events been thrown, so not only the readFromRequest? yep, makes sense! >>> I still have to define also the ability for the form to be submitted >>> when a widget's value changes. Considering the car selector example, >>> this would allow to refresh the model list as soon as the maker is >>> changed withouth having to push the "select maker" button. >> >> >> >> you mean having a client-side javascript thingy to do a submit-on-change? > > > > Yep. > >> I saw Bruno tinkering on something like this, IIRC it was triggering >> an action (since we didn't have value-changed events) >> >> only downside of it is that you have to add a no-value option to the >> list which is selected by default in order to trigger the change: >> >> the list then shows the text (select one from the list) otherwise you >> can't trigger the generation of the select-maker if e.g. the option >> you want to make is the default selected one :-) > > > > We must assume that the form is in a consistent state when sent to the > browser. I mean in the above example, the model-list should have been > updated before the form is sent. This can be achieved automagically with > the "on-change" (or "on-value-change") being triggered during the "get > value" phase (binding or reading from the request). > yesyesyes... following the thought-train, right on! >>> I'm thinking of adding a new "submit-on-change" attribute on >>> , but I'm wondering if this belongs to the view or to the >>> form definition. Seems better on the view. >> >> >> >> yep, that's what Bruno did, if you like, I can check how he did it. > > > > onchange="getForm(this).submit()" ? Maybe with an additional hidden > field indicating that it's not a "normal" submit ? > yep, and AFAIR the hidden field was just the action-widget itself! >>>> also this links back to another wild idea I'm having >>>> I'm still not quite satisfied with the way the repeater-binding (the >>>> not simple one) needs to operate for detecting >>>> new/deleted/syill-existing rows in the repeater... >>>> >>>> So I hope the way you handle valuechanged can give some inspiration >>>> on how to mark the changed state of repeater rows? >>> >>> >>> >>> Aha ! I was also thinking of an event-driven binding, but kept this >>> wild idea for now : the binding would register itself on the repeater >>> and "collect" modifications. >> >> >> >> yep, it is different to what I was thinking but it might serve as an >> alternative. >> >> what I meant is that the control itself would be remembering what >> happened to the rows in between two calls to some method that sets a >> 'resetRowModificationInfo()' which would then be called to set some >> revision-point between which the control would be remembering some >> metadata on each of the rows. >> >> of course this borrows from the current way binding is used: once at >> the start (load) and once at the end (save) of the use case. > > > > Can't this directly be implemented in the repeater itself ? The repeater > tracks row modifications, and the binding is responsible to call > resetRowModificationInfo(), recordRowModifications(true) and > getRowModificationInfo() > yep, that is what I was thinking and then the getRowModificationInfo() could provide for a interateNewRows() iterateDeletedRows() iterateStillExistingRows() to make it ever more easy ;-) >> looking at how this often maps to talking to a (persistency) backend >> system I'm not really tempted into changing this usage pattern for now >> >> of course if we agree that stacking up these changes are not part of >> the concern of the widget itseld, then we will need a listener of I agree that making it a concern of the repeater-widget itself just makes things a lot easier However I'm still somewhat doubthing if we should. question: Is 'we like it for the binding' enough of a justification to add this concern to the repeater-widget ? (of course: if it is useful to the binding it will also be useful to people that don't use the binding but are handling the changes programatically, right?) also adding the recordRowModificationInfo(true) helps me see we could just do it: it indicates that it really is an exteral concern we can add dynamically (I like) >> some kind to do it for us... still have to think about how the Binding >> could then get a hold of those results. >> >>>> BTW: I assume that the ValueChangedEvent is holding both the old and >>>> new value? >>> >>> >>> >>> Of course ! >> >> >> >> yep, but what I really meant is that I was assuming that the widget >> was remembering both old and new states (which I realize now it is >> probably not) > > > > Ah, you mean the widget itself ? No : the old value is lost. But is it > really useful to keep the old value if the ValueChangedEvent has it ? > Nope. I have to admit I was trying to find in your current work some argumentation for adding my repeater-concern expained above... you know this kind of reasoning: << If we add 'oldValue' to fields just for the events, it would somewhat justify my lingering addition to the repeaters??? >> I reallize the reasoning is a false one to make (but so human ;-)) In every case talking about it resulted in a finer way to solve things... >>>>> allow event listeners to be written both in Java (classic-style) >>>>> and in JavaScript (flowscript-style). >>>> >>>> >>>> >>>> Makes sense. >>>> >>>> I guess reusing the awt eventhandling interfaces is out of the >>>> question? >>> >>> >>> >>> Using AWTEvent doesn't really makes sense, but the new FormEvent >>> class extends java.util.EventObject, and the listeners extend >>> java.util.EventListener. However, I used the convenient >>> AWTEventMulticaster as the base class for a FormEventMulticaster. >>> >>>> Not that I would consider it as a breaking design point to start >>>> using it, but yesterday someone showed me usage of jbeaver (see at >>>> http://www.ratundtat.com/index01.htm?menue=40, it's not oss but it >>>> didn't look entirely impossible to create something similar.) and I >>>> couldn't help dreaming about a common GUI description language that >>>> could result into both Swing and Woody based apps... >>> >>> >>> >>> And SWT... Some of my current wild thoughts is to use Woody and >>> flowscript to build Eclipse plugins. Imagine every component coming >>> with its own Woody form and associated flowscript and binding : >>> building a Cocoon IDE for Eclipse can become quite easy. >> >> >> >> yep, but what I meant was this: in order to be able to swap from the >> one to the other it would be really nice to also be able to re-use the >> use-case controllers and their accompanying eventListeners... >> >> of course if we go for this event-dispatcher idea that would still >> hold, no? > > > > Further thinking needed on this : I'm concentrating on the "normal" use > of forms for now to avoid energy dispersion ;-) > no pro, I have no direct need for this (nor much spare time ATM) and this is just more stuff we can dig up during the hackathon ;-) main idea was however: (in wild mode) supposing we have something that can generate a 'woody form' represented in a set of swing or swt widgets in an standalone app (which was the topic here) and assuming we go for the event-dispatchers then the same use case controller could simply use the same form.registerValueChangedListener(dispatcherName, myWoodyListener); in the swt/swing context, it would just be the internal machinery that maps low level awt events to the correct dispatcher (thus the dispatch idea allows hiding the specifics of either web-requests or awt events, thus enabling reuse of more code...) >>>> From that angle: being able to share your event-controllers might >>>> make sense (although wrapping them inside specific listener >>>> interfaces should not be that hard) >>> >>> >>> >>> Sorry, don't understand: what are "event-controllers" ? >> >> >> >> sorry here, I started mixing use-case controller (i.e flow-script or >> apple) and the eventListener... as shown above they often go hand in >> hand: >> >> some eventListeners only affect the view itself, but some of them >> really have to callback into the state of the use-case. > > > > Ah, ok, I understand. Then either the event-dispatcher stuff or adding > attributes to widgets may be the solution. > yes, but we prefer... ;-) regards, -marc= -- Marc Portier http://outerthought.org/ Outerthought - Open Source, Java & XML Competence Support Center Read my weblog at http://radio.weblogs.com/0116284/ mpo@outerthought.org mpo@apache.org