Showing posts with label Panel. Show all posts
Showing posts with label Panel. Show all posts

Thursday, May 6, 2010

Quick and Easy Scalable Rounded Bevels

Too Much Photoshop
I was watching a UI design video yesterday where the presenter imported a Photoshop design into Blend and turned it into a Silverlight GUI. One thing I noticed was that he kept a lot of the raster layers from the Photoshop image as graphical elements inside the Silverlight app. If you have a carefully crafted design that uses filters and effects that aren't available in Blend then you have no choice if you want to retain the fidelity of the design mock-up. That usually means, though, that the Silverlight app is going to be at a fixed width/height to match the Photoshop design - since raster graphics don't usually scale well.

I don't like fixed width and height Silverlight apps. I kept thinking "there must be a way to do that in nothing but XAML". Some of the graphics were understandably left as raster since there would be no equivalent way to achieve the same result in Blend. But the elements that bothered me were usually simple panels or buttons that had a common visual theme - they had rounded corners, and they had some kind of bevel that made them appear raised or lowered. This is the sort of thing I mean:


Let's pretend it was a proof done in Photoshop (it was actually an old copy of Paint Shop Pro) and we brought it into Blend. The gradients are easy enough to do in Blend, but what about the shading around the edges of the buttons and panel, giving them a raised appearance?

A lot of the button styles that I've seen people blog about tend to follow 2 or 3 themes: glassy styles with inner glows and highlights; ellipses with radial gradients; or linear gradients. These buttons are either all elliptical buttons, or they don't bother trying to give rounded shading at the edges like the image above.

The problem is the corners. If it weren't for the rounded corners on those rectangles, you could just use thin rectangles hard up against each edge with an appropriate linear gradient. But even then you run into difficulty at the corners where those rectangles meet. There's no easy way to make the gradients from each edge's rectangle meet smoothly.

You could probably do it on a fixed size button with paths shaped specifically for the corners and edges, but then it wouldn't scale well.

This got me thinking about how to achieve the kind of look in the top image, in a way that scales to any kind of size, or roundedness. While the method described below doesn't have the flexibility of the filters and effects that you find in Photoshop, it is a fairly good approximation, and is easy to achieve.

Fuzzy Borders
The approach I came up with is to use Border controls with two sides missing, and the Blur effect applied to make it fuzzy. Here is a working example:


The idea is to use a Border control with a White BorderBrush, the top and left edge thickness set to 3, and the bottom and right edge thicknesses set to 0. Then add another Border control, but with a Black BorderBrush, and the opposite edges showing. The blur effect is set at 4 and the borders are offset by 1 (using the Margin) from the edges. The Opacity of the borders is down at about 30%. That's it - just two borders on top of whatever background you want. And it scales to any size.

The example above uses the same technique for the buttons and for the panel containing the bigger buttons. The only difference in the object tree for the buttons is that there are a couple of extra rectangles to manage States (eg MouseOver, Disabled etc). The buttons swap the colors from black to white and vice-versa for the MouseDown state, and offsets the ContentPresenter element too.

Here are the object trees for the panel and for the button:



You can see that I have copied the elements from the button template into the grid on the right to be used as a panel (should have renamed the element "PanelBorder" instead of "ButtonBorder").

You could also use this as a Control Template for a ContentControl and have a nice reusable 3D panel style.

Here are the files in case you want to play with it.

Sunday, March 14, 2010

A Scalable Orb Panel-Button-Thingy

I think I may have mentioned before that I'm not much of an artist - thankfully the internet is chock-full of inspirational ideas. When browsing around some Photoshop tutorial sites, I came across an avatar of enzudesign on a forum; the avatar looks like this.

That's a cool looking button. So credit where credit's due - enzudesign is a talented artist. I wanted to try create something similar in Silverlight that would work as a button, following the same kind of process that I used for the Radio Button I did recently. This one has a LOT more content to it. My first attempt at it required 14 ellipses; 11 of those requiring a blur effect. It also meant containing those ellipses in about 18 proportional columns and rows. It looked nice, but performed poorly. I suspect all those blurs were chewing through a lot of CPU. I don't know how much CPU effort it takes to update those proportional values into real values when the whole thing is animating, but I'm guessing it contributed to the poor performance. This is what it looked like with and without the blur effects:


The blur gives the chrome surrounding ring a rounder appearance, and makes the black look more "rubbery". In comparison, the image without the blur looks stark. I guess this is quite subjective, but to my eyes, the blurred button looks nicer. But it's performance is terrible. I turned it into a button template and the transitions from Normal to MouseOver were not smooth at all. I've included a (collapsed) copy of the above design in the download. So it looked great but it had to lose some detail.

The result, after trimming it down, is embedded below, but I wanted to do more with it than just use it as a button. It occurred to me that this design would make a nice border for a panel; cut it into quarters and you have the corners. All it would need is filling in some horizontal and vertical banding to join the corners. "All it would need" actually turned out to be somewhat fiddly, but before I bore you with the details, the effect I was after needed to have two features:
  1. The button itself would scale nicely, keeping proportion where it needed to
  2. The transition from round orb to rounded panel should be as seamless as possible.
Here is what the "finished" product looks like (download link at the bottom of the post):


The button now consists of only 10 ellipses; 4 of those with blur effects. I won't go through the whole process of compositing the button since that was covered here. But I will quickly cover the process I used of turning an ellipse into a rounded panel.

Setting up the Panel Grid and Corners

I wanted a panel where the corners stay the same and, as the panel grows or shrinks, the "filler" between the corners would expand or contract to fill the gap. The first thing to do was to work out how to get just the appropriate part of each corner to show. I played around with a few options such as using a clipping path, an opacity mask, or using the Path | Subtract tool to convert the ellipses into just the quarter that was needed. These approaches all had their own problems: I don't like clipping paths because they are high-maintenance to animate in storyboards; the Opacity Mask has similar problems since it essentially just a brush that covers the whole object; using the Path | Subtract tool gave the right shape, but the blur effects and the gradients were now applied to just that quarter and proved too fiddly to work with. This was compounded by the fact that the button is not symmetrical across all four quarters.

In the end I discovered that if I confined the whole thing into a grid cell, but sized it so that it was twice as wide and twice as high as the cell, then the Grid automatically clipped out everything except the bit that fitted in the cell. So the here is what the containing grid looks like with the four corners in place:


The first and last columns, and the first and last rows, are each set to a fixed width/height (in this case 50 pixels). The middle columns and rows have their width/height set to "1*" to take up all the gap between the fixed rows and columns. Each corner has an entire copy of the "button" shapes sized to 100x100 pixels, and has it's horizontal/vertical alignment set to the appropriate values for each corner.

Although this provides the corners that I need, I'm not entirely happy with this approach because I can't easily animate the column width and height of the corner cells if I want the radius of the corners to change. In the embedded Silverlight app above, it is the button itself that is changing radius - not the panel grid. I would like to come back and address this at a later date to work out how to achieve this more easily. I have in mind that creating a custom control with template parts for the corners and horizontal fills would work - I'll have to experiment when I get the time.

The Fillers

The next stage was to create the fills between the corners and in the middle. For the top and bottom fills, this was achieved by copying the whole button structure, changing the ellipses into rectangles, and removing the columns from the containing grid to make the shapes span the whole grid. I had to change into the XAML view to change the ellipse tags to rectangle tags.

The resulting grid is sized the same as the corner shapes (100 pixels high) and restricted to the middle top grid cell to clip out the bottom half.

The same result is copied and pasted for the bottom fill and placed in the middle bottom cell.

The vertical fills are done essentially the same except there were a couple of areas that were a bit trickier because the button is asymmetrical vertically. I was able to remove a few of the shapes for the vertical fill because it's mostly just black between the chrome ring and the blue orb center. The trickiest part was the center blue part of the orb. The bit that goes from the the left side of the blue center to the right side of the blue center. For the other rectangles I just tweaked the existing gradients, but for the center rectangle I created the gradient from scratch using the color picker tool to get a selection of colors across the width of it.

The last part of the grid was straight forward, it is just a rectangle with a solid color fill. It stretches horizontally and vertically to fill the middle space.

The only other thing to point out is that I had to set the margins of the horizontal and vertical fill edges that met the corners to -3 so that there was no obvious seam. If you look hard you can still make out where the parts meet, but I don't think you would notice them unless you were specifically looking for them.

The Animation

The animation is somewhat of a cheat. It looks like the button shrinks, and then expands into a panel containing the buttons. There is actually two groups of controls here: the button is shrinking down in size, but then it's opacity animates to 0 to reveal the panel behind it (in exactly the same location). It is then the panel that grows to reveal the fillers, and then displays the buttons inside it. The whole process is reversed when the panel is closed.

I really like the end result, but I would like to come back and revisit this later to make the whole thing easier to replicate with a different design.

Download, including source, from here.