Sunday, December 24, 2006
Wet and Dry UI
It's not because it's messy or that it takes a lot of time. It's because I have to figure out which one is the "wet" and the "dry" pad. The two pad system is very frustrating because even though I've been doing a lot, I still have trouble figuring it out.
It's best to show you what the two pad system looks like --
Which one is the wet and which one is the dry?
Well, if you look closely, the one on the right is the wet (actually, the resolution is horrible, but take my word for it. The wet one is on the right). Right before the French instructions, you'll see the words "Wet Wipe."
It's the same for the dry.
The problem with the "UI" is that the font is way too small -- for everything. Second, in the area where differential occurs (i.e. "Wet" and "Dry"), these areas look identical.
Where visual differences are required to provide distinction, it's best to make them stand out. Especially when you're relying on typography to guide the user, it's best to highlight the areas where you want your users to look (and act).
People look for visual clues to do things. By making the font properties identical (size, weight, color, etc.), you leave the impression that the two wipes have the same importance and that order doesn't matter when in fact they do.
The wet pad is on the right. If that pad is used first, shouldn't that pad be on the left? At least for the U.S., that makes sense.
When we design UI, we need to think about how we can make it easier for our users to do what they need to do. For the screen cleaning system, the most basic of that is letting the user know, "Hey, the pad on the left is the wet one. Use that first."
It's not to switch the order of things and make the font super tiny so that its difficult to read. Screen wipes are supposed to be easy to use.
Tuesday, December 19, 2006
Cool Blue Nile Sliders
One of the really cool things about Blue Nile is that they have some really innovative ways to filter data (in this case - diamonds). The filtering is asynchronous (AJAX), but it's their filtering control that we'll be looking at.
Here it is --
Here's the link to their filtering widget.
With the Bindows 2.5 or 3.0 Toolkit, we'll build a reusable component like what Blue Nile has. Here are a few of them built with our component -- FilterSlider --
The first thing we notice is that the Bindows library doesn't offer a slider with two thumbs. BiSlider only has one thumb, but we can set a property to make it a vertical or a horizontal slider.
What we'll do is take two of these vertical sliders and stack them on top of each other. We'll set the maximum and the minimum value to be the same. In fact, they'll be the same in almost everything (size, location, etc.), but the value. This way one of them is the top and the other is the bottom thumb.
The code looks complicated, but it really isn't. The sliders are placed inside a slider body which is nothing more than a BiComponent. Inside the FilterSlider, we also have two "odometers" which are BiLabels that display slider values. These change dynamically as the slider value changes.
Last but not least, we have a "topBg" and a "bottomBg." These are the backgrounds for the thumbs. That is, when we slide the thumb, the background expands and collapses depending on the direction that we're going.
The other important piece is how we select the thumb. Because the sliders are on top of each other, we have to first determine the position of the pointer and then from that determine which thumb we're on. We can't simply have the slider listen for the "change" event.
Whenever we dispatch a mousemove or a mousedown event in FilterSlider, we determine where we are. If we are over a thumb, we set that thumb to have a higher z-index than the other thumb.
The thumb with the higher z-index "wins" and so, that thumb listening for the change event (i.e. slide, maximum or minimum value change) will react.
With a little styling we get this.
The implementation is found here and the test driver is here. To get the images, just view the source and follow the links.
Feel free to use and improve on the code. Just let them know where you got it from.
Enjoy and Happy holidays!
Monday, December 11, 2006
Custom Bindows View Port
You might not have known the name, but if you've used Google or Yahoo! Maps, you're using this pattern.
The way that the pattern works is that the map is the detail part and the little view port located at the lower right or the upper left is the overview part of the pattern. You use this pattern when there is a large amount of spatial information, but you can't see it all. So, thru the view port, you navigate to specific parts of the data.
Jenifer has a great description of it here.
As of this writing (I am using Bindows 2.5), Bindows doesn't have a BiViewPort, but we can easily create one. We'll use it to navigate a randomly generated graph created from the Bindows graphical API.
Let's look at the design details.
First, let's decide how large we want our virtual view area to be. The virtual area is the place where we'll put the graph. Let's make it 5000x5000 pixels. Obviously, we won't be able to see the graph in its entirety (at least with any meaning; besides, how many people have monitors capable of displaying a graph that size!), but we'll be able to see a portion of the graph as wide and as high as the screen.
Then, we'll need a way to navigate the virtual area. This is where the overview window comes in. We'll need an overview window and then a little viewport to navigate the virtual area.
Basically, the overview window is the virtual area in miniature. Likewise, the viewport is a little miniature "screen." Because they're miniatures, we have to properly scale them so they look and react proportionally just like the real thing.
The overview window and the viewport are pretty simple. They're just BiComponents. The viewport is added to the overview window. Here's the code --
Notice that we've hardcoded a size to the overview window (ex. overview.setSize(200, 200)). This is fine as long as the viewport is properly scaled based on the overview and the graph size. This is what we're doing with the function getMultiple() --
The function returns the number of times that the graph is bigger than the overview. We use the multiple to scale down the viewport like this --
Now, here comes the really important part. We need to take care of navigation. This means that we need to move the viewport and once we're done moving the view port, we move the graph.
For the first part, the viewport listens for the dragstart event. Once that event fires, a number of things happen --
- The viewport is made movable (i.e. added as BiMoveHandle)
- The movable component listens for the moveend event
When the move ends, we take the location of the viewport and then scale up to match the position of the virtual area so that the screen can display the same nodes found in the viewport. Here's what all this looks like --
The linkMgr is the BiLinkManager. In other words, it's the graph container. We're moving this entire component which contains all the elements that make up a graph (nodes and edges).
We're done!
One thing you'll notice is that I didn't talk about creating graphs. The folks at Bindows have some terrific tutorials, examples and documentation on that. You can also review my source.
When you do, you'll also note that I use two additional components which I didn't mention -- originalPos and navWinInfo. The first one is just a BiComponent which marks the position that the viewport was at (that's the gray box which you'll see in the prototype). The second one is just a BiLabel which displays current xy coordinates.
You can find the prototype here (be sure and maximize your browser - F11). Feel free to study and use the source. If you do, let them know where you got it.
Enjoy and have fun!
Monday, November 27, 2006
Creating a Google Gadget Tips-Like Widget :: Part 2 :: Making It Reusable
If you missed the first part, you can find it here.
In this second part, we'll take what we've done and make it so that we can create any number of components with something simple like this --
To start, we need to think about what the developer will use the component for. Here are a number of things that he'll want do with our custom component (let's call this the VerticalTabComponent) --
- Use their own grabber
- Give the grab label a completely different name
- Customize content in the tab pane
- Control background (and foreground) color
- Replace the default close button with something more snazzy
- Etc.
So, armed with that information let's derive VerticalTabComponent from BiComponent.
Basically, we need to put the members and functions from BiComponent into the prototype scope of VerticalTabComponent. We'll also need to call the BiComponent constructor (function) on behalf of the derived component like this --
We'll also expose a few privileged functions (these are like public functions but have access to private members) --
Notice the granularity of the functions. Most of them return derivatives of BiComponent rather than objects like String (i.e. the tab button label, etc.). As we noted in the first part, "BiComponent is by far the most useful component in Bindows."
Of course, much of what we expose to the developer is based on how much we wish to abstract. For example, look at these two functions --
Why on earth would we want to set the background color this way when we can use the power of CSS?
As it turns out in this case, we do want to hide some of the details. When we set the background color for the VerticalTabComponent, what we're really doing is setting the background color for an internal container. This is something that the developer shouldn't worry about.
You'll also notice that we only expose functions. We didn't expose any of the member variables. We want to control what the developer has access to and directly grabbing member variables is not what we want.
We've also removed almost all of the inline styling and placed them in an external CSS file. Bindows uses the concept of themes and to use that we need to define an appearance which is nothing more than a selector rule found in the CSS file within the theme.
So, we'll define the appearance and then make sure that the selector rule is the same name as the appearance. Here's one example of how we create appearances. This one is for the internal container --
Within the theme.css file, we'll add these selector rules --
If you look in the VerticalTabComponent, we did keep a few inline styles --
We did this to styles that change the state of the component that are independent of the theme.
In general, when we make a reusable component, we should do our best to move the styles out of the code and into an external file. In essence, what we are telling the users of our component is that these selector rules are free for you to modify.
Lastly, we'll have a few event handlers to deal with behaviors like dragging, mouseovers and mouseouts --
We're now ready to create the component and with a single line of code we have magic --
If we wanted to create a look different from the default "Google", that too is easy, but a bit wordy. Here's the code for a "Meebo" (http://www.meebo.com/) like look --
That's all there is to it. If you're interested in the example, the implementation is found here.
You can download the entire sample here (including the VerticalTabComponent). Of course, you'll need the Bindows JavaScript Toolkit. My example uses Bindows 2.5, but any version of Bindows should work. Have fun!
Friday, November 10, 2006
Creating a Google Gadget Tips-Like Widget
During the installation, the Desktop installed Google Gadgets and the "tips" dialog --
Basically, the dialog is a series of help screens accessed by mousing over the vertical tabs. You really can't put too much information in each pane, but I thought that the vertical tabs were unique and worth imitating for a web application.
To do that, I'll use the Bindows JavaScript toolkit. Originally created by Erik Arvidsson, it's an incredibly complete and powerful DHTML/Ajax framework for building web applications. It's definitely not the only one around (i.e. Dojo, Tibco's General Interface, etc.), but I use it everyday and I'm pretty familiar with it.
(For this post, I'm not going to go into the basics of how to build a Bindows application. Yoram Meraiz and his Team at MB Technologies have great tutorials and a lot of examples to get you started.)
Whenever I "imitate" or prototype something, the first thing I do is look at the behavior of the thing. All the visualizations (i.e. the "pretty" things) come last. So, from the Google's help dialog, we know that our model --
- Will listen for events (i.e. mouseover a tab and the pane changes)
- Can be dragged (another event is dispatched)
- Can be closed by clicking a button
- Has text for each tab and the title of the dialog
- Has images for content in each tab pane
- To listen for events, we will need a BiComponent
- To drag the dialog, we will need a BiComponent
- To close the dialog, we will need a BiButton (a derivative of BiComponent)
- For the titles, we will need a bunch of BiLabels (another derivative of BiComponent)
- For the content in the tab pane, we will need a bunch of BiImages (another derivative of BiComponent) as well
It's a pretty good bet that we'll use BiComponents.
The BiComponent is by far the most useful component in Bindows for a number of reasons --
- A BiComponent understands events. In other words, a BiComponent listens for and can dispatch events
- A BiComponent can add other BiComponents. This means that they can function as a group (i.e. they can be dragged, hidden together, etc.)
- A BiComponent can be styled inline (naughty, naughty) or by selector rules (i.e. via a CSS file or in Bindows parlance, a theme)
Let's take a look at how we use the BiComponent to imitate the Google help dialog.
First, we'll take advantage of the fact that we can add components together. So, we start out by creating a container to hold all the other parts of the dialog. Not surprisingly, the container is a BiComponent --
Then, we look at what should be in the container. The dialog's label and grabber area are no brainers. We'll use a BiLabel and a BiImage for those and add them to the container --
The same goes for the close button. It's easy --
Now, let's look at the tab button and the tab pane. They're related in that when you mouseover the button, the content changes in the pane. So, what we'll do is create another container that holds the tab button, the tab pane and the content.
We will repeat this for each tab (there are five). Of course, we'll need to add this to our container. One benefit of doing it this way, is that we can add all the tab components and then they can be positioned together in one shot.
Note that each tab is already built up. In other words, we're not going to build them up when they are needed (i.e. when a mouseover occurs). We'll just use the power of CSS and style them so that they do not display if they're not moused over --
For now, the code uses a lot of inline styles and as an early prototype, that's fine. Right now, what we're after is to imitate behavior and look and not the reuse or maintainence of the component. Eventually, when we take this prototype and make it a reusable component/widget, we'll worry about that.
Let's look at how we handle the behavior for the tabs. When we mouseover the tab button, a number of things happen --
The text in the tab button is bolded The tab pane appears with the new content The previously selected tab is no longer bolded and its content is no longer displayed
Here it is translated into code for one of the tabs --
The last behavior is dragging the dialog and doing that in Bindows is easy. We just add the container to a BiMoveHandle component --
We've omitted the close button, but essentially it listens for the onclick event and in our case, simply displays an alert box that says "Clicked!"
That's it. What we end up with is our implementation and you can view the full source here.
In an upcoming entry, I'll address how we can turn this prototype into something reusable so that you can use it in your own code (we'll also get rid of the inline styling too).