Thursday, January 31, 2008

Audio Capture Component?

It seems to me like this would be a very handy component. If you don't know what I mean by it, let me explain. What I want to build is an application that runs on a tablet PC but sometimes requires the user to enter extensive notes. I am trying to avoid requiring anyone to use the keyboard to enter this information. What I would like is a way for the user to speak the notes, capture it as an audio file, and store it on the file system and eventually back on the server. I know FMS would allow me to do it but since this will be an AIR application used by people "in the field", they may not always have a connection back to the server at the time of the recording which is why I need to store it on the file system.

If this doesn't already exist, it might be my next pet project.

Thursday, January 24, 2008

What Kuler Desktop should be

First of all let me say that I think Kuler is one of the, forgive the pun, coolest Flex apps I have seen. Kuler desktop however, leaves a lot to be desired. It seems to me that Kuler is a perfect example of an app that should be a desktop application that can be occasionally connected to the internet to retrieve published color schemes. Its main functionality, the creation of color schemes, does not require you to be online.

When I first read about Kuler desktop I had high hopes but was dissappointed. So, I set out to create something that I could use offline. What I came up with is this:



This doesn't do everything that Kuler does obviously but it serves my need for a way to create color schemes plus additional tints and shades. You simply pick your colors using the HSB sliders. If you want to link the hues of your colors, you can click the small link icon which shows up when you hover a large color swatch. Once linked, changes to the hue of the main color are mirrored in the linked colors. I'm not sure linking only the hue is enough. What do you think? Once linked, should change to saturation or brightness also be mirrored?

To get the HEX code for any swatch, including the tints and shades, simply right click and choose "Copy HEX code" to copy the code to your clipboard.

There are some additional features in the works and the current version may be buggy. Feel free to try it out by clicking here (or try out the badge installer on the right) and let me know what you think.

Wednesday, January 16, 2008

Flex 3 Programmatic skinning gotcha

I recently came across what seemed like a bug in ProgrammaticSkin. It turned out not to be a bug, but it was a confusing API. Anyway, what I was trying to do was create a skin that can be used for buttons that would be a bit more interesting than the built in border skin.

So I fired up Illustrator and figured out what I wanted. What I needed to do was create a vertical gradient and then overlay a partially transparent horizontal gradient on top of it. Sounds easy enough. However what I tried to do didn't work. Can you spot why?

var hm:Matrix = horizontalGradientMatrix(0, 0, w, h);
var vm:Matrix = verticalGradientMatrix(0, 0, w, h);

var fillColors:Array = [bgColor,
ColorUtil.adjustBrightness(bgColor,
brightnessAdjustment),
bgColor];

g.beginGradientFill("linear", fillColors,
[1, 1, 1], [0, 127, 255], hm);

g.drawRect(b.left, b.top, w, h);
g.endFill();


fillColors = [0xffffff,
0xffffff,
bgColor,
bgColor];

var hilightStop:Number =
Math.min(shininess * 126, 126);

g.beginGradientFill("linear",
fillColors, [.20, .20, .20, .20],
[0, hilightStop, 127, 255], vm);

g.drawRect(b.left, b.top, w, h);
g.endFill();


I looked at this for quite a while before realizing why both gradients were vertical. It turns out that horizontalGradientMatrix() and verticalGradientMatrix() don't create and return a Matrix. What it does, is modify its own instance of a Matrix and then return it. So you end up with hm an vm both referencing the same Matrix object. Since verticalGradientMatrix() was called last, that instance was a vertical gradient.

The solution is to call horizontalGradientMatrix(), do what you need with the horizontal one, then call verticalGradientMatrix() to get the vertical one. So the code above becomes this.

var m:Matrix = horizontalGradientMatrix(0, 0, w, h);

var fillColors:Array = [bgColor,
ColorUtil.adjustBrightness(bgColor,
brightnessAdjustment),
bgColor];

g.beginGradientFill("linear", fillColors,
[1, 1, 1], [0, 127, 255], m);

g.drawRect(b.left, b.top, w, h);
g.endFill();

m = verticalGradientMatrix(0, 0, w, h);

fillColors = [0xffffff,
0xffffff,
bgColor,
bgColor];

var hilightStop:Number =
Math.min(shininess * 126, 126);

g.beginGradientFill("linear",
fillColors, [.20, .20, .20, .20],
[0, hilightStop, 127, 255], m);

g.drawRect(b.left, b.top, w, h);
g.endFill();


The result is now exact what I want. Notice the subtle glow achieved by the slightly lighter center portion of the button.

Sunday, January 13, 2008

What's your title?

I am frequently asked what I do for a living and have found that when I reply with my title, "Principal User Interface Engineer", most people ask "yeah... but what do you do?". So let me ask all of you a question.

If you are someone who is responsible for designing what software behaves and looks like, then what is your title? Just curious if there is a succinct way to sum it up.

Tuesday, January 8, 2008

Worst UI Ever? Error Messages Part 2

I can't take credit for finding this one but it is a great example of how not to treat the user. I won't bother to repeat all my thoughts here but you can read them in the comments of the original blog.

However, I will take a moment to summarize what should be a global philosophy for anyone creating software. Here it is:

"Software is a tool to be used by humans to accomplish something. It is NOT a tool used by computers to accomplish something."

When you step back and think about it, it seems obvious. I buy and use software because I need to accomplish something (editing photos, paying bills, communicating with friends) in a way that is easier than the way I used to do it. The last thing I want is to feel like I am driven by what the software wants from me.

If a person said, "write your phone number down for me" and I wrote 555.555.5555, (555) 5555555 or 555 555-5555 then they would have no problem understanding that. If they said to me that when they think of phone numbers, they think of the format (555) 555-5555 and then asked me to write it in that format I would laugh and not expect to actually have to do it. I don't think it is asking too much of the software developer to implement that kind of logic. Some human-like logic is hard to implement obviously but some isn't. An when it is done, it can have a huge impact on the opinion a user will have of your software.

Who is "The User"?

In the software development world, it is common to hear developer, designers, product owners, etc. all talk about "The User". Most of the time however, we don't think much about who Mr./Ms. User actually is. This inevitably leads to a problem.

The problem is that if you don't know anything about who you are designing software for, you are very likely going to create software that either is geared toward use by the developers or product owners themselves or is based on some union of all traits of any possible user. Since most software companies are not building software development tools there is an obvious problem with the former. And, unless you are building very specialized software for a narrow population of user, you can't really combine the traits of a super power user and occasional perpetual novice user into one ideal person which makes the latter a problem.

Why shouldn't the developer build what she would want?

The answer is simple. Developers are a different breed of person. Take no offense if you are one. I am one too. Developers enjoy having an understanding of the inner workings of software and can appreciate the technical difficulty of it. Plus, they understand every aspect of the software they are building. All of that makes it hard to see the software from the point of view of a non-technical person, who has no interest in how software works and doesn't care how difficult something is to implement.

Why can't one UI serve all the traits of all possible users?
If you think about most things in the physical world, there is little that is "one size fits all". In fact, things that used to be called "one size fits all" now say "one size fits most". The reason is obvious. One size can never fit all. This doesn't just pertain to size...

Imagine you were in the shoe manufacturing business and you created a line of sneakers. So far so good. If someone decided that you should make shoes that would be good in the winter while shoveling snow, you would not likely try to adapt the sneaker design for this purpose. Even if you could make that work you will ultimately wind up in the situation where as you need to create dress shoes, sandals, water shoes, etc. they can't be combined into one shoe. The only way this can work is to understand the needs of "The User" better.

In our shoe manufacturer example the better solution would be to think of the different types of shoes as meeting the needs of particular consumers. As you go through the process of defining the different types of shoes needed, you will probably see opportunities to combine types. A sneaker and a hiking boot could be combined. But a hiking boot and a sandal probably can't.

When designing software we need to do the same thing. Certain functionality is meant to meet specific user goals. Those goals can usually be categorized into buckets such as Employee or Manager, Student or Teacher, Player, Coach and Scout. Once you categorize the functionality, you can start to make decisions about how the UI can support those types of user.

As UCD practioners will notice this is a step toward defining Personas. While Personas are very powerful mechanisms for understanding specific users, I have found that Agile teams are very reluctant to spend the appropriate amount of time defining them. If you have the time, by all means, go for it. If you don't, and can't get the time somehow, at least try to make a quick pass at it at the beginning of the process. Finding out during the middle of development that the software is going to be used for global reporting by executives as well as the individual employees for data entry would be very costly .

The Bottom Line
While you don't need to go survey hundreds of real users, it is very valuable to at least think about who those users are. Every user will be happier with the result.

Animation without aggravation

Happy New Year. Now with all those holidays behind me I can get back to blogging more regularly. To start off the year, I thought I would start with an example from Klok.

I recently was asked how I accomplished the animation when minimizing the left panel in Klok. It doesn't get much easier than this folks. This is one of those things that seems like it would be difficult to accomplish but Flex really does all the work for you.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">

<mx:HBox width="100%" height="100%">
<mx:Canvas height="100%" width="200" backgroundColor="#ff0000" resizeEffect="Resize" id="leftPanel"/>
<mx:Canvas height="100%" width="100%" backgroundColor="#0000ff"/>
</mx:HBox>

<mx:Button click="leftPanel.width=10" label="Shrink It"/>
<mx:Button click="leftPanel.width=200" label="Grow It"/>

</mx:Application>

Basically, all the magic happens by setting the resizeEffect. This causes the change in size to be animated. Setting the resizeEffect to the string "Resize" will cause an instance of mx.effects.Resize to be created with the default properties. If you want control over those properties, it isn't much more complicated.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.effects.easing.Bounce;
]]>
</mx:Script>
<mx:Resize duration="1000" id="myResize" easingFunction="{Bounce.easeOut}"/>
<mx:HBox width="100%" height="100%">
<mx:Canvas height="100%" width="200" backgroundColor="#ff0000" resizeEffect="{myResize}" id="leftPanel"/>
<mx:Canvas height="100%" width="100%" backgroundColor="#0000ff"/>

</mx:HBox>
<mx:Button click="leftPanel.width=10" label="Shrink It"/>
<mx:Button click="leftPanel.width=200" label="Grow It"/>
</mx:Application>



Notice that in both examples the buttons aren't calling any methods to kick off the animation. The buttons just set the width property. Pretty simple isn't it?

In reality you probably would be changing the width inside a method, but the important thing is that you don't have to tell the animation to "play". It is just hooked up view the resizeEffect attribute. There are many effect attributes that you can set which can allow you to do some cool stuff.