Thursday, January 31, 2008
Audio Capture Component?
If this doesn't already exist, it might be my next pet project.
Thursday, January 24, 2008
What Kuler Desktop should be
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
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?
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"?
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
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.
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: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>
<?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.