Brushes and state

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
36 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

Mariano Montone
James,

On Fri, Jun 19, 2009 at 3:28 PM, James Foster <[hidden email]> wrote:
renderContentOn: html
    |marker|
    self map
        center: 45.5267 @ -122.8390 zoom: 11;
        setUIToDefault.
   
    marker := (GMarker overlayAt: 45.5267 @ -122.8390)
                          draggable: true.
    self map addOverlay: marker.
    marker onDrag: ( html gmrequest callback: [:latlng | self lat: latlng lat lng: latlng lng ]).

That places a marker and callbacks whenever the user moves the marker across the map.

This seems quite similar to my current implementation, though my examples put the marker above the map creation to encapsulate the rendering. In the above, does 'self map' return a Brush or a WAComponent subclass? 
 
It returns the brush and that's the problem. I'm just generating the code to bind the callback at the moment, but it's meaningless to store the marker in an instance variable in the brush. That's why I'm considering separating static and dynamic behavior.  I may use brushes for rendering static parts and a component to hold markers and the like.

I'm using callbacks, but that has some limitations if I want to hold the markers, so I'll be moving the callbacks and markers to a component.

I agree that the callbacks need to call a component and a component is needed to save state. What I don't see is that the map itself should be a component rather than a brush. Do you envision adding the marker list to your base GoogleMap component? Do you expect the markers to also be known to your domain logic? If so, then it seems that the information is in three places: (1) on the map in the browser; (2) on the map component; and (3) in the domain. I don't see the benefit of (2). 
 
I think they should be part of your domain model, but that doesn't mean you can get rid of every logic that belongs to the Controller (in MVC). 

If you wanted to capture the current selection from a drop-down list, would you create a component to wrap the drop-down list or would you have the drop-down list notify the component of its current value. I don't see any substantive distinction between a GoogleMap and a drop-down list. They each have state and can notify the server when that state changes. Yes, the map has more states (multiple markers) and the states are more complex (two floating-point numbers for the location of a marker instead of an integer for the current selection). But, fundamentally, the concept is the same. Something changed in the browser and we want to record that in the server. 

I could easily imagine that you would create a reusable component that includes a map, but the map itself does not need to be a component any more than a drop-down list needs to be a component.
 
Maybe, but the drop down list has to handle your domain model list somehow. For example, this is what I would expect from a good drop down list "component": I would serve my domain model list to it and register a callback whenever the selected object changes. Something like:

(DropDownList on: myList) onChange: [:selectedItem | self doSomethingWith: selectedItem ]

Or better, with something like value models:

selectedItemHolder := ValueHolder on: myList first.
DropDownList on: myList selectedItem: selectedItemHolder
This would not make sense in a traditional client/server application, that's why you haven't found a need for something like this, I think.

I don't understand this distinction. Are you implying that event-based updates are not useful if the web server is remote from the client? It seems to me that most of AJAX is useful in just this case. I assume that when I'm using http://maps.google.com to get directions and I drag a marker on the map, it sends an event back to the server and recalculates the route, the time, and the distance, and renders new information to the client. I don't see that your use case is any different. Having a shorter network time is nice, but doesn't change the basic design or usefulness of being notified of events.

Ok

Cheers, Mariano

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

Colin Putney
In reply to this post by Julian Fitzell-2

On 19-Jun-09, at 12:20 PM, Julian Fitzell wrote:

> The idea of having different Renderers is that, for example,  
> somebody might eventually decide they'd really like to have  
> templates in Seaside and that would probably be implemented as a  
> different Renderer. Or maybe they want to embed a google map in a  
> Component that is using the RSS Renderer (actually, that Renderer  
> still uses Brushes, so it may not be the best example). As I said,  
> this is relatively theoretical at the moment given the lack of other  
> Renderers but I'm interested in it from the architectural angle.

Well, too theoretical. I'm working integrating my Altitude experiment  
back into Seaside 2.9. It makes changes to the rendering of links and  
forms, so I'm using an alternate Renderer to allow it to exist side-by-
side with the normal behavior. Creating alternate renderers isn't a  
very common need, but it's really handy when you need to do something  
out of the ordinary. Without adding special support, I wouldn't be  
able to use James maps implementation with Atitude.

> I believe (and this is where I may be wrong) that an implementation  
> using Brushes is (slightly) less flexible and, while there are  
> things that would be easier using Brushes, I don't see that this  
> problem does any of those things.

I think the problem here is that it violates the intent of the design.

WAHtmlCanvas provides an abstraction for generating HTML. It does  
provide a very few higher level abstractions, such as callbacks, but  
generally it operates at the level of tags and attributes. Rendering a  
higher-level abstraction in terms of HTML tags is not the roll of a  
brush, it's the roll of a component. Now, if you don't need to retain  
state between HTTP requests, you don't need a full component; a simple  
painter will do.

I think implementing a new kind of brush should be a fairly rare  
thing, and reserved for those occasions where you need to generate  
markup that the regular WAHtmlCanvas can't produce.

Colin
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

Julian Fitzell-2
On Sat, Jun 20, 2009 at 3:22 PM, Colin Putney <[hidden email]> wrote:

On 19-Jun-09, at 12:20 PM, Julian Fitzell wrote:

The idea of having different Renderers is that, for example, somebody might eventually decide they'd really like to have templates in Seaside and that would probably be implemented as a different Renderer. Or maybe they want to embed a google map in a Component that is using the RSS Renderer (actually, that Renderer still uses Brushes, so it may not be the best example). As I said, this is relatively theoretical at the moment given the lack of other Renderers but I'm interested in it from the architectural angle.

Well, too theoretical. I'm working integrating my Altitude experiment back into Seaside 2.9. It makes changes to the rendering of links and forms, so I'm using an alternate Renderer to allow it to exist side-by-side with the normal behavior. Creating alternate renderers isn't a very common need, but it's really handy when you need to do something out of the ordinary. Without adding special support, I wouldn't be able to use James maps implementation with Atitude.

Agreed. Nobody much has taken advantage of custom Renderers so far but I think that's partly because the architecture hasn't been as clear as it could have been in the last few recently. It's much improved now (I'll get some diagrams out before we go final) and I suspect people will find more uses for it as they become more familiar with the full architecture.
 
I believe (and this is where I may be wrong) that an implementation using Brushes is (slightly) less flexible and, while there are things that would be easier using Brushes, I don't see that this problem does any of those things.

I think the problem here is that it violates the intent of the design.

WAHtmlCanvas provides an abstraction for generating HTML. It does provide a very few higher level abstractions, such as callbacks, but generally it operates at the level of tags and attributes. Rendering a higher-level abstraction in terms of HTML tags is not the roll of a brush, it's the roll of a component. Now, if you don't need to retain state between HTTP requests, you don't need a full component; a simple painter will do.

In fact the Canvas doesn't even really provide callbacks. Callbacks are in the Core package and could be used without the Canvas package even being loaded. The CallbackRegistry is held by the RenderContext, with which every Renderer (including Canvases) are initialized. Renderer simply exposes the callback functionality of its context to its users.

I think implementing a new kind of brush should be a fairly rare thing, and reserved for those occasions where you need to generate markup that the regular WAHtmlCanvas can't produce.

That's my contention too. In particular, as I said to James, Brushes are intended to be "stacked" so if it doesn't make sense for someone to stack another Brush on top of yours, that's a warning sign. Also, TagBrushes are pretty much intended to be directly mapping to X(HT)ML tags so if yours isn't you're probably fighting against something there. Finally, if you find your Brush either duplicating code from another Brush or actually trying to *use* another Brush I'd consider that a warning sign too.

I'm curious to see whether James' results from trying to implement GoogleMap as a painter-like object in 2.8 confirm this.

Julian

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

jgfoster
In reply to this post by Julian Fitzell-2
Julian,

I've gotten things going with GoogleMap as something other than a Brush or Component (see GoogleMaps-jgf.22.mcz in http://seaside.gemstone.com/ss/GoogleMaps.html). As an exercise in rendering a generic object, it was certainly useful (I knew of the capability, but hadn't ever done it). My immediate reaction is that it is cleaner in that it _uses_ a brush rather than _is_ a brush. On the other hand, there are some complications. 

With the Brush implementation, there are a number of inherited "features" that just come along. Specifically, I now need to cache any tag-related attributes that the programmer might want to set (such as ID, CLASS, etc.) and put them out. If there were a need for a full implementation, then this could get awkward (for example, I didn't do STYLE, and I don't support generic attributes).

Also, when I create my own canvas in #'renderOn:' (as suggested by your code just below), callbacks fail because the implementation of #'children' is incomplete. My work-around was to just use the passed-in Renderer, assuming that it supports HTML. 

If the design philosophy is that Brushes represent only things defined in the official HTML spec (which seems reasonable), then the new approach is good enough. I do like this better than making a full Component (which seems heavy-weight).

Comments?

James

On Jun 19, 2009, at 7:51 PM, Julian Fitzell wrote:

That should provide a good comparison.

Looking at the 2.9 code now I realize it may be hard for you to simply lift the code because of the refactoring in the way walking the component tree happens.

As a starting point for 2.8, try making GoogleMap subclass Object and implement something like:

renderOn: aRenderer
  |html|
  html := WAHtmlCanvas
    context: aRenderer context
    callbacks: (aRenderer context callbacksFor: self).
  self renderContentOn: html.

I think you should then be able to implement #renderContentOn: to output your content and allow a GoogleMap to be passed to the #render: method.

That code is untested, just typed into gmail, but it should be something like that. :)

Julian

On Fri, Jun 19, 2009 at 4:43 PM, James Foster <[hidden email]> wrote:
Julian,

I think the only think I'm equipped to give even a weak opinion on is that as I was doing this some months ago I found a Brush to be more natural to use (as a client) than a Canvas. As to the relative merits of Painter and Renderer, I'm just not sufficiently informed to venture an educated opinion (though that isn't usually an impediment ;-). 

I like your point that a Brush can be embedded in another Brush and this is a feature that the GoogleMap does not need. Given that sending #with: to a GoogleMap does not make sense, I'll agree that making it a Brush is a bit awkward. Also, there is a sense in which subclassing from WADivTag comes back to the question of delegation vs. inheritance, and in principal I'd prefer delegation. 

In an effort to contribute more intelligently to this discussion, I'd like to try refactoring my library using a Painter. I'll fire up a Seaside 2.9 environment and look at WAPainter with an eye toward what would be required to refactor my GoogleMap in a Seaside 2.8 environment. 

James

On Jun 19, 2009, at 12:20 PM, Julian Fitzell wrote:

On Fri, Jun 19, 2009 at 10:50 AM, James Foster <[hidden email]> wrote:
Julian,

As you were going through this my thought was eventually expressed when you said that the painter approach "works essentially the same as the brush" approach. And Yes, my updateRoot: code was simply an extension to WAComponent that adds some JavaScript. Switching it to a class-side method on GoogleMaps as you describe would be equivalent and perhaps more obvious and less intrusive. 

The part I'm not yet following or seeing much value in is the idea that using a different renderer would be desirable. I don't see much point of rendering a GoogleMap on anything other than an HTML canvas. Or are you envisioning two renderers (HTML for the GoogleMap thing and something else for the rest)? At the moment, my understanding of the alternate renderers is so shallow that I don't see the use/value. For my purposes, at least, I'll probably leave my implementation as a Brush subclass since it seems to work for all use cases I understand. Which is not to say that I mind the discussion!

James,

The discussion is clarifying for me as well so let's carry on... :)

First, I do think your design would be improved slightly by putting that method on the class side of GoogleMap.

The idea of having different Renderers is that, for example, somebody might eventually decide they'd really like to have templates in Seaside and that would probably be implemented as a different Renderer. Or maybe they want to embed a google map in a Component that is using the RSS Renderer (actually, that Renderer still uses Brushes, so it may not be the best example). As I said, this is relatively theoretical at the moment given the lack of other Renderers but I'm interested in it from the architectural angle.

I believe (and this is where I may be wrong) that an implementation using Brushes is (slightly) less flexible and, while there are things that would be easier using Brushes, I don't see that this problem does any of those things.

Or to look at it another way, just as you say that GoogleMap doesn't need to *be* a Component but can be rather *used by* a Component, I don't think it needs to *be* a Brush but can instead *use* a Brush; there's nothing you need to output to the Document that can't easily be done with the existing Brushes.

So you might end up with

MyComponent
  (uses)
any Renderer
  (to render a)
GoogleMap (a Painter)
  (which uses its)
HtmlCanvas (a Renderer)
  (to get a)
DivTag (a Brush)
  (which writes to)
HtmlDocument (a Document)
  (which writes to)
Stream

When I say it "works essentially the same as the brush", what I mean is, the Painter implementation is equally convenient to use and no harder to implement but provides added flexibility. It also requires no class method extensions and means you can leverage WAScriptTag instead of duplicating stuff to make sure the CDATA is added and so on. Since I can't (yet) see an advantage to the Brush implementation, my suggestion is that the Painter implementation is "more correct" architecturally.

I still think "uses a Brush" (actually two) is a clearer relationship for this than "is a Brush" even in 2.8, but without Painter there's really no added benefit (though you could easily duplicate the very limited amount of functionality Painter has in its #renderOn: method and you'd be in the same shape as 2.9).

So the architectural question for me comes down to (sorry for the long message), is there an additional benefit to doing it as a Brush over a Painter that I'm missing?

Julian
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

Julian Fitzell-2
I'm not sure exactly what the issue with #children is, but I know (or at least think I know) that what I posted should work (conceptually) in 2.9. It's possible that stuff isn't quite factored correctly in 2.8 somehow... or it's possible the code I wrote in the email just wasn't quite right.

There's definitely some extra grayness in this case because what you're rendering is *largely* a div, even though it's more than that and I can see your point about tag attributes. One thing that comes to mind here (don't try this at home, kids, it may yet be a terrible idea) is to give GoogleMap a block that gets passed the div brush during rendering. Would look something like:

renderContentOn: html
  html render:
    (GoogleMap new
      setCenter: 45.5267 @ -122.8390 zoom: 11;
      enableGoogleBar;
      addType: GMapType physical;
      addControl: GControl largeMapControl;
      divBlock: [ :div | div class: 'foo'; style: 'bar' ])

And then GoogleMap would have

renderContentOn: html
  div := html div;
  div
    doSomeStuff;
    someMoreStuff.
  self divBlock value: div.
  div with: ["you probably don't need this since your div is empty"]

Now I don't know that this is a good idea nor am I even 100% sure it works but it seems like it should and *could* be a solution to this problem. The problem is of course no different than any other component that has a top-level div that somebody might want to customize the attributes of so it's not unique to your case. Of course, somebody could also just subclass GoogleMap to do that kind of custom styling (as long as it is well-factored) and that may actually be the best option.
     
I'm not sure that brushes literally need to only represent HTML spec tags, though TagBrushes certainly lean that way and it may be a good principle to keep an mind as a starting point.

If you want to provide a walkback on the #children problem I can try to see if it is solvable in 2.8.

Julian

On Mon, Jun 22, 2009 at 3:44 PM, James Foster <[hidden email]> wrote:
Julian,

I've gotten things going with GoogleMap as something other than a Brush or Component (see GoogleMaps-jgf.22.mcz in http://seaside.gemstone.com/ss/GoogleMaps.html). As an exercise in rendering a generic object, it was certainly useful (I knew of the capability, but hadn't ever done it). My immediate reaction is that it is cleaner in that it _uses_ a brush rather than _is_ a brush. On the other hand, there are some complications. 

With the Brush implementation, there are a number of inherited "features" that just come along. Specifically, I now need to cache any tag-related attributes that the programmer might want to set (such as ID, CLASS, etc.) and put them out. If there were a need for a full implementation, then this could get awkward (for example, I didn't do STYLE, and I don't support generic attributes).

Also, when I create my own canvas in #'renderOn:' (as suggested by your code just below), callbacks fail because the implementation of #'children' is incomplete. My work-around was to just use the passed-in Renderer, assuming that it supports HTML. 

If the design philosophy is that Brushes represent only things defined in the official HTML spec (which seems reasonable), then the new approach is good enough. I do like this better than making a full Component (which seems heavy-weight).

Comments?

James

On Jun 19, 2009, at 7:51 PM, Julian Fitzell wrote:

That should provide a good comparison.

Looking at the 2.9 code now I realize it may be hard for you to simply lift the code because of the refactoring in the way walking the component tree happens.

As a starting point for 2.8, try making GoogleMap subclass Object and implement something like:

renderOn: aRenderer
  |html|
  html := WAHtmlCanvas
    context: aRenderer context
    callbacks: (aRenderer context callbacksFor: self).
  self renderContentOn: html.

I think you should then be able to implement #renderContentOn: to output your content and allow a GoogleMap to be passed to the #render: method.

That code is untested, just typed into gmail, but it should be something like that. :)

Julian

On Fri, Jun 19, 2009 at 4:43 PM, James Foster <[hidden email]> wrote:
Julian,

I think the only think I'm equipped to give even a weak opinion on is that as I was doing this some months ago I found a Brush to be more natural to use (as a client) than a Canvas. As to the relative merits of Painter and Renderer, I'm just not sufficiently informed to venture an educated opinion (though that isn't usually an impediment ;-). 

I like your point that a Brush can be embedded in another Brush and this is a feature that the GoogleMap does not need. Given that sending #with: to a GoogleMap does not make sense, I'll agree that making it a Brush is a bit awkward. Also, there is a sense in which subclassing from WADivTag comes back to the question of delegation vs. inheritance, and in principal I'd prefer delegation. 

In an effort to contribute more intelligently to this discussion, I'd like to try refactoring my library using a Painter. I'll fire up a Seaside 2.9 environment and look at WAPainter with an eye toward what would be required to refactor my GoogleMap in a Seaside 2.8 environment. 

James

On Jun 19, 2009, at 12:20 PM, Julian Fitzell wrote:

On Fri, Jun 19, 2009 at 10:50 AM, James Foster <[hidden email]> wrote:
Julian,

As you were going through this my thought was eventually expressed when you said that the painter approach "works essentially the same as the brush" approach. And Yes, my updateRoot: code was simply an extension to WAComponent that adds some JavaScript. Switching it to a class-side method on GoogleMaps as you describe would be equivalent and perhaps more obvious and less intrusive. 

The part I'm not yet following or seeing much value in is the idea that using a different renderer would be desirable. I don't see much point of rendering a GoogleMap on anything other than an HTML canvas. Or are you envisioning two renderers (HTML for the GoogleMap thing and something else for the rest)? At the moment, my understanding of the alternate renderers is so shallow that I don't see the use/value. For my purposes, at least, I'll probably leave my implementation as a Brush subclass since it seems to work for all use cases I understand. Which is not to say that I mind the discussion!

James,

The discussion is clarifying for me as well so let's carry on... :)

First, I do think your design would be improved slightly by putting that method on the class side of GoogleMap.

The idea of having different Renderers is that, for example, somebody might eventually decide they'd really like to have templates in Seaside and that would probably be implemented as a different Renderer. Or maybe they want to embed a google map in a Component that is using the RSS Renderer (actually, that Renderer still uses Brushes, so it may not be the best example). As I said, this is relatively theoretical at the moment given the lack of other Renderers but I'm interested in it from the architectural angle.

I believe (and this is where I may be wrong) that an implementation using Brushes is (slightly) less flexible and, while there are things that would be easier using Brushes, I don't see that this problem does any of those things.

Or to look at it another way, just as you say that GoogleMap doesn't need to *be* a Component but can be rather *used by* a Component, I don't think it needs to *be* a Brush but can instead *use* a Brush; there's nothing you need to output to the Document that can't easily be done with the existing Brushes.

So you might end up with

MyComponent
  (uses)
any Renderer
  (to render a)
GoogleMap (a Painter)
  (which uses its)
HtmlCanvas (a Renderer)
  (to get a)
DivTag (a Brush)
  (which writes to)
HtmlDocument (a Document)
  (which writes to)
Stream

When I say it "works essentially the same as the brush", what I mean is, the Painter implementation is equally convenient to use and no harder to implement but provides added flexibility. It also requires no class method extensions and means you can leverage WAScriptTag instead of duplicating stuff to make sure the CDATA is added and so on. Since I can't (yet) see an advantage to the Brush implementation, my suggestion is that the Painter implementation is "more correct" architecturally.

I still think "uses a Brush" (actually two) is a clearer relationship for this than "is a Brush" even in 2.8, but without Painter there's really no added benefit (though you could easily duplicate the very limited amount of functionality Painter has in its #renderOn: method and you'd be in the same shape as 2.9).

So the architectural question for me comes down to (sorry for the long message), is there an additional benefit to doing it as a Brush over a Painter that I'm missing?

Julian
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside



_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

jgfoster
Julian,

The #children problem is reported by Seaside in the usual manner with no extra explanation (see attached).

The idea of passing a block to the was one that I thought of as well. I seem to recall that the X Window System had something analogous for initializing widgets (I can't lay my hands on any documentation, however). On the other hand, it seems to be adding complexity in that some things are set one way and some things another way. I'd almost prefer everything being done in the block:

MyComponent>>#renderContentOn: html
html render: (GoogleMap configure: [:map | 
map
class: 'foo';
style: 'bar';
setCenter...;
yourself.
].

Of course, this has the disadvantage of forcing you to use the more complex style for everything. 

Without harping on the point too much, I'm still not feeling as uncomfortable as it seems I should with the Brush approach. One of the things mentioned as a characteristic of a Brush is that it can have things embedded in it (using #'with:'). There are exceptions already, including <br /> and <hr />, so this isn't really a firm rule. Also, a GoogleMap makes sense only on an WAHtmlCanvas. While it can create its own Canvas, that seems a bit of an additional complexity. I guess I'm still seeing HorizontalRule, Button, TextInput, ListBox, and GoogleMap as being on a continuum of simple to complex but otherwise not substantively different in the way they are created and used. For some, embedding other tags makes sense; for others it does not. Most can display user-defined data and return new values. Most have events that can trigger asynchronous callbacks. All are represented by HTML Tags. Some share tags but are distinguished only by the class (input button). With the GoogleMap, I could even imagine embedding other content that would show if JavaScript were disabled in the browser.

In any case, it seems like several options are available. Maybe if new Painters were more familiar, that approach wouldn't seem so different. (Okay, having typed that last sentence I see that it isn't very profound!)

James




On Jun 22, 2009, at 7:24 PM, Julian Fitzell wrote:

I'm not sure exactly what the issue with #children is, but I know (or at least think I know) that what I posted should work (conceptually) in 2.9. It's possible that stuff isn't quite factored correctly in 2.8 somehow... or it's possible the code I wrote in the email just wasn't quite right.

There's definitely some extra grayness in this case because what you're rendering is *largely* a div, even though it's more than that and I can see your point about tag attributes. One thing that comes to mind here (don't try this at home, kids, it may yet be a terrible idea) is to give GoogleMap a block that gets passed the div brush during rendering. Would look something like:

renderContentOn: html
  html render:
    (GoogleMap new
      setCenter: 45.5267 @ -122.8390 zoom: 11;
      enableGoogleBar;
      addType: GMapType physical;
      addControl: GControl largeMapControl;
      divBlock: [ :div | div class: 'foo'; style: 'bar' ])

And then GoogleMap would have

renderContentOn: html
  div := html div;
  div
    doSomeStuff;
    someMoreStuff.
  self divBlock value: div.
  div with: ["you probably don't need this since your div is empty"]

Now I don't know that this is a good idea nor am I even 100% sure it works but it seems like it should and *could* be a solution to this problem. The problem is of course no different than any other component that has a top-level div that somebody might want to customize the attributes of so it's not unique to your case. Of course, somebody could also just subclass GoogleMap to do that kind of custom styling (as long as it is well-factored) and that may actually be the best option.
     
I'm not sure that brushes literally need to only represent HTML spec tags, though TagBrushes certainly lean that way and it may be a good principle to keep an mind as a starting point.

If you want to provide a walkback on the #children problem I can try to see if it is solvable in 2.8.

Julian

On Mon, Jun 22, 2009 at 3:44 PM, James Foster <[hidden email]> wrote:
Julian,

I've gotten things going with GoogleMap as something other than a Brush or Component (see GoogleMaps-jgf.22.mcz in http://seaside.gemstone.com/ss/GoogleMaps.html). As an exercise in rendering a generic object, it was certainly useful (I knew of the capability, but hadn't ever done it). My immediate reaction is that it is cleaner in that it _uses_ a brush rather than _is_ a brush. On the other hand, there are some complications. 

With the Brush implementation, there are a number of inherited "features" that just come along. Specifically, I now need to cache any tag-related attributes that the programmer might want to set (such as ID, CLASS, etc.) and put them out. If there were a need for a full implementation, then this could get awkward (for example, I didn't do STYLE, and I don't support generic attributes).

Also, when I create my own canvas in #'renderOn:' (as suggested by your code just below), callbacks fail because the implementation of #'children' is incomplete. My work-around was to just use the passed-in Renderer, assuming that it supports HTML. 

If the design philosophy is that Brushes represent only things defined in the official HTML spec (which seems reasonable), then the new approach is good enough. I do like this better than making a full Component (which seems heavy-weight).

Comments?

James

On Jun 19, 2009, at 7:51 PM, Julian Fitzell wrote:

That should provide a good comparison.

Looking at the 2.9 code now I realize it may be hard for you to simply lift the code because of the refactoring in the way walking the component tree happens.

As a starting point for 2.8, try making GoogleMap subclass Object and implement something like:

renderOn: aRenderer
  |html|
  html := WAHtmlCanvas
    context: aRenderer context
    callbacks: (aRenderer context callbacksFor: self).
  self renderContentOn: html.

I think you should then be able to implement #renderContentOn: to output your content and allow a GoogleMap to be passed to the #render: method.

That code is untested, just typed into gmail, but it should be something like that. :)

Julian

On Fri, Jun 19, 2009 at 4:43 PM, James Foster <[hidden email]> wrote:
Julian,

I think the only think I'm equipped to give even a weak opinion on is that as I was doing this some months ago I found a Brush to be more natural to use (as a client) than a Canvas. As to the relative merits of Painter and Renderer, I'm just not sufficiently informed to venture an educated opinion (though that isn't usually an impediment ;-). 

I like your point that a Brush can be embedded in another Brush and this is a feature that the GoogleMap does not need. Given that sending #with: to a GoogleMap does not make sense, I'll agree that making it a Brush is a bit awkward. Also, there is a sense in which subclassing from WADivTag comes back to the question of delegation vs. inheritance, and in principal I'd prefer delegation. 

In an effort to contribute more intelligently to this discussion, I'd like to try refactoring my library using a Painter. I'll fire up a Seaside 2.9 environment and look at WAPainter with an eye toward what would be required to refactor my GoogleMap in a Seaside 2.8 environment. 

James

On Jun 19, 2009, at 12:20 PM, Julian Fitzell wrote:

On Fri, Jun 19, 2009 at 10:50 AM, James Foster <[hidden email]> wrote:
Julian,

As you were going through this my thought was eventually expressed when you said that the painter approach "works essentially the same as the brush" approach. And Yes, my updateRoot: code was simply an extension to WAComponent that adds some JavaScript. Switching it to a class-side method on GoogleMaps as you describe would be equivalent and perhaps more obvious and less intrusive. 

The part I'm not yet following or seeing much value in is the idea that using a different renderer would be desirable. I don't see much point of rendering a GoogleMap on anything other than an HTML canvas. Or are you envisioning two renderers (HTML for the GoogleMap thing and something else for the rest)? At the moment, my understanding of the alternate renderers is so shallow that I don't see the use/value. For my purposes, at least, I'll probably leave my implementation as a Brush subclass since it seems to work for all use cases I understand. Which is not to say that I mind the discussion!

James,

The discussion is clarifying for me as well so let's carry on... :)

First, I do think your design would be improved slightly by putting that method on the class side of GoogleMap.

The idea of having different Renderers is that, for example, somebody might eventually decide they'd really like to have templates in Seaside and that would probably be implemented as a different Renderer. Or maybe they want to embed a google map in a Component that is using the RSS Renderer (actually, that Renderer still uses Brushes, so it may not be the best example). As I said, this is relatively theoretical at the moment given the lack of other Renderers but I'm interested in it from the architectural angle.

I believe (and this is where I may be wrong) that an implementation using Brushes is (slightly) less flexible and, while there are things that would be easier using Brushes, I don't see that this problem does any of those things.

Or to look at it another way, just as you say that GoogleMap doesn't need to *be* a Component but can be rather *used by* a Component, I don't think it needs to *be* a Brush but can instead *use* a Brush; there's nothing you need to output to the Document that can't easily be done with the existing Brushes.

So you might end up with

MyComponent
  (uses)
any Renderer
  (to render a)
GoogleMap (a Painter)
  (which uses its)
HtmlCanvas (a Renderer)
  (to get a)
DivTag (a Brush)
  (which writes to)
HtmlDocument (a Document)
  (which writes to)
Stream

When I say it "works essentially the same as the brush", what I mean is, the Painter implementation is equally convenient to use and no harder to implement but provides added flexibility. It also requires no class method extensions and means you can leverage WAScriptTag instead of duplicating stuff to make sure the CDATA is added and so on. Since I can't (yet) see an advantage to the Brush implementation, my suggestion is that the Painter implementation is "more correct" architecturally.

I still think "uses a Brush" (actually two) is a clearer relationship for this than "is a Brush" even in 2.8, but without Painter there's really no added benefit (though you could easily duplicate the very limited amount of functionality Painter has in its #renderOn: method and you'd be in the same shape as 2.9).

So the architectural question for me comes down to (sorry for the long message), is there an additional benefit to doing it as a Brush over a Painter that I'm missing?

Julian
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside

walkback.html (6K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

Julian Fitzell-2
Fair enough. I don't think there are any hard and fast rules. If you, as the author, see your class more like a "special kind of div canvas tag" than as something that has it's own protocol and happens to be currently implemented by writing a div using a canvas, then so be it.

From my point of view, composition feels better than subclassing here: if you can easily generate the HTML you need with the existing tags, you shouldn't need new ones. I don't see your class creating its own Canvas as additional complexity, really (I realize it takes a little additional code in 2.8) - it's no different than what every component does. The RenderContext should carry on throughout (that's why it's there) but the API you want to use to render should be entirely up to you.

Anyway, thanks for humouring me in this little experiment. Hopefully others have found it informative.

Julian

On Tue, Jun 23, 2009 at 9:22 AM, James Foster <[hidden email]> wrote:
Julian,

The #children problem is reported by Seaside in the usual manner with no extra explanation (see attached).

The idea of passing a block to the was one that I thought of as well. I seem to recall that the X Window System had something analogous for initializing widgets (I can't lay my hands on any documentation, however). On the other hand, it seems to be adding complexity in that some things are set one way and some things another way. I'd almost prefer everything being done in the block:

MyComponent>>#renderContentOn: html
html render: (GoogleMap configure: [:map | 
map
class: 'foo';
style: 'bar';
setCenter...;
yourself.
].

Of course, this has the disadvantage of forcing you to use the more complex style for everything. 

Without harping on the point too much, I'm still not feeling as uncomfortable as it seems I should with the Brush approach. One of the things mentioned as a characteristic of a Brush is that it can have things embedded in it (using #'with:'). There are exceptions already, including <br /> and <hr />, so this isn't really a firm rule. Also, a GoogleMap makes sense only on an WAHtmlCanvas. While it can create its own Canvas, that seems a bit of an additional complexity. I guess I'm still seeing HorizontalRule, Button, TextInput, ListBox, and GoogleMap as being on a continuum of simple to complex but otherwise not substantively different in the way they are created and used. For some, embedding other tags makes sense; for others it does not. Most can display user-defined data and return new values. Most have events that can trigger asynchronous callbacks. All are represented by HTML Tags. Some share tags but are distinguished only by the class (input button). With the GoogleMap, I could even imagine embedding other content that would show if JavaScript were disabled in the browser.

In any case, it seems like several options are available. Maybe if new Painters were more familiar, that approach wouldn't seem so different. (Okay, having typed that last sentence I see that it isn't very profound!)

James

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

jgfoster
Any thoughts on the callback problem? Would that be fixed in 2.9?

James

On Jun 23, 2009, at 11:59 AM, Julian Fitzell wrote:

Fair enough. I don't think there are any hard and fast rules. If you, as the author, see your class more like a "special kind of div canvas tag" than as something that has it's own protocol and happens to be currently implemented by writing a div using a canvas, then so be it.

From my point of view, composition feels better than subclassing here: if you can easily generate the HTML you need with the existing tags, you shouldn't need new ones. I don't see your class creating its own Canvas as additional complexity, really (I realize it takes a little additional code in 2.8) - it's no different than what every component does. The RenderContext should carry on throughout (that's why it's there) but the API you want to use to render should be entirely up to you.

Anyway, thanks for humouring me in this little experiment. Hopefully others have found it informative.

Julian

On Tue, Jun 23, 2009 at 9:22 AM, James Foster <[hidden email]> wrote:
Julian,

The #children problem is reported by Seaside in the usual manner with no extra explanation (see attached).

The idea of passing a block to the was one that I thought of as well. I seem to recall that the X Window System had something analogous for initializing widgets (I can't lay my hands on any documentation, however). On the other hand, it seems to be adding complexity in that some things are set one way and some things another way. I'd almost prefer everything being done in the block:

MyComponent>>#renderContentOn: html
html render: (GoogleMap configure: [:map | 
map
class: 'foo';
style: 'bar';
setCenter...;
yourself.
].

Of course, this has the disadvantage of forcing you to use the more complex style for everything. 

Without harping on the point too much, I'm still not feeling as uncomfortable as it seems I should with the Brush approach. One of the things mentioned as a characteristic of a Brush is that it can have things embedded in it (using #'with:'). There are exceptions already, including <br /> and <hr />, so this isn't really a firm rule. Also, a GoogleMap makes sense only on an WAHtmlCanvas. While it can create its own Canvas, that seems a bit of an additional complexity. I guess I'm still seeing HorizontalRule, Button, TextInput, ListBox, and GoogleMap as being on a continuum of simple to complex but otherwise not substantively different in the way they are created and used. For some, embedding other tags makes sense; for others it does not. Most can display user-defined data and return new values. Most have events that can trigger asynchronous callbacks. All are represented by HTML Tags. Some share tags but are distinguished only by the class (input button). With the GoogleMap, I could even imagine embedding other content that would show if JavaScript were disabled in the browser.

In any case, it seems like several options are available. Maybe if new Painters were more familiar, that approach wouldn't seem so different. (Okay, having typed that last sentence I see that it isn't very profound!)

James
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

Julian Fitzell-2
Yes, I think so. Actually, what if you change to this method:

renderOn: aRenderer
  |html|
  html := WAHtmlCanvas
    context: aRenderer context
    callbacks: aRenderer context callbacks.
  self renderContentOn: html.

That may well be your problem. In 2.8, components are expected to own callbacks and your object is not a component (in 2.9 this is not a problem). But if you just use the existing callback registry created by the closest component (as the above code does), I think it might be fine?

Julian

On Tue, Jun 23, 2009 at 12:27 PM, James Foster <[hidden email]> wrote:
Any thoughts on the callback problem? Would that be fixed in 2.9?

James

On Jun 23, 2009, at 11:59 AM, Julian Fitzell wrote:

Fair enough. I don't think there are any hard and fast rules. If you, as the author, see your class more like a "special kind of div canvas tag" than as something that has it's own protocol and happens to be currently implemented by writing a div using a canvas, then so be it.

From my point of view, composition feels better than subclassing here: if you can easily generate the HTML you need with the existing tags, you shouldn't need new ones. I don't see your class creating its own Canvas as additional complexity, really (I realize it takes a little additional code in 2.8) - it's no different than what every component does. The RenderContext should carry on throughout (that's why it's there) but the API you want to use to render should be entirely up to you.

Anyway, thanks for humouring me in this little experiment. Hopefully others have found it informative.

Julian

On Tue, Jun 23, 2009 at 9:22 AM, James Foster <[hidden email]> wrote:
Julian,

The #children problem is reported by Seaside in the usual manner with no extra explanation (see attached).

The idea of passing a block to the was one that I thought of as well. I seem to recall that the X Window System had something analogous for initializing widgets (I can't lay my hands on any documentation, however). On the other hand, it seems to be adding complexity in that some things are set one way and some things another way. I'd almost prefer everything being done in the block:

MyComponent>>#renderContentOn: html
html render: (GoogleMap configure: [:map | 
map
class: 'foo';
style: 'bar';
setCenter...;
yourself.
].

Of course, this has the disadvantage of forcing you to use the more complex style for everything. 

Without harping on the point too much, I'm still not feeling as uncomfortable as it seems I should with the Brush approach. One of the things mentioned as a characteristic of a Brush is that it can have things embedded in it (using #'with:'). There are exceptions already, including <br /> and <hr />, so this isn't really a firm rule. Also, a GoogleMap makes sense only on an WAHtmlCanvas. While it can create its own Canvas, that seems a bit of an additional complexity. I guess I'm still seeing HorizontalRule, Button, TextInput, ListBox, and GoogleMap as being on a continuum of simple to complex but otherwise not substantively different in the way they are created and used. For some, embedding other tags makes sense; for others it does not. Most can display user-defined data and return new values. Most have events that can trigger asynchronous callbacks. All are represented by HTML Tags. Some share tags but are distinguished only by the class (input button). With the GoogleMap, I could even imagine embedding other content that would show if JavaScript were disabled in the browser.

In any case, it seems like several options are available. Maybe if new Painters were more familiar, that approach wouldn't seem so different. (Okay, having typed that last sentence I see that it isn't very profound!)

James
_______________________________________________


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside



_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

CSS

Robert Sirois
Ok, so this isn't quite a Seaside-related question, but here it is anyhow:

I have a dialog which I changed the '.ui-widget-content' class on the 'jqueryUiCss' method so that the dialogs would become semi-transparent (added opacity: .5).

This makes everything semi-transparent, but now what I want to do is add a div 'with: [aDiv]' sorta thing, only make that opaque on top of the semi-transparent dialog.

Changing the opacity values on the contained div modify the percentage of opacity that it is already at (can't go above one). I was hoping there was an easy way to override the container's value.

Thanks,
RS


Microsoft brings you a new way to search the web. <a href='http://www.bing.com?form=MFEHPG&publ=WLHMTAG&crea=TEXT_MFEHPG_Core_tagline_try bing_1x1' target='_new'>Try Bing™ now
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: CSS

Lukas Renggli
> This makes everything semi-transparent, but now what I want to do is add a
> div 'with: [aDiv]' sorta thing, only make that opaque on top of the
> semi-transparent dialog.

That's a common CSS problem.

There are various solutions available, the most simple is to not make
the opaque element a child of the transparent element, but a sibling.
The other one is to apply the transparency only to the background
color, not to the complete element. Unfortunately this is not
supported on some browsers.

For details see for example <http://www.css3.info/opacity_rgba_and_compromise/>.

Lukas

--
Lukas Renggli
http://www.lukas-renggli.ch
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Brushes and state

NorbertHartl
In reply to this post by Julian Fitzell-2

> Anyway, thanks for humouring me in this little experiment. Hopefully
> others have found it informative.
>
I definitely did.

Norbert


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Dialog widget

Robert Sirois
In reply to this post by Lukas Renggli
How do I change the 'title' class for the dialog-ui widget? Can I do it via 'dialogClass:'?

RS


Windows Live™ SkyDrive™: Get 25 GB of free online storage. Get it on your BlackBerry or iPhone.
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Dialog widget

Philippe Marschall
2009/6/29 Robert Sirois <[hidden email]>:
> How do I change the 'title' class for the dialog-ui widget? Can I do it via
> 'dialogClass:'?

Nope. The dialog hierarchy is quite simple and not extensively
customizable. You can make subclasses and override #divClass though.
Once your requirements get more complex you might want to consider
writing your own that fits your requirements.

Cheers
Philippe
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

RE: Dialog widget

Robert Sirois
So rather than using the ui-smoothness theme I oughta make my own. Thanks, I was leaning towards that :)

RS

> Date: Mon, 29 Jun 2009 19:32:10 +0200
> Subject: Re: [Seaside] Dialog widget
> From: philippe.marschall@gmail.com
> To: seaside@lists.squeakfoundation.org
>
> 2009/6/29 Robert Sirois <watchlala@hotmail.com>:
> > How do I change the 'title' class for the dialog-ui widget? Can I do it via
> > 'dialogClass:'?
>
> Nope. The dialog hierarchy is quite simple and not extensively
> customizable. You can make subclasses and override #divClass though.
> Once your requirements get more complex you might want to consider
> writing your own that fits your requirements.
>
> Cheers
> Philippe
> _______________________________________________
> seaside mailing list
> seaside@lists.squeakfoundation.org
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


Windows Live™: Keep your life in sync. Check it out.
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Dialog widget

Philippe Marschall
2009/6/29 Robert Sirois <[hidden email]>:
> So rather than using the ui-smoothness theme I oughta make my own. Thanks, I
> was leaning towards that :)

If you have custom markup requirements you sooner or later need to
write your own components. Seaside currently doesn't support
themes/skins where each component can have various markup, sorry. It
has never been an issue so far. If you need it you could introduce a
WAThemedComponent that would look up the theme and dispatch to the
right #renderThemeXXXMethod:.

Cheers
Philippe
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
12