<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">Julian,<div><br></div><div>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"&nbsp;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.&nbsp;</div><div><br></div><div>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!</div><div><br></div><div>James</div><div><br><div><div>On Jun 19, 2009, at 8:44 AM, Julian Fitzell wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite">Hm... yes, you're right it would need to be in an instance variable... I had forgotten about that.<div><br></div><div>I may have to go back and look at that... I intended Painter to be useable as a throw-away object. Along the way, updateRoot: got pulled up because it didn't have any dependencies on anything further down the class hierarchy but updateRoot: just can't work unless it gets called and currently that is via the #children mechanism unless you want to call it yourself.</div> <div><br></div><div>So I'm not sure whether it's better to push #updateRoot: back down to WAPresenter or leave it on WAPainter with the caveat that if the painter uses updateRoot: it needs to be listed in #children. The latter seems to add a confusing ambiguity. Could also split WAPainter into two classes but that's starting to get a little excessive, I think. Back to the architecture design board for a bit... (opinions appreciated)<br> <br></div><div>Forgetting updateRoot: then for a second, though, you could still implement simply with a Painter or renderable object by having a class-side method that does the root modification.</div><div><br></div><div> MyComponent&gt;&gt;updateRoot: aRoot</div><div>&nbsp;&nbsp; &nbsp;super updateRoot: aRoot.</div><div>&nbsp;&nbsp; &nbsp;GoogleMaps updateRoot: aRoot.</div><div><br></div><div><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; border-collapse: collapse; "><div> MyComponent&gt;&gt;#renderContentOn: html<br>&nbsp;&nbsp; &nbsp; &nbsp; html render: (GoogleMap new<div class="im" style="color: rgb(80, 0, 80); ">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class: 'myMap';<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setCenter: 45.5267 @ -122.8390 zoom: 11;</div> </div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "..."</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; yourself)</div></span></div><div><br></div><div>With that pattern, I think any object that implements #renderOn: now works essentially the same as a brush? Assuming the root updating is not component-specific (you don't show what's in it but I gather it's a class extension on Component added by your package?). WAPainter adds the benefit that the component doesn't have to be using the same Renderer that GoogleMaps expects (which the other two options depend on).</div> <div><br></div><div>Julian</div><div><br><div class="gmail_quote">On Fri, Jun 19, 2009 at 7:11 AM, James Foster <span dir="ltr">&lt;<a href="mailto:Smalltalk@jgfoster.net">Smalltalk@jgfoster.net</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"> <div style="word-wrap:break-word">Julian,<div><br></div><div>Just to clarify your example a bit, let me know if I'm on the right track here... With a 2.9 Painter if you need to implement #updateRoot: then you need to be in the parent's #children. To be in the parent's children, you probably need to be in an instance variable and you probably need to be set up in the parent's #initialize method. Thus, your example below of creating the map during #renderContentOn: would not work.</div> <div><br></div><div>To use a Painter or Component: (1) add an instance variable to the parent for each map; (2) initialize each instance variable in #initialize; (3) add each instance variable to #children; and (4) use the map in #renderContentOn:.</div> <div><br></div><div>To use a Brush: (1) add one message send to your #updateRoot: for any number of maps; and (2) add any number of maps in #renderContentOn:.&nbsp;</div><div><br></div><div>Given that the map itself does not need any state, creating it as a component (I'm not familiar with the painter implications) seems to encourage reuse by inheritance rather than by delegation.&nbsp;</div> <div><br></div><div>I mention these issues to describe my understanding and invite help, not to argue. I agree that the distinctions are a bit subtle and I don't need there to be a clear "right" answer.</div> <div><br></div><font color="#888888"><div>James</div></font><div><div></div><div class="h5"><div><br><div><div>On Jun 18, 2009, at 6:13 PM, Julian Fitzell wrote:</div><br><blockquote type="cite">So, with the need to update the root, I think this would work equally well as a simple renderable object in 2.8 but it wouldn't really be be much better than a component or brush.<div> <br></div><div>With a 2.9 Painter, you can implement #updateRoot: on the Painter&nbsp;subclass&nbsp;itself&nbsp;so&nbsp;the&nbsp;map&nbsp;object&nbsp;would&nbsp;be&nbsp;able&nbsp;to&nbsp;encapsulate&nbsp;all&nbsp;of&nbsp;that.&nbsp;This&nbsp;does&nbsp;require,&nbsp;as&nbsp;you&nbsp;point&nbsp;out,&nbsp;that&nbsp;the&nbsp;Painter&nbsp;be&nbsp;added&nbsp;to&nbsp;#children,&nbsp;of&nbsp;course,&nbsp;but&nbsp;I&nbsp;think&nbsp;that's&nbsp;better&nbsp;than&nbsp;having&nbsp;to&nbsp;implement&nbsp;your&nbsp;own&nbsp;root&nbsp;updating&nbsp;behaviour&nbsp;and&nbsp;not&nbsp;unexpected&nbsp;for&nbsp;the&nbsp;user.</div> <div><br></div><div>Your&nbsp;renderContentOn:&nbsp;would&nbsp;look&nbsp;basically&nbsp;the&nbsp;same as your version with either a painter or a renderable object:</div><div><br></div><div>MyComponent&gt;&gt;#renderContentOn: html<br>&nbsp;&nbsp; &nbsp; &nbsp; html render: (GoogleMap new<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class: 'myMap';<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setCenter: 45.5267 @ -122.8390 zoom: 11;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "..."</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; yourself)<br><br></div><div>So I think the result is slightly better with a Painter in the sense of implementing #children, rather then update root behaviour.</div> <div><br></div><div>The other advantage is more theoretical and that is that a painter is not dependent on the encapsulating component using Canvas as its renderer. I say this is pretty theoretical because the only other renderer right now is the RSS one. :) From an architectural point of view, though, components are able to use any renderer they want and if they're using one that doesn't support your brush, well they couldn't easily use your implementation.</div> <div><br></div><div>This is all pretty subtle. Thus the debate. :)</div><div><br></div><div>Julian</div><div><br><div class="gmail_quote">On Thu, Jun 18, 2009 at 5:49 PM, James Foster <span dir="ltr">&lt;<a href="mailto:Smalltalk@jgfoster.net" target="_blank">Smalltalk@jgfoster.net</a>&gt;</span> wrote:<br> <blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Julian,<br> <br> I did the GoogleMaps stuff several months ago and was only comparing component and brush; I didn't consider a Painter mostly from lack of knowledge (and being on a 2.8 environment). I wasn't aware that there was a debate about when to use brushes; I thought it was just me who couldn't figure it out!<br> <br> I ended up with a Brush because it felt more like I was just defining a special &lt;div&gt; and I didn't want to require people to define a child component. I figured that until I needed something more complicated I'd stay with the simplest thing that could possibly work (tm), and the brush approach came together nicely. Once I started thinking about GoogleMaps as being little more than a fancy div/listbox/image, several things fell out quite cleanly. I discovered that I don't want to keep any state and that I want to treat the configuration as one does with other things. For example (where all of the messages to the brush are optional, but #setCenter:zoom: is most useful):<br> <br> MyComponent&gt;&gt;#renderContentOn: html<br> <br> &nbsp; &nbsp; &nbsp; &nbsp;html googleMap<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class: 'myMap';<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setCenter: 45.5267 @ -122.8390 zoom: 11;<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;enableGoogleBar;<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;addType: GMapType physical;<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;addControl: GControl largeMapControl;<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setUIToDefault;<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;on: 'zoomEnd' do: [:x :y :z |<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'alert("Zoom from ' , x printString , ' to ' , y printString ,<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;' (see GMUsingLatLong&gt;&gt;renderContentOn:)");'];<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;yourself.<br> <br> The only other requirement is that the component needs to allow some #updateRoot: behavior:<br> <br> MyComponent&gt;&gt;#updateRoot: anHtmlRoot<br> <br> &nbsp; &nbsp; &nbsp; &nbsp;super updateRoot: anHtmlRoot.<br> &nbsp; &nbsp; &nbsp; &nbsp;self updateRootWithGoogleMaps: anHtmlRoot.<br> <br> The implementation involves creating various scripts and feeding them out, but it works fine in the brush.<br> <br> GoogleMap&gt;&gt;#with: anObject<br> <br> &nbsp; &nbsp; &nbsp; &nbsp;self ensureId.<br> &nbsp; &nbsp; &nbsp; &nbsp;super with: [<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;anObject renderOn: canvas.<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;canvas html: self mapScript.<br> &nbsp; &nbsp; &nbsp; &nbsp;].<br> &nbsp; &nbsp; &nbsp; &nbsp;self addLoadScript: self variable , 'Init()'.<br> <br> Overall it seemed less intrusive for the library client to use a brush rather than a component. Other than #updateRoot:, there isn't really much to do. With a component there were so many things that were brush-like, including setting the class, id, style, etc., and more issues (does it need to be included as a child?). I was able to do so much without state, that it just seemed nice this way. It seemed like I got further than any of the other GoogleMaps packages I found, but I'd love to see another approach or get expert feedback on what I've done.<br> <br> In general, while I'm heavily involved in Seaside from the GemStone point-of-view, there is still a great deal for me to learn about when to use different parts of the framework. This exercise was another opportunity to learn and I did learn something!<br> <font color="#888888"> <br> James</font><div><div></div><div><br> <br> On Jun 18, 2009, at 5:15 PM, Julian Fitzell wrote:<br> <br> <blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> James,<br> <br> It's been a while since I looked at the google maps stuff so I don't recall... what do you actually render to the page? If memory serves, you only need to output stuff that can be generated by the standard brushes (divs and JS or something, right?) and you don't need to put content inside it.<br> <br> I have no memory of whether there is anything that requires a component to keep state but it doesn't *seem* at first glance to me like something that needs to be a brush either. I don't mean to suggest you're wrong since you've obviously gone through the exercise and I haven't but there's quite a bit of debate now and then over when to use brushes. I'm just wondering whether you considered a renderable object (a Painter in 2.9) as an option or just component/brush. And if you ruled out the third option, is there a particular reason you think a brush is more appropriate?<br> <br> Julian<br> <br> On Thu, Jun 18, 2009 at 4:00 PM, James Foster &lt;<a href="mailto:Smalltalk@jgfoster.net" target="_blank">Smalltalk@jgfoster.net</a>&gt; wrote:<br> Mariano,<br> <br> I'll be interested to see how this comes out. As I mentioned earlier, I started with a component and switched to a brush. I came to view the GoogleMap as a browser widget, something like a listbox, where you give it some data and let it draw itself. Yes, you can configure callbacks, but that isn't really different from other brushes. What sort of 'state' do you envision keeping with the map? Might that be better in a domain-specific component that wraps a map? I was able to implement over 40 examples and have not yet found a need to get more complex.<br> <br> James<br> <br> On Jun 18, 2009, at 2:58 PM, Mariano Montone wrote:<br> <br> <blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> Thanks Julian. I think a component will be ok.<br> <br> Mariano<br> <br> On Thu, Jun 18, 2009 at 1:22 PM, Julian Fitzell &lt;<a href="mailto:jfitzell@gmail.com" target="_blank">jfitzell@gmail.com</a>&gt; wrote:<br> Hi Mariano,<br> <br> Off the top of my head, if I were implementing a google maps package, I would do it as a component or a painter (see below). Brushes certainly aren't intended to be kept around so if you have state to persist between requests that's not the way to go.<br> <br> There are people who like implementing everything as brushes but the main functionality of brushes is that they can be selected in arbitrary orders to nest content within each other, e.g.:<br> <br> html div: [ html span: [ html paragraph: 'foo' ] ].<br> <br> Unless you plan to do be able to do:<br> <br> html div: [ html googleMap: [ html paragraph: 'foo' ] ]<br> <br> (i.e. unless the thing you are creating allows content to be put inside it) I don't think there's much advantage in making your own brush. (The other reason to consider using brushes of course is that they have more direct access to the document).<br> <br> Even if you don't need the benefits of components (see <a href="http://blog.fitzell.ca/2009/05/when-to-use-seaside-component.html" target="_blank">http://blog.fitzell.ca/2009/05/when-to-use-seaside-component.html</a>&nbsp;), you can just create a renderable object by implementing #renderOn: and do:<br> <br> html render: (GoogleMaps new configSomeStuff; yourself)<br> <br> This process is made much clearer in 2.9 where you can subclass WAPainter, implement #rendererClass to control what kind of renderer you get passed (you might possibly implement the google maps thing *using* one or more custom brushes and have your own renderer for them), and implement #renderContentOn: as you would for a component.<br> <br> Hopefully that makes things clearer and not muddier. :)<br> <br> Julian<br> <br> On Thu, Jun 18, 2009 at 5:51 AM, Mariano Montone &lt;<a href="mailto:marianomontone@gmail.com" target="_blank">marianomontone@gmail.com</a>&gt; wrote:<br> Hello!,<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; I'm implementing an API for rendering Google Maps. I've decided to implement it as a brush. That's because I'm just generating javascript code. But now I have a problem: when adding support for callbacks, I need to hold some state; for example, the map the callback refers to. But I think brushes are not meant to hold state, that is something left for the components mechanism, isn't it? So I would like to know what would be the correct way of implementing it in the framework. Should I implement maps as components, or should I add state to my brushes; I may hold a state in the callback block too, but I don't think that's good.<br> <br> Thanks!<br> <br> Mariano<br> <br> <br> <br> _______________________________________________<br> seaside mailing list<br> <a href="mailto:seaside@lists.squeakfoundation.org" target="_blank">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br> <br> <br> <br> _______________________________________________<br> seaside mailing list<br> <a href="mailto:seaside@lists.squeakfoundation.org" target="_blank">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br> <br> <br> _______________________________________________<br> seaside mailing list<br> <a href="mailto:seaside@lists.squeakfoundation.org" target="_blank">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br> </blockquote> <br> <br> _______________________________________________<br> seaside mailing list<br> <a href="mailto:seaside@lists.squeakfoundation.org" target="_blank">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br> <br> <br> _______________________________________________<br> seaside mailing list<br> <a href="mailto:seaside@lists.squeakfoundation.org" target="_blank">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br> </blockquote> <br> _______________________________________________<br> seaside mailing list<br> <a href="mailto:seaside@lists.squeakfoundation.org" target="_blank">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br> </div></div></blockquote></div><br></div> _______________________________________________<br>seaside mailing list<br><a href="mailto:seaside@lists.squeakfoundation.org" target="_blank">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br></blockquote></div><br></div></div></div></div><br>_______________________________________________<br> seaside mailing list<br> <a href="mailto:seaside@lists.squeakfoundation.org">seaside@lists.squeakfoundation.org</a><br> <a href="http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside" target="_blank">http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside</a><br> <br></blockquote></div><br></div> _______________________________________________<br>seaside mailing list<br><a href="mailto:seaside@lists.squeakfoundation.org">seaside@lists.squeakfoundation.org</a><br>http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside<br></blockquote></div><br></div></body></html>