>> WEBBER: Morning everybody. I'm sorry this is so early. We're all kind of in pain from
that, I think, after last night's party. My name is Joel Webber, this is Ray Ryan, both
from the GWT team. And we're going to talk a little bit about what we are now referring
to as our UI overhaul. Don't forget there's the live wave, good place to ask questions.
We'll try to pick questions up at the end from there as well as from the microphones.
And you can also heckle us virtually from there and we'll find out about it later.
>> RYAN: I hope you know how to run that thing. >> WEBBER: Yeah, I think I do.
>> RYAN: All right. >> WEBBER: I hope. So what do we mean by this
UI overhaul? This is not really a formal process. This is really just a set of things we've
been doing recently and been in 2.0 and things that we are doing as of 2.1 which we're working
on presently that constitute a lot of major changes in a way UIs are built in GWT. It's
not a replacement for what's there for the most part but a lot of additions and optimizations
and new bits and pieces. Really four things, you get UiBinder, if you're working in--the
first three, if you're working in 2.0 right now, you're probably at least passingly familiar
with these things; UiBinder, a way to move a lot of your code into XML which sometimes
can generate quite a savings in complexity; ClientBundle, the way to process CSS, one
of the biggest paying points I found in GWT Apps; LayoutPanels, where they cut the collection
of panels that make--that will hopefully make most of your layout problems much, much simpler
to solve. And for these first three, you can see an update to the mail sample as a 2.0,
uses all of these features. It's a really good starting point. They take nothing else
away. Go look at the way that's built and that tells you a lot of things about--a lot
about the things that we changed in 2.0. And then finally, these things we're referring
to is either data presentation Widgets or Cell Widgets depending on the phase of the
moon that we are adding in 2.1, which will solve the problem, I think once and for all,
of building large collections efficiently. So large tables, large trees, large lists,
things like that. And I'm going to let Ray take it and talk about the UiBinder and ClientBundle.
Those are what his babies. >> RYAN: Hi. I'm still Ray and...
>> Louder. >> RYAN: Hi.
>> WEBBER:Whoa. >> RYAN: Okay.
>> WEBBER: I think that turned up at both ends.
>> RYAN: Okay. So you can hear me now? Excellent. I'm Ray and I'm going to talk to you about
UiBinder and its best friend ClientBundle. Like Joel said, these are features that are
in GWT 2.0 now, had been for a few months, you can use them today. So UiBinder is a way
for you to write your HTML User Interfaces in HTML, which is a pretty natural way to
go. It gives you a lot of help in getting rid of the kind of Boilerplate that you face
when you're dealing with writing [INDISTINCT] or interfaces in Java, anonymous event classes,
and that kind of thing. You were into them a lot less often. And it also encourages you
to write your Apps in a style that's kind of inadvertently lead you to write them in
a more efficient and more performant manner. You'll use fewer widgets except where they're
appropriate. You'll wind up with an app with a smaller footprint. That is not an accident
to coin a phrase. We're trying to guide you into the pit of success where the easy and
pleasant way to work is also the correct way to work. So, here's how you write UI in Java.
You're not really expected to be able to read this. And even if you could read it, I doubt
you'd have a very clear idea of what was going to be in front of your users after this code
and all run. We're kind of making some panels, and some images, and I think there's some
text in there and we're going to stick it up. And it's just a nuisance to write and
nuisance to maintain. As of GWT 2.0, you can write it in a style that you, more or less,
would have written it, had you been working in a browser in a conventional system. We're
inside of a bunch of HTML. We got some divs. Looks like there's going to be a welcome message
and a signOutLink and an aboutLink and some serious set styling going on but, as a Web
developer, you probably understand what's in front of you here. So let's actually make
this thing do something. First, you want to look at the two anchor lines in bold there.
Those are a couple of GWT widgets that basically correspond to your old friend "a href=" da,
da, da, da, da, da. And you see the attribute on them called UiField. That is telling you
the name that--and I turn my head, I talk louder, neat. That's the name that your Java
code will use to get access to these fields to actually make them do something interesting.
So we'll jump over to the Java code that actually maintains this. Up at the top, you'll see
we have our UiField annotation declaring that the signOutLink and the aboutLink need to
get pressed to us from that XML file that we talked about. And our constructor, we're
sub-classing GWT composite here which if you're a good developer, you're probably familiar
with. And the only line of code we write to actually create this interface is a call to
the create and bind method on our binder and we pass it in to our net method and it--widget
method. And for event handling, we're just doing some more annotation. We're saying that's
the signOutLink. When it generates a click event, it should call this method. The aboutLink,
when it generates a click event, it should call this method. No anonymous classes, none
of that nonsense, just make your UI, make your code, and get on to the actual interesting
part. Now, one of the other--man... >> Hello.
>> RYAN: Hello. One of the other best features that shipped with GWT 2.0 was the ClientBundle.
You may have heard Webber talked about this at previous I/O conventions first as the FooBundle
then as the ImmutableResourceBundle, which is my personal favorite version of its name.
We finally settled on ClientBundle and we actually shipped the thing. ClientBundle is
a way to take all of the other stuff that makes up your App. The styling, the commands
in CSS, image resources, any text, any other arbitrary resource types that you want to
customize the thing to run and bake that right into your JavaScript. So, A, you have a lot
less work to do to figure out how you're actually going to stage this somewhere to pull it in
with later requests from you App; B, your users benefit hugely because they have a single
download that brings in everything at once rather than multiple network request going
over to fetch this little image and that little image and the other little image. We use every
trick that we can find; spriting to tie all images together into a single download; data
resources to bake things in, in a single download where possible. We know what browser you're
compiling to so we'll use the tricks that work for that browser, but not for this browser,
and so on. Oh, and one key thing to point out is that we don't just take CSS. We make
the CSS work a lot more like an engineering tool. There's no global name issues. If I
have a style class called main in widget A and my friend over here have made a class
called main in widget B, we don't stomp on each other and we didn't have to agree on
a convention of having a 24 character long, this, that, that about the other style name.
We have conditionals and we have namely constants and stuff like that. I'm not go into that
fun to see a success resource because it's been discussed here before. But look in the
docks. It's fun. The way it shows up in UiBinder is with an XML element called UiStyle where
I'll declare familiar looking CSS like the main div and this guy should have a background
color or text color of red. And I'll apply it in my usual styles at the class attribute.
The only bit of magic here are the squarely braces inside the class attribute to indicate--this
isn't a string that I'm putting here, this is an actual reference to another part of
the template. In this case, it--there's a reference to the style block and the main
class that was declared inside of there. And you're not typically going to actually put
all of your CSS inline in your template like that. It's nice for sketching, but you won't
normally work that way. And that's fine. You can punch it out into a CSS file on the side,
several CSS files on the side; maybe share some of those CSS files; that kind of a thing.
It's not just for play. And of course, when you're working on interfaces, you don't just
do styling. You like to use pictures as well and we've got you covered there. What I'm
doing here is I'm indicating to the template that there's an image around here somewhere
that I want to work with. I'm going to put it in a field, remember our friend UiField
named background image. I'm going to assume that the image file matches the field name,
and so I don't have to make a separate attribute for background image.png or whatever. We'll
just look around. We'll assume that you've made things sensible, although if you need
to get more explicit, you can. And finally in this case, I'm going to mention that when
this image is used that it's in a context where repeating is necessary. I want to re-tile
itself in a horizontal and in a vertical, both axes. I can now go back down to my CSS
in the style block there and use a bit of the CSS magic we've added. The "@sprite" marker
there indicates that this particular CSS class is to indicate spriting. It's going to use
this GWT image specified up above as background image. And now finally, when I go down to
my Hello world spam, if I apply that background style that I've declared here, I'll have my
nice tasteful image background which, in my case, tends to be a picture of my son at age
two, but I'm not sure that would be today. Finally, the trick that UiBinder knows is
it ties it nicely to a GWT's internalization system. Yet that's not new, our ability to
have localized messages and constants and so on. What is new is this ability to kind
of work with it directly in your code. All I've changed here is I've added this UI message
element around the existing Hello world message. And what I've done is I've signaled the GWT
system that this is a localizable strength. When the code is compiled, we're going to
generate on the side for you a properties file that has all of these messages baked
into it in the usual key equals Hello world style, a file that you can hand to your translators
so that they can in turn provide alternative languages and you can--and away you can go.
But an interest to full disclosure, I should mention that the early users of GWT 2.0--oh,
I'm sorry. This is a slide I just added. We can do more than just simple little strings.
I wanted to point out here that we can have bits of things that aren't message tucked
into the middle of the message because of the wrapping, this can be hard to read but
what's going on here is we have a message we're going to show our users that says, "To
do the thing, click here," and that's going to be a link that fires off a widget of some
kind and massage vigorously. On the bottom here, you can see what gets generated and
put in front of your translators. They get two little placeholders there, the 0 and the
1 to let them know that there's a bit of opacity here that they can't mess with when they translate
it. The tokens have to still show up here. And they can--but they can translate the message
with all of the context and not breaking it up into three separate substrings and hoping
that somehow things get pieced together for them. I've got the entire message placed there.
There's actually other attribute to comply on UI message to give them more of a notion
of what's going on, to provide a description for it, and so on. The docks go into some
detail. Now, for the full disclosure part, once you've actually compiled the system finding
the bits and putting them all together and getting it in front of your users work well
enough for Google's use. We didn't quite get the level of [INDISTINCT] that we should have
to get--make it easier for you to use with your system. I was so embarrassed by this
while writing these slides that I finally updated our public documentation to make it
really clear how to do that pulling in a lot of input from a particular bug report that
I saw on my sleep. So it's all been working for a while and now you actually have a hope
of making it work yourself. And that's all I had to say about UiBinder and ClientBundle.
I'm going to handoff to Joel now who wrote all the other interesting stuff and let him
talk about it. >> WEBBER: So layout, if you've been using
GWT for awhile, you probably noticed that layout is charitably a nightmare. That's true
of a lot of HTML in each UIs. And we really have two different approaches in the old way
of doing things which we'll now be the old way of doing things. You can either try and
use tables, put everything where you want it to go, which I think there's been a large
complaint about the [INDISTINCT] core widgets making at things like dock panel, horizontal
panel, et cetera, et cetera. Worked well for awhile but it doesn't work so well when it
get into standards mode. And people hate it because it makes CSS more of a pain and so
forth. There's an alternative which is to try and lay everything out manually. One pixel
at a time, top to bottom, all encode. And there are a number of frameworks to take that
approach and it works, except that it's kind of slow. And for a lot of the apps that we're
working on, it's just not sufficient. You end up with apps that are really, really slow
to resize, you know, you have to resizing draggable panels within the app, they're really
slow, and so forth and so on. We don't want apps to be out of the box sluggish. And if
you want to just [INDISTINCT] and make your apps sluggish, that's fine. But we don't want
to force you to do that. So we came up with an alternative that actually turns out there's
a nice little constraint system hiding inside CSS that people don't really use all that
often, probably a number of reasons for that. It doesn't work out of the box on some older
versions of some browsers. But it turns out by using left, top, right, bottom, width,
and height, those six properties on top of position absolute in which--please don't react
when I say position absolute with these actual constraints that works out fairly well, not
as bad as it sounds. You actually get something much more like the "springs & struts" model
that you have and something like Cocoa or the old next UIs. Those worked very well in
practice. So what we built is a series of widgets, a series of panels, containers that
use this--they all use the same structure. There's a core layout system they all share.
And the purpose of these things is to support application level layout. In other words,
there are some things that HTML and CSS layout models do very well. We should continue using
HTML and CSS layout for those things. But for the core screen application structure,
topper over here, footer over here, scrollable panel over here, splitter over there, the
top level of constructs in your app, these are very, very effective and much more--both
easier to use, more predictable, and more efficient than what you get out of source
standard CSS layout techniques. So all of them in standards mode, in fact, they require
standards mode but that shouldn't be a problem for most people since [INDISTINCT] mode is
mostly a nightmare. And they support animations right out of the box. There's actually a good
reason for doing that because it allows us to build certain things that would otherwise
be very difficult just with the simple constraint system of animation sort of layered on top.
I'll show a few examples of that in a few minutes. It also allows us to do things that
are more efficient out of the box than you might do by hand unless you're willing to
go dig very deeply into some [INDISTINCT] that's required. And they do actually support
browsers all the way back to IE6 with some Herculean hackering, but hacker that you don't
need to know about, unless you're really, really masochistic to want to read the code.
What are these things not? I mentioned this before. If it works well on HTML right now,
don't use layout panels. That's not the right place to use them. So forms, document style,
layouts, most of your widgets will not actually be used in the structure, just the core screen
layout. And it's also a simpler layout when you're maybe used to it, if you're familiar
with swing or SWT or these other sort of top-down and bottom-up merged layout models where there's
a negotiation between the outside and the inside. This is much simpler. Again, if you're
familiar with Cocoa and Interface builders, it's much closer to what you get up there.
So let me just show you. It's a lot easier for me to show you than to talk about it.
Okay, this is the GWT 2.0 mail sample. You may have noticed this, you may not have noticed
there is much to notice about it, but it was different in any significant way. But there
are some subtle and important differences. So the first, we see that. And this window's
a little bit large. Sorry about that. We will see that it is fast to resize. Now, not only
is this fast to resize, it's fast to resize on browsers like Firefox that defer layout.
So, if you notice on window resize events on Firefox are deferred. I think Opera does
the same thing. It's still smooth. So you won't have this weird horrible structure that
happens on some apps, even some Google Apps that you resize the window and the whole thing
lags and the UI feels sluggish and weird and flaky. It's very, very solid. It's very, very
fast in that respect. I mentioned animation earlier. So these kinds of transitions are
really, really easy to get out of the box and they're fast and they're smooth. You never
have to worry about how well I nested the dock panel inside the split panel inside a
stack panel and it jumped upside down and flipped over and crashed. None of that sort
thing happens. This is all straight--it's very straightforward. It's a coherent model
and it definitely works out of the box everywhere. And there's no code here to speak of. You
set up the structure, even the window resize event has handle it automatically and internally.
So you don't have to think about things like, "Okay, I'm going to write some code here to
figure out when the resize is and I've got a header up here so I need to measure that
and subtract 37 pixels, blah, blah, blah, blah, et cetera." And that's it, I mean, that's
the idea. These are things that should be simple and traditionally UI [INDISTINCT],
they're very straightforward. And GWT and/or the Web, they have always been kind of a nightmare,
no longer. I hope. It seems to be working well so far. So what was actually going on
in that? This is the--this is a rough fact simile of some of the mail.
>> RYAN: Actually, this is the exact copy-paste of the "mail.ui.xml" file.
>> WEBBER: Okay. This is not a rough fact simile. This is actually the "mail.ui.xml"
file. So what are we looking at here? We got a DockLayoutPanel. That's something that put
some things at north, west, you know, just like the dock panel if you use that before
in the past. I put a splitter inside the center region and that splitter contains shortcuts
on the left-hand side. That's the stack panels and all that stuff, MailList, MailDetails,
so forth and so on. So it's straightforward to use this way. If you set things--if you
set them up using these structures, it will more or less, just do exactly what you want
it to, which is sort of the goal we've always have. Okay, what does it not give you? It's
not going to give you, you know, all unicorns and rainbows. There are some constraints.
It's not sensitive to preferred size. This is probably the biggest complaint, but it's
also the most intractable. And this is much like, you see--you'll see in a lot of interface
builder Apps. You kind of have to give these things the space they need. You can't say,
for example, make this top section as tall as the HTML inside at once to push out, it's
top-down only. And that's actually another good reason to say this is really only meant
for application core screen super structure. You can--it require explicit sizes on a lot
of places, like you may have noticed on this case here, DockLayoutPanel--the outer DockLayoutPanel
specifies that it's in EM units. Those are font-relative of units. And the north is five
EMs tall. Good thing about being able to use things like EMs and percentages is that you
can make the layout fairly fluid. EMs make you font size-relative which is really convenient
and percentages make you container size-relative. So you can get a lot of things done that way
even without a preferred size negotiation. You do get resize events. This is another
point of contention of the browser the way it's typically done.
qwer
>> WEBBER: ...you do get resize resize events. This is another point of contention in the
browser the way it's typically done. With the exception of Internet Explorer, nobody
provides an on resize event. It's really unfortunate because if you've ever had to do something
like, you have a list of icons and you want to make it--you only want to render enough
for the space that you have available in some panel, there's no way to find out when the
thing gets resized in the general case. We provide a limited series of resize events
that percolate through the layout panel structure. This seems to work out pretty well for most
reasonably use-cases, but there are some things who are--that are a little tricky to do. And
as I said before, it's not appropriate [INDISTINCT] during layout. You probably don't want to
use this to layout your forms. At least not at the leaves--not near the leaves, you know,
typical form, HTML form layout structure seemed to work pretty well for that kind of thing.
>> RYAN: So one more point to make here before we enter cellar cells. Although we require
some explicit sizes, you want to ay attention to the fact that in the--the main body here,
the center elements, there's no constraint provided there. We're requiring you to nail
down the margins. The body is going to take up what's available after that. And so, the
part that needs to be flexible is very flexible. >> WEBBER: Thanks Ray. I have a bit of underselling
myself sometimes. Okay, but what you get out of it? So you have some constraints you have
to conform to, but it's extremely fast, as I mentioned, because we're not running any
code, all right? One of my favorite quote is "The fastest code is the code you never
run," and we don't run any code in the default case. May well--okay, almost no code, some
small epsilon there. It handles a resize event to check a few things, but that's it. Everything
else leans in the browser's native code which is already running very quickly. Again, you
don't have to write a window resize listener and it's actually not leaning on the resize
listener to do most of the work. So that keeps it fast and saves you a lot of trouble. If
you use "em", "ex" font sizes--sorry--unit sizes, then you can react to font size changes
automatically. So, you don't have to worry about, "Well, I made this thing 37 pixels,
so very wide and it looks great, until my user decides to magnify the screen." That
sort of thing is handled automatically. Again, animation support is built-in because it allows
you to do things like touch that panel where it's not--it's normally animating from, you
know--from here to there, specified in absolute positions. It's actually animating among constraints.
So, this thing was constraining to be top height. I animated it and now it's constrained
to a different set of constraints that are bottom height, for example. If you play with
it, that will make sense. And again, they're predictable. They do exactly what you want
them to do. They go exactly where you tell them to. They don't bounce around on you.
And another point, it's not a bullet point in the slide, but another point about performance
is that because of the way they're constructed, they also make a number of other things faster.
They tend to constrain layout, and when I say layout. And when I say layout, I mean,
if you Kelly Norton's talk yesterday about layout performance--a number of things, layout
performance is among them. You know that browser layout--the reflow engine can be extremely
expensive. These things tend to compartmentalize your layout. So, a lot of things--a lot of
little things just get faster because they don't affect each other. Something that when
your header over here, changing doesn't cause the browser to reflow this part over here
that's completely unrelated. That a little--a subtle but really helpful benefit. So, you
got everything on the screen. You got it
actually where you want it to go for once, and without any major hackery. Probably one
of the biggest complaints we've had after layout is, "Okay, I got my app looking great.
I just put, you know, 100 table rows and five columns or 10 columns, 20 columns wide. And
it's excruciatingly slow. How do I fix this?" And we often said, "Well, you probably shouldn't
be using that many widgets. You got a widget in every single table cell here, that's really
expensive." And you say, reasonably, "Well, what should I do about that?" And we say,
"Uhh..." we scratch our heads and we come up with some strange hacks that involve HTML
and catching events at a high level and trying to pump them down and you find yourself in
a world possibly worst than the one you were hoping to escape in JavaScript. So we want
to fix that. We've wanted to fix this for a long time. And as of 2.1 when this is released
and obviously you can play with it right now. We feel like we have a pretty good solution.
We have the underpinnings of a really good solution to a lot of these problems. So, again,
we've been giving this advice often, don't use so many widgets. Why is that? Widgets
are necessarily more expensive than individual DOM elements because they simply have a little
extra book keeping. This is necessary for dealing with events properly, it's necessary
for cleaning up after potential memory leaks on some browsers and so forth and so on. That
if you don't--in a place where you don't use a widget, it will necessarily be faster because
there's no code running. What we're aiming to do is to let you get the benefits in this
and the performance of doing everything with giant fact inner HTML calls without the excruciating
details of trying to hook up events and keep track of all the elements that you've created.
And if you've looked at--if you've use the tables in the [INDISTINCT] that shipped in,
well, 1.0 ages ago, you'd noticed--probably somewhere around there, 1.1 maybe. We added
the ability to add widgets to everything because it was necessary for certain cases. But that
actually was a little bit under constrained. So, if you ever have tried to put a tab panel
inside a tree item, probably not. Or if you had, it was on a LARK. Definitely it wasn't
a good idea if you did. And it, even if it behaved properly, it was probably very, very
slow. What we need is something more constrained than that. Again, that's at lists, tables,
and trees. These are the kinds of things, what we need large numbers of the same thing
over and over again are very simple things, but you need them to be interactive. And--but
that interactivity tends to be limited, buttons, check boxes, text inputs, date pickers, you
know, just sort of standard editable table stuff has been a nightmare before. Then these,
you know, these one comes a surprise to anyone, but these are the kinds of things we're talking
about. We've got a--never mind that the word table and trees got reversed today. I'm going
to--I'm going to blame presently on that. >> RYAN: That was my fault. Surprise.
>> WEBBER: I'm going to blame Ray. I like blaming him presently though because he can't
defend itself. So, of the top we have a tree, that's straight out of the average Geo picker.
That is a very, very large tree because it includes every region in the world, every
region, city, et cetera, et cetera. But they needed a check box in each one because that's
the point. The point is selecting parts of a large tree. This is a really common case.
That actually is built out of widgets in the current day they may have optimized it by
hand. That should be really easy to do. Tree down there--table down there, I can't read,
thank you Ray. >> RYAN: They both start with T. I don't see
what you're so worked up about. >> WEBBER: So--oh, thanks for fixing that
demo too. I had not made point it out that thing was interactive. So, there we have a
table out of the AdWords UI, if you have ever used that, the campaign management UI. These
include a couple of really subtlety interactive thing and it's actually more interactive than
you can see here because it has a lot of hover effects and so forth. Again, the original
implementation used a lot of widgets and it was very, very slow. They fixed that by optimizing
by hand, we're taking a lot of the lessons they've learned and brought it into this framework.
And then the list here, this is really a good example of a thing. This is really a list
of simple things, but they're, I mean they're fairly complex. They're not super interactive.
But you need to be able render large numbers on them that comes out of way obviously. So,
let me show you an example of we've done. You may have seen this in the keynote. I promise
I'm not on wireless this time. So that should be a less of a problem. Okay, so this is not
a super exciting app unless you happen to be an accountant perhaps. I doubt many of
you are accountants. What's exciting about it is, to me anyway, being the performance
freak that I am, is that there are almost no widgets in this app, but it was really
easy to construct.
qwer
>> WEBBER: ...but it was really easy to construct. Sorry?
>> RYAN: I don't say a word. >> WEBBER: Okay. I'm hearing things now, wonderful.
Probably, I think on the order on 25 widgets. The last time I check, it's 25, 26 something
like that. And they're for things like, you know, the search box here, the super structure.
Again, this is using the same layout panels I showed earlier. But when it comes down to
individual pieces here, even when they're interactive in fairly complex ways, they are
just cells, which is a term I'll introduce to you in a moment. Table is the same way.
This, by the way, were some of the animation stuff we built-in in the layout panels. This
is--This was--I think this is five or six lines of codes. But here, we have--so we got
a table. Oops, if I can click the right thing with an interactive element, that also is
a cell. Those are efficient. They're very, very fast to render. Importantly, I forgot.
I should remember the number here but last time, one of my partners in crime measures
the speed... >> RYAN: [INDISTINCT] bazillion, I think,
was the term that we were using. >> WEBBER: Oh I was actually going to refer--I'll
be looking for smaller number. I was going to say, how long does it take to actually
render this table. And I think it's on the order of a few milliseconds. That's all backend
time right there. And what that allows you to do is crazy things like [INDISTINCT] the
other day morning where I can do a type of head search. You know, that is a really simple
implementation on top of what's already there. And that's even fast on old crafty slow browsers
because it's leaning on an HTML to get its work on. So the point is that of all this
is that you can do something that's easy to do. You can create your--create these widgets.
They're very straightforward. So I use these widgets to create individual cells to render
the contents. It's easy to do, but it ends up being really fast because the interface
is designed in such a way that you--that give us enough leverage to be able to leverage
innerHTML and so forth to get fast rendering, but still keep them interactive. We have very
simple idea of how this works. This is the simplest example I could think of. I got a
cell list of simple stackedview, got a simple text cell, the simplest thing you can impossibly
imagine rendering. So just give me a string, put it in the cell. And that parameterizes
the cell list that has--that gives the cell list a way of rendering itself. And I add
the view to an adaptor. The adaptor is really just a convenience class that pushes data
into it. When you sit down to use it, I think you'll find that it's really quite straightforward.
And in that adaptor, this actually behaves sort of like a glazed list if you ever use
that sort of library where anything I do in that Java util list is just reflected automatically
in the UI. And the cell list itself will go to great links to make sure that that gets
optimized. So if I change a few things, it will try to render the subset that get changed.
If I change a whole bunch of things at once, it'll try to render them all together very
quickly. It's, you know, again, designed to make sure that that is easy for us to do.
We have the right APIs so we have that kind of leverage. No pop quiz on this. I just wanted
to give a quick overview of the kinds of thing we're talking about. This is not a really
complex framework. It's really a small set of components that are very easy to use together
but don't require you to buy and do any complicated MVP architecture or anything like that, but
are design to work well with anyone. So basically, you have views, essentially mostly listviews,
tables or special case of list, entries, or nested list, so a list that contain list.
So it's really a small set of concepts there. You have an adaptor here where, again, which
is not a special thing. It's just a convenience class providing list on the client to the
table or to the view. I have a cell for rendering the contents and for dealing with events.
So cells can handle events but in very limited ways that allow you to build most of what
you want to build but don't require any heavyweight architecture, heavyweight bookkeeping work.
And then your app code, controller, or whatever you want to call it gets notification of updates.
So the way that cells are made editable is that cells provide updates to their content,
so each cell is bound to a type. So that allows--that type could be a string or that type could
be a built-in, an enum, or a complex DTO. It could be anything you want it to be. Again,
it's much easier to just dig into the code and just trying to read that diagram, but
I just wanted to give a rough overview. So I think I've already talked to everything
on the slide, almost. Oh, async data source is actually--that's actually another core
really, really in core part of the structure. It is designed from the ground up to deal
with asynchronous fetches--a synchronous slow fetches because we are talking about the distributed
network that is the Web. It is invariably slower than you want it to be when you're
over the Internet. And it can handle push data. So as long as you can find a way to
push data down to the client, maybe you're running an HTML5 App and you're just using
Web sockets or something really clever like that, or you're just pulling in the background.
The views don't have to know about this. You can always push new data into them and that
allows us to do all kinds of crazy things like--well, you saw the automatic update and
the keynote demo yesterday. You know, that sort of thing is very easy to do and there's
no hackery to make that work. And when we started this, we really found that there weren'tmany
frameworks that were designed around the asynchrony that is common in the Web. Most of them tended
to be--tended to assume that the models backing up or get the list or reflection were synchronously
available which requires you to do all sort of extra levels of caching and things like
that. So we really set up to make that simple from a get-go. If it's slow and it's async,
I can still--the UI is very, very simple to build. And we also wanted to keep simple stuff
simple. All right. If I've got a list on the client, then I should just be able to shove
it into a view without any extra work and shouldn't be--shouldn't require me to instantiate
three or four adaptor classes and a few other complicated thing. It should be very, very
simple. I want to talk a little bit about the cell itself. Cell is actually a very,
very simple concept. It's a flyweight--roughly a flyweight pattern. They tend to be stateless,
except in some rare cases. And this is the simplest cell you could possibly write. So
basically, the purpose of a cell is to take some value, turn it into HTML via string builder
and then get out of the way. It does very little alts. I think what's not shown here
is the event handling structure which is still in a little bit of flux but it basically allows
cells to handle events but in a stateless way so that they could communicate edits to
the outside world. But again, we've just driven really to keep that very simple and we'll
provide a lot of cells out of the box so that most common cases will be dealt with easily.
And--wow, did we really just make it all the way to the end?
>> RYAN: We did. >> WEBBER: Every practice, we ran 15 minutes
over. I can't really--we have lots of time for questions...
>> RYAN: He's not even talking fast. I don't know what we did.
>> WEBBER: So, four things, ClientBundle, let you actually process all the things that
weren't processing before, images, CSS really makes your life simpler in a lot of cases.
UiBinder, get rid of Boilerplate, [INDISTINCT] and HTML which makes your life easier hopefully.
And again, as Ray said before, just drive it as the pit of success, make the easy thing
the fast thing so that the App you want to write is the one you want to end up with.
And layout, make layout work, make it fast, make it easy, make it predictable so you can
stop worrying about it and just move on and write you App. And again, these Cell Widgets,
make it fast and efficient and simple and easy to render large collections, again, so
you can stop worrying about--hopefully, you stop worrying about UI performance
problems and worry about your actual app. And I'm still stunned we have this much time.
It's great. So, again, there is the live wave. There is, hopefully, there are some questions
on it which we'll switch to in a moment. And this link may even work if we're lucky, the
thing on where Ray assigned you. >> RYAN: Oop, I'm sorry. I forgot about that
bit of magic. Yeah. Please standby. >> WEBBER: Cool. You're about to login at
your own account. >> RYAN: Oh, so I am.
>> WEBBER: So, why don't we start with audience questions while we figure that out? [INDISTINCT].
>> Hello. >> WEBBER: Does it work?
>> Mic does work. >> WEBBER: I think so.
>> Hi. I was hoping if you guys could quickly explain how I could create my own widgets
in the UiBinder. I mean, I understand that there's a parser that you will have to overwrite
if it is--if it's not so. >> RYAN: Ninety percent of widgets work right
out of the box. If your widget works in a bean style--you can mess with that, I'll answer
this while I answer this. >> WEBBER: All right.
>> RYAN: If you write your widget in a bean style, zero args constructor, setters and
getters then it'll just go. Every attribute that you set in the XML element will result
in a set call. We've recognized a lot of the primitive types out of the box. So if you
put--if I have set height takes an int argument, I'm going to make sure that the user provides
you with an actual int literal and we'll throw appropriate exceptions if they don't.
>> Right. >> RYAN: As of the GWT 2.1 release, we'll
even show you the line numbers on the on the error messages that we generate them which
was going over sight. You have--and even with those constraints, you've got back doors,
like for example, if you're--we need to work with a widget that does have constructor arguments,
you've got the UI factory annotation so let you indicate a method that's responsible for
instantiating these things, so you can take care of that encode. You can actually annotate
the constructor itself saying I want the arguments on this constructor to be available as attributes
that the user can set from the markup, and so on. There are cases where we have to rely
on customized parsing. We haven't yet opened up--we haven't made that customized parsing
stuff pluggable. It's rare that it's actually necessary. We will get to ramping that at
some point but we've managed to get away without doing so this long because most people don't
ask for it. >> Okay. Thank you.
>> RYAN: Mm-hmm... Okay. So you want to do one mic, one mic, and pick from the screen?
>> WEBBER: Sure. Here's I'll give you one screen here. Wow, that's a lot of percent
20s in that question. He probably spends way too much time running URLs. Let's see. Let
me see if I can read this. Are there any plans to add more example documentation based around
the UI overhaul? Yes, absolutely, absolutely. So one of the things that I really wanted
to do in 2.0 but fell behind on, and I'm working on it right now is a new sample we're calling
the Cookbook for one of the better term. It will have a lot of necessarily called recipes,
but we're really trying--really trying to cover all the cases that people are actually
running into and we're accumulating lots of questions, bug reports, discussions on fora
and so forth that point to a lot of common cases where people get stuck. We're going
to try to encode those all on the sample. So rather than have a sample that shoots a
tour of what's there, it's--the idea is more to cover all of the things that you would
actually want to do and practice so that hopefully, you can just go find one of those, copy and
paste that out, then tweak it to your heart's content. So we definitely recognized that
there is a bit of a failing on in terms of the guidance provided by samples and that's
definitely something we're working on. That already is checked into the....
qwer
>> WEBBER: ...that's definitely something we're working on. That already has checked
in to the Byte Shed code, by the way, in the [INDISTINCT], sort of [INDISTINCT] you look
at it, it is actually there. It's a little sketchy right now or a little bit of a rough
sketch but we'll keep iterating on it in there. >> RYAN: Go ahead.
>> Hey, there, I've used the duct panel layout before and I really liked the way that the
north and the south stayed put on the top and the bottom of the browser and then the
center panel is expanded to fit the remaining space. But in the used case of having the
header and the footer at the bottom of the page where you want the scroll bar to be the
entire page, do you have any plans to make a layout panel like that?
>> WEBBER: There's not a formal layout panel that handles that out of the box, but it is
actually pretty easy to construct. I'll give you a very rough idea, and that's actually
one of the samples that I'm putting in to Cookbook because so that's came up a few times.
What you do there--is there's the base layout that has--it allows you to specify pretty
much any kinds of constraints you can imagine, a sort of an--it allows you to pretty much
build anything but it's a little bit like brain surgery. Well, maybe, a dietary or something,
not like brain surgery. What you would essentially do is add something that--add one panel, one
child that's constrained to be top, left, right bottom zero that is all pinned to the
edges, that has the scroll bar. And then anything you want to float on top of that, you just--you
can constrain it to be actually on top of it. So, for example, header and footer would
be, you know; top, zero; height, 5m, just something like that; and then bottom, zero;
height, 5m with the footer. The underlying panels that had a scroll bar within it, would
still be scrolled underneath that, so it covered the whole--it would cover the whole screen,
and it would look as though, where the document level scroll. It's not actually the document
level scroll, it's actually just sitting underneath it but it gives you the same effect. And it
actually ends up being a lot more efficient when scrolling the entire document because
things like pop-ups don't have to be moved to cache when you scroll the document around.
>> Okay, thank you. >> WEBBER: Does that maker sense?
>> Yeah, the header moves with that scrolling, right. The header doesn't stay at the top.
>> WEBBER: Right and it looks like it's moving with the scrolling but it's actually just
sitting on top of it. >> Got you.
>> WEBBER: But it has the same effect and it's faster. Go for it.
>> Thanks, Joel. >> WEBBER: Sure. All right, better theme.
Who's--who was going to work on the better theme? John is that you? No, we a--we definitely
are looking into creating much better themes out of the batch right now, they're pretty
weak, we recognized that. In 1.5, we had themes that were not and quite as incredibly bad
as the ones in very first release. But there are--there's a lot more work that we're doing
on that front. And we've recruited UI designers internally to help out with that process.
We're also looking at ways to--because CSS resource, UI binder and all this stuff is
fairly new, we haven't actually reworked all the way that we just work yet. This is also
the incumbent complaint, so it's hard to mix these with sort of traditional way to--perhaps,
sort of two-styling mechanisms. So after [INDISTINCT] some I/O, we all [INDISTINCT] for a few days.
We're going to be looking at creating themes that can actually can be used with CSS resource
to gain the efficiency benefits from that but still be flexible and still allow you
to pick and choose how you want to play on different widgets and so forth. And as part
of that process we will de-uglify them as well. So, hopefully that would be helpful.
>> RYAN: Go ahead. >> Could you talk about the division of labor
on UiBinder templates? So, I could imagine somebody with HTML, CMSs skills starting off
with something at HTML CMS and hand it over to a GWT program who converts it or the GWT
program that does the whole thing or you go back and forth. Could you talk about the tradeoffs?
>> RYAN: We've seen all those things happen. It's very common. The common point is that
you usually have a designer working at the prototype in HTML, and then they throw it
over the wall to the engineer. I think the web microprocessors were giving enough that
you can get something that renders pretty quickly and then work over to try to factor
things in the widgets that should with it and so on. I know of at least one team, and
I hope that they're not an exception. They had actually a lot of luck with setting up
their designer with the host of development mode build of their product talking to a mock
backend so that he could actually work. And they thought him enough eclipse to get it
to start and type in the files and mess with the stuff, and hit the refresh button on their
browser with thicken of backend that things refreshed and rebuilt quickly, and they actually
have a quite awhile of success with that. I've heard of other counter examples where
designers got very frustrated at dealing with--they're used to looking in Firefox and using Firebug
and messing with the CSS names there and we given these horrible mangled [INDISTINCT]
names. One way to make that more tolerable is to find the configuration option; I don't
remember that at the top of my head, to turn off, at least tone down [INDISTINCT] of the
CSS names. We can't just leave them completely unmodified because--then we lose the name
spacing. And all of the sudden those collisions I talked about start to happen and your app
goes up. But you can still at least read the things. I personally--when I'm messing with
it, well, I'll tend to experiment with inline styles on the specific elements and then go
back to my code to a--or my templates to put the successful experiments on the class names,
that kind of thing. We want to make that better for designers.
>> WEBBER: Yeah. That's still in the very much brainstorming phase. I mean making the--especially
the Firebug, dev tools, loop closer--close that loop. A couple of other things, I want
to mention, you mentioned, dead mode setup for designer. A way that I've actually seen
that worked really well is you don't even have to install eclipse for them because the...
>> RYAN: Push up. >> WEBBER: If you give them the browser plug-in
and just give them the URL to point at, you can run several clients off of dev but simultaneously.
If you set up a machine where they can work to get the refresh cycle going, and they--people
have used text involving and shared directories, and things like that. So, they're editing
one file but they don't actually even know--have know about eclipse. And by the way, in case
you're, I'm sure, at least somebody in this room is thinking, that's great but dead mode
refreshes really slow. We absolutely know that and there are people in Atlanta right
now working very diligently on making that less true. So, hopefully that will also help
that process. Are there any--just plans to add a designer functionality to plug in, it
significantly speed up more in tasks. We are not, to my knowledge, planning on actually
building designer our self into the equipments plug-in. There is at least one really good
one from instantiations that a--it's been a lot--it's been a little while since I've
used it But they are actually out in the Sandbox demonstrating it right now. Those guys are
much better at GUI builders than we are. So, we prefer to let the experts do there thing.
That would be the best recommendation that I'm aware of for doing--for getting a sort
drag and drop functionality. >> RYAN: Go ahead.
>> I played with Roo yesterday and had it generate a--kind of scuffled that application
and noticed that it was using some other things besides what you've covered, that seemed almost
a little bit more of an MVP framework, a little--some more framework level components, like place
and some other class that I haven't heard of before.
>> RYAN: I'll be talking about that in depth this afternoon at 3:30.
>> Okay. >> RYAN: And while you're looking at that
why don't you start to bump into the works and the missing creatures, please understand
that it's very much a work in progress. There are probably whole layers of abstraction that
will go away and will certainly change radically. >> Yeah, it just--it looked interesting like
you guys are providing a little more framework to get an app running.
>> RYAN: We are definitely working on an application framework and that will be part of the 2.1.
>> How about new data binding API? We have a data binding API.
>> RYAN: That is also part of the application framework.
>> Okay, I'll trust you on that. So come to the talk at 3:30 and/or watch it on YouTube.
>> So you mentioned that much of the speed of the data presentation widgets in 2.1 comes
from taking the thing and making it a giant set in your HTML. Right now, I'm using Grid
and FlexTable, and there's a thing called the setText, which specifically does set in
our texts... >> WEBBER: Right.
>> ...so that won't render HTML on the screen. Is there going to be something similar to
that for the data presentation widgets? >> WEBBER: So, yes and no. Essentially out
of the box--not exactly, but it should be really easy to get the same effect. So when
you saw that list example I setup before, there was a--this is the simple case where
I just have data on my client. I want to get it into the widget fast. I don't want to involve
some data fetching layer or anything like that. The structure there that we're settling
on is essentially like glazed lists where you can--you have--you have one adaptor class
to re-instantiate that binds to the table and the lists in tables are more or less the
same structure; they're just telling definitions that break fields out into columns. And you
would simply be operating on, say, a Java Util list instead of a--like a setText call,
but the effect should be the same. But the effect is the same, but if you make a hundred
calls to it to set up lots of rows, the widget will optimize that automatically. So, you
don't have the effect where the amount of data you're changing really affects the speed
of you're app very much in a really significant way. So, essentially yes but slightly different.
>> Thank you. >> WEBBER: Sure. Ah, let's see, use templates
in UI binder. I'm not quite sure how to parse that question.
>> RYAN: You can only use templates in UiBinder. >> WEBBER: That sounds good.
>> RYAN: I've tried to understand the question. >> WEBBER: [INDISTINCT]. Hopefully that was
clear enough. >> RYAN: Okay. And I think over there.
>> Yeah. Could you talk a little bit about the limitations and the considerations you
have--you need to have in mind for developing for mobile devices, specifically for Android
and iPhone. I have to run through some hoops just to get the scroll is in the swipe motion.
It seems to be not straightforward, so could you talk about any limitations?
>> WEBBER: Sorry, the--what's the part about scrolling again?
>> Scrolling, you know, if you just swipe your finger and get the scrolling effect.
>> WEBBER: Right. >> I have to run through some hoops, it wasn't
straightforward. >> WEBBER: Uh-hmm, it's true. So, when it
comes to supporting mobile devices, we really have two problems, I guess. I mean, ignoring--just
keeping your apps small enough to start efficiently and so forth which is more of just a scale
issue. One is--again, your rendering has to be fast enough that populating individual
pages is not slow, that's part of what we've designed these data presentation widgets to
solve. On mobile, we found--we've measured that even filling in a fairly large list is--could
measured in milliseconds in terms about the time it takes. But there are lot of APIs we
currently had not route. And that is some--that is a gap we have to close in the near future.
So things you mentioned like swipe gestures and so forth, those are things that for which
there are built-in events. But we simply haven't brought an interface to them yet. And we're
looking, probably in the third quarter, so to be focusing on closing the gap on a lot
of these things. There is also the issue, subtle issue but it's also the issue of scrolling
an interior area. So, you've noticed most, not all but the vast majority of mobile web
apps will scroll the entire screen. In other words, they just let things flow naturally
and that's where you have to scroll the whole thing. And if you've seen for example, the
mobile Gmail App, they do some crazy hacks to work around the problem that you need,
say, an archive button that stays available on the screen. So one of these things will
sort of float around and try to follow you. What we have found is that, with the newest
releases of all the mobile browsers, later iPhone releases, later Android releases, it's
become possible to actually synthesized the need of scrolling behavior and still keeps
a headers and footers and then swipe animations and all of that. Actually, Apple really showed
the way on that with their--if you look at the iPhone help app, they have pretty much
duplicated the native functionality in JavaScript. And we're looking to--we just begun actually
doing the same thing in our own code. >> Just a follow up question, how does the
Gmail do it? Because it seems to do it really well.
>> WEBBER: My understanding of what Gmail is doing, and it's been a little while since
I've used the mobile version, since I got my Android phone, I believe what they are
doing is, they're using regular document level scroll, but then when you--when it settles
down, the little floaty bar just eventually follows you. So, it waits a minute to catch
up, so it's not constantly interfering with the UI and slowing things down but then it
slides back into place. You can do that sort of thing but I think, ultimately, the better
solution is to actually make it behave more like a native App, because that what everybody
really wants. That's what I really want anyway so.
>> RYAN: Cross-site widgets. Cross-site widget; what's a better way to add them to the page
UI without using a div element ID key to locate the injection points.
>> WEBBER: I would ask that you follow up with the question on the users group. I'm
not quite sure I understand the question. And, well, the new Cookbook--I'll just jump
to the next really quickly because it's easy. Will the new Cookbook contain an event bus
example? I certainly hope so. That would be Ryan's job to do though.
>> RYAN: I'm the... >> WEBBER: [INDISTINCT]
>> RYAN: Roo is generating an event plus example for you, right now. And it will be generating
a better event bus example for you before we shift, then again, I'll talk about that
some at 3:30. >> My question is about infinite scrolling.
Yesterday, you mentioned it with the bit of a brain surgery, can you comment, how would
you proceed with this surgery? How would you do it? And also, in what you have right now,
as I understand this adaptor, you put data there, you're supposed to put all the data
at once and then it... >> WEBBER: Right.
>> ...like everything on your page, like candid elements or div elements.
>> WEBBER: Exactly. So that adaptor really--just to be really clear. I think, I probably wasn't
as clear about this as I should have been, that adaptor class was a convenience class
or when you do want to just punch large amounts of data into the widget directly and have
it just sit there. The general--the more general interface, the listview delegates to something
that asks, the list you keep track of a range. So for paging, for example, just a simple
case, it says, I'm looking at items 37 to 230 rate. The adaptor or the thing that is
providing data to the listview is responsible for making sure it gives that data. That's
sort of the asynchronous nature of it. For paging, I think it's pretty clear how that
would work. For the infinite scrolling or sort of on demand rendering, it's the same
idea except that the range is more fluid. I have some samples sort half-baked on my
desktop right now that implement this and it actually works really, really well in practice
even the basic data sources. So, for example, one of the Cookbook samples I want to create,
once I get the widgets in place is a Flickr browser. And I found--I've been able to flick--browse
through thousands of icons, thousands and thousands of icons really smoothly, really
efficiently and without having to fetch data in advance. So, look for those, probably in
the Byte Shed and the first--in the near future and a...
>> RYAN: The Expenses App, is that used the list [INDISTINCT] or did you do a customized...
>> WEBBER: the Expenses App has a custom adaptor that does handle the async data source.
>> RYAN: And the app is like a generated by [INDISTINCT]. So don't use the listview adapter.
It's really quite trivial to use your own if that's the appropriate thing to do. The
listview adapter is a convenience when you've got list to run.
>> WEBBER: They'll be sent out of the box for handling simple cases. For slightly less
simple and made after case but, you know, simple backend data source cases.
>> Thank you. >> We're using the paging scroll table and
the incubator. How is your new table related to that? Is it complete rewrite? Is the interface
completely different? >> WEBBER: It is a mostly complete re-write
but we've taken a lot of lessons learned from that table, from that implementation, we learned
a lot of good things and we learned a lot of bad things in the process.
>> Can you say, what's some of the weaknesses of the incubator one was?
>> WEBBER: Ah, lets see, and the guy who wrote it just left. So, I'll have to bug him later
but... >> RYAN: I remember it being hard to customize,
when we kept bringing stuff in there. It was difficult to make it fast. You had...
>> WEBBER: Right, actually... >> RYAN: [INDISTINCT] actually get bulk rendering
to work... >> WEBBER: Right.
>> RYAN: Rather than--but when we did that, that's like, all we do now.
>> WEBBER: Right, and the interface--exactly, so the things like bulk rendering were these
weird--I mean even the term is kind of odd and awkward. There are these--sort of weird
bolt-on cases and we really sort of flipped the problem around and made it--made the efficient
thing the easy thing rather than something you do after you've gotten it working and
then find out that it's slow. It also doesn't try--there are certain things that that table
tried to do, that we've just later on proven, especially with the help of Speed Tracer team,
are just physically impossible to do it efficiently in the browser. So it tried to do things where
it would adjust column sizes to the size of the contents and so forth, and they just never
really worked very well for those cases. And we found like, it really caused the AdWords
team no sense of--no end of trouble. So, what we're trying to do is extract the use-cases.
And I think we're mostly there at this point. So, extract the use-cases that we know can
be made to work well. So, we don't try to send you down to this primrose path and find
out later that you've ended up in a place where you just can't get a fast app. We hoped
the transition move really straightforward and if it's--to the extent that it's not,
hopefully we can. We'll try to provide samples to make it clear how to move from one to the
other. There will be a number of teams within Google as well who are in the same situation.
Sir? >> Yeah, so I think the cell stuff is really
interesting in terms of, you know, getting away from widgets. I'm not at all by means
an expert, but I thought the widgets were really interesting because there's--like one
of their jobs is handle all the cross browser of idiosyncrasies, So when you start doing
in like cells. And all of a sudden, they have input, you know, boxes in them and all of
those that are just like really native input boxes--is all of a sudden --is it going to
be easier to just build apps with no widgets at all? Like what, like--even your search
boxes. You said, "Well, the search box is a widget." But if you're doing input boxes
in cells, can't we just do input boxes anywhere that aren't widgets basically?
>> WEBBER: Right. What I actually see happening there is--well one, I see that we will provide
a lot of these cells out of the box that already handle a lot of the stuff for you. So, and
if there--if there are cross-browser issues that sit on top of--higher than the down level,
they will still be doing some work to take it--to deal with those kinds of situations.
But the other thing that I see happening is--so the cell on its own can't handle events. It
needs help from something to do that. And that's where a lot of the deficiency comes
from, but that has to move into the container. What I would like to see happen is that we
see a series of widgets come into being, or in some cases, just mutating the ones that
are there so that they have a cell at their core and they provide the feature--the facilities
that the container would otherwise provide. So if you need to use a select box and a regular
widget context or a form or something like that, you can still do that just as easily
but at sharing the same infrastructure. This is the way a lot of, more traditionally, UI
toolkits work as well if in fact the term cell, we stole from Cocoa essentially, we
stole it from--I've forgotten whom. But hopefully, that will provide more of a coherent stack
and a kind of smooth transition from, you know, if I got it here in a widget, I probably
got here in the cell as well that's appropriate. So you can mix and match more easily but still
not deal with cross-browser issues... >> RYAN: Looking at the--sorry.
>> WEBBER: Okay. >> RYAN: Looking at the clock in the back,
I think we're out of time. >> I just--do you have a timeframe on when
that might happen? >> Well--actually, they're probably the best
there. Everybody's behind so we're going to run outside and be available right out the
front door if that's okay. >> RYAN: Thank you.
>> WEBBER: Thanks everybody.