Nouns and Verbs Meet Menus 30Sep08 | 0

So everyone in usability/User-interface-land knows what WIMP is & it’s PARC history. And lately, there’s been a huge fuss over Windows’ move away from teeny-tiny-impossible-to-navigate menus to more plump ‘ribbons‘. Even in coder-land, Object oriented programming was a huge revolution, and now REST (effectively an OO-web!) is following suit.

I’m not going to get too deep into the details of all that; instead let me back up to the traditional menu. Yes, yes, that thing with “File, Edit,View, Insert, Format, Help” and the like. Question for you: how many nouns and how many verbs are on that list? And then compare each of their contents:

    File

  • New (Create)
  • Open
  • Save
  • Close
    Edit

  • Cut
  • Copy
  • Paste
  • Select
    View

  • Page
    Insert

  • Break
  • Picture
    Format

  • Page
  • Paragraph
    Help

  • Contents
  • About

This menu system is entirely schizo. Noun::Verb & Verb::Noun back & forth all day long. No wonder users can never find what they want to do: there’s no consistent pattern of thought.

When you’re out working the lawn, washing your car, which do you think about first: the action or the object? Tough call really. Likely it’s “I want to perform an action, I need these objects, now I act with them.” What thought pattern is presented in these Menus? For one, too many options: you likely don’t have your laptop when you’re using a push-mower. You likely don’t have the oven running when you’re washing your car.

Back to my point: while I’ve been on about this Noun::Verb thing for a few months, I just noticed it in gmail’s settings interface. It’s a “what are we talking about? ok, what are we doing?” kind of thought pattern. It’s behind all good analytic stuff. Keeping the noun-first,verb-second saved some code of mine this week from getting mangled, would have saved a mis-managed project, and honestly, the deaf community has been doing it for years. Object first, actions second.

Aside: This noun-first/object centric user interface design was revived a bit by the “right-click” menu. But even then, sometimes it’s properties, other times it’s actions to perform. It was the beginning of the end for double-representation where I can’t play with the object like I can with my hands. No, any playing is mediated not JUST by the mouse/computer, but by the Almighty Menu (*cough* KDE’s task program k-arm *cough*). Why do I have to go to the menu to add, remove or edit a task? Why can’t I just grab it & move it? or click a plus-sign when I mouse-over it? Why? Cuz it’s too hard for programmers who tell us to be happy with what we’ve got instead of making them work harder/more. Amazing. (Stay tuned for the code-release of said task-list from me!)

I don’t doubt the original PARC guys’ braininess. They set out on something good.Their ‘document editor” likely didn’t have “document properties & statistics” (nouns!) or movie clips, email accounts, database connections (more nouns!) to connect & attach and insert & sort. But over the years, as software took on feature-creep, these nouns & verbs crept into hidden nooks & crannies of the menus, overloading ‘em, instead of rethinking and applying major modifications which scare users away.

That’s why gmail’s mail interface works: you know WHERE you are, and there’s a simple set of verbs that you can do while you’re there. It’s an entirely rethought mail system that people love for it’s simple Noun::Verb order.

Final note: sometimes it’s perfectly ok to have the verbs in the menu (edit, insert, format) when the workspace (noun) is clearly defined: “this is a document, you compose elements here” or in gmail “this is your latest mail, read it, value it or get rid of it.”

Wordpress: Author List by Category 01Jul08 | 0

I just keep functioning out WP! (What can I say, it’s my job!)

This is a rework of WP’s own list_authors() function, but with some extra SQL:


<?php

function my_list_authors_by_category($args = '', $cat_id=0) {
global $wpdb;

$defaults = array(
'optioncount' => false, 'exclude_admin' => true,
'show_fullname' => false, 'hide_empty' => true,
'feed' => '', 'feed_image' => '', 'feed_type' => '', 'echo' => true
);

$r = wp_parse_args( $args, $defaults );
extract($r, EXTR_SKIP);

$return = '';

/** @todo Move select to get_authors(). */
//    $authors = $wpdb->get_results("SELECT ID, user_nicename from $wpdb->users " . ($exclude_admin ? "WHERE user_login <> 'admin' " : '') . "ORDER BY display_name");
//replace with the 'author by category' SQL
$authors = $wpdb->get_results("SELECT distinct post_author as `ID`, user_nicename FROM `default_posts` as `dp`, `default_terms` as `dt`, `default_term_relationships` as `dtr`,`default_users` as `du` where `dp`.`post_author`=`du`.`ID` and `dp`.`ID`=`dtr`.`object_id` and `dtr`.`term_taxonomy_id`=".$cat_id.";");

$author_count = array();
foreach ((array) $wpdb->get_results("SELECT DISTINCT post_author, COUNT(ID) AS count FROM $wpdb->posts WHERE post_type = 'post' AND " . get_private_posts_cap_sql( 'post' ) . " GROUP BY post_author") as $row) {
$author_count[$row->post_author] = $row->count;
}

foreach ( (array) $authors as $author ) {
$author = get_userdata( $author->ID );
$posts = (isset($author_count[$author->ID])) ? $author_count[$author->ID] : 0;
$name = $author->display_name;

if ( $show_fullname && ($author->first_name != ” && $author->last_name != ”) )
$name = "$author->first_name $author->last_name";

if ( !($posts == 0 && $hide_empty) )
$return .= ‘<li>’;
if ( $posts == 0 ) {
if ( !$hide_empty )
$link = $name;
} else {
$link = ‘<a href="’ . get_author_posts_url($author->ID, $author->user_nicename) . ‘" title="’ . sprintf(__("Posts by %s"), attribute_escape($author->display_name)) . ‘">’ . $name . ‘</a>’;

if ( (! empty($feed_image)) || (! empty($feed)) ) {
$link .= ‘ ‘;
if (empty($feed_image))
$link .= ‘(’;
$link .= ‘<a href="’ . get_author_rss_link(0, $author->ID, $author->user_nicename) . ‘"’;

if ( !empty($feed) ) {
$title = ‘ title="’ . $feed . ‘"’;
$alt = ‘ alt="’ . $feed . ‘"’;
$name = $feed;
$link .= $title;
}

$link .= ‘>’;

if ( !empty($feed_image) )
$link .= "<img src=\"$feed_image\" style=\"border: none;\"$alt$title" . ‘ />’;
else
$link .= $name;

$link .= ‘</a>’;

if ( empty($feed_image) )
$link .= ‘)’;
}

if ( $optioncount )
$link .= ‘ (’. $posts . ‘)’;

}

if ( !($posts == 0 && $hide_empty) )
$return .= $link . ‘</li>’;
}
if ( !$echo )
return $return;
echo $return;
}
?>

And then to call, just define the category you want:


<?php

$cat=0;
if (have_posts()){    //get the present category, if there is one
if (is_category()) {
$cat = get_query_var('cat');
}
}
if($cat!=0){
?>
<h2>Authors:</h2>
<ul>
<? my_list_authors_by_category('exclude_admin=1',$cat); ?>
</ul>
<? } ?>

Sort Wordpress Posts by Custom Field 23Jun08 | 1

I do *A LOT* of query_posts in my WP Theming for work, and I’ve found a million-and-one reasons why I needed this bit-o’-code lately. Instead of having oh-so-blog-centric sortable options such as “By Title”, “By Category” “By Modified Time”, I needed to manually sort the posts in a given category. Why? Cuz I’ve got a list of posts in a category that needs to be sequential. Very non-bloggy, but VERY CMSy.

Solution: Just add this code in to each template & make sure the blog posts are in the category MYCATNAME with a custom tag of “order”, from 0 to ad infinitum.

<?php $posts=query_posts("category_name=OptionsPage&orderby=menu_order&order=asc");

$out_array=array();

//loop #1: get the post & attach post_meta 'order' to the menu_order
foreach($posts as $post){
$this_order=0;
if( strcmp(get_post_meta($post->ID, "order", true),"")!=0){
$this_order=get_post_meta($post->ID, "order", true);
}
$post->menu_order=$this_order;
if($out_array[$this_order]==null){//start the ordering  (solved: if 2 have the same order!)
$out_array[$this_order]=$post;
} else {
array_push($out_array,$post);
}
}

//sort acc.to menu_order
$wp_query->posts=null;
$wp_query->posts=array();
for($i=0;$i<count($out_array)+1;$i++){
if($out_array[$i]!=null){
array_push($wp_query->posts,$out_array[$i]);
}
}

//start looping through!
$idx=0;
if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); $idx++;?>
<li class="flipmenuitem"><a id="link<? echo $idx; ?>" href="#" style="background:#FFF url(’<? echo bloginfo(’template_directory’);?>/images/<?if($idx==1){?>minus<?} else {?>plus<?}?>.gif’) no-repeat;padding-left:15px;" onclick="javascript:Effect.comboEffect0(’flip<? echo $idx; ?>’,this);"><?php the_title(); ?></a>
<div class="flippage" style="display:<?if($idx==1){?>block<?} else {?>none<?}?>;" id="flip<?echo $idx; ?>"><?php the_content(); ?></div></li>
<?php endwhile;
else :
endif; ?>
// … you get the picture here..

Information Errors 18Jun08 | 0

InfoWorld has a great write-up about a data-center project & what they learned from it. I’ve summarized and abstracted it here:

When you don’t know, you trust.
Differing experiences & personalities will trust different things:
D: themselves
I:  people: what they can get others to do/say.
S: what others say/face-value?
C: the information: their mind/face-value of what others say. They will not try to influence or change.

Details always trip you up
:
Practical: some times things just don’t work.
–Execution problem
Idealism: some times you just didn’t think or plan enough (and you could have).
–Information problem

Consequences:
Think + Do everyone’s job in the process, from vendor information, estimations, ordering, manufacturing, shipping; environmental surroundings; temporal surroundings (future-proofing)

Information Errors can be minimized by having a diverse enough team that can think through each step, and communicate (listen!) enough to take each info-tid-bit to heart. Most personalities aren’t capable of valuing this, and those that are, aren’t usually forceful or influential enough to make their case known. Solution? Humility.

NVidia GPU Box 31May08 | 0

I hope NASA and Oceanography get ahold of this idea..  sure beats out anything from SGI from back in the day!

Though each Nvidia is presently over $600, I personally love how their Samsung Spinpoint F1 750GB HDD is only $100 on newegg! ;)

Unalienable right: Internet 28Apr08 | 0

 http://xkcd.com/416/

..OR you could just get a data plan for your cell & stop the madness before children get abducted. (And yes, I do accomplish this with Ubuntu too!)

Fake out wordpress to pull in another page-content.. 22Apr08 | 0

Sometimes using <? $thispost=get_post($id); echo $thispost->post_content ?> isn’t enough.

Sometimes, you need a full menu structure to be highlighted or built according to that post.

Sometimes you need a menu parent pull in the ‘first child’ content.

Sometimes, you need to fake wordpress out:


<? //Usage: given, any time this template is called and happens to be on page_id '6' make page '6' act  like page '8'.
$wp_query=fakePage($wp_query->post->ID,"6","8",$wp_query);

function fakePage($currid,$thisid,$fakeid,$wp_query){
if(strcmp($currid,$thisid)==0){
$thispost=get_post($fakeid);

//the fake-out
$wp_query->post->ID=$fakeid;
$wp_query->post->post_content=$thispost->post_content;

$wp_query->queried_object->ID=$fakeid;
$wp_query->queried_object_id=$fakeid;
$wp_query->queried_object->post_content=$thispost->post_content;

$wp_query->posts[0]->ID=$fakeid;
$wp_query->posts[0]->post_content=$thispost->post_content;
}
return $wp_query;
} ?>

Trac 03Apr08 | 0

I love Trac. I hate Trac. Trac won’t take off until it starts looking prettier/simpler. A few sites try cosmetic fixes, but that’s just not enough.

Fact is, Trac is built with information as a FIRST priority.

This is good: Code Developers love information. We’ll take as much as anyone can give us, because almost always, no one gives US enough.
This is bad: The rest of the world is only concerned with a subset of the information we know, and gets overloaded with all that coders revel in.

To that end, I’ve been devin’ up a slightly better version of Trac for my company.

Here’s some ideas I’ve been on:

Ticketing

Start a New ticket:

Ticketing has got to be the heart-and-soul of Trac. Everything seems to revolve around it. A ticket can be for anything from “OMG! The program is crapping to pieces!” (from a client) to “Dude, what about this?”(brain-storming) to “You’ve got to fix these 9 little things, why do I have to keep typing in all this extra info over and over?”

Fact is, making a new ticket should be easy. Painless. (and powerful) Otherwise, it won’t be used. And besides, there’s plenty of other info that can be added on later.

newticketing

Ticket Viewing/Reports:

Just a simple point here: Why does this have to be a separate page? Why can’t these 8 or 9 reports be dumped into the subnav ( in place of ‘available reports’) while making either ‘Active tickets’ or ‘My tickets’(face it, we’re all about ourselves!) the default ‘View Tickets’ page?

reports

Ticket Follow-up:

Take a look at a default installation. One simple question: Why all the crap?

There’s 3 sections: Original Ticket, Comments, and Friggin-Complex-Info-you-must-type-in-before-you-can-even-think-about-pressing-submit-a-ticket!

Unnecessary. Especially since that “Change Properties” box at the bottom:
1) has nothing to do with commenting
2) has everything to do with the original ticket UP TOP!

So call me crazy, but why not something more like this:

trac ticket suggestion 0

ooh.. simple.. clean…

Notice:

1) Ticket changes are in parallel with the ticket.
2) Changes are minimized for those who feel like clicking ‘edit’ (to which the ticket then gets turned into a textarea, properties turn into editable select boxes.)
3) Commenting? Yup. All alone at the bottom after all the comments. Who knew?

Changelog:

changes

I like changelogs. But when a client uses the WebDAV interface, EACH FILE becomes a commit. This can lead to some VERY long changelogs. Notice those “16:32″’s up there? Why not group them all under “Wiki Changes by trac @ 16:32″? And especially those “Autoversioned Commit” You can’t tell me that can’t be parsed! Just dump all these under a collapsable ul/li & call it a day.

Ok, that’s enough of my rant for now, I’m sure I’ll be back with more Trac fixes (and a code release!) soon.

Any other bright ideas out there?

An anti-click, mouseover world.. 18Mar08 | 0

I’m sure you’ve all seen this, but just in case:
http://www.dontclick.it/

Something inside me revolts against not clicking– and it’s causal expectation: I must do something active in order to do something. But honestly, I ‘get’ it. Having to click everything all the time creates a command/dominance facade, as if the user really had control of their computer; hence all the HCI(Human-computer-interaction) frustrations/rage. We’d all like to think that we’re in control of our computers, when in reality, even the development team who built the software are under the rule of the almighty strict language syntax & debugger.

Dontclick.it is a great testbed for low-command HCI, and I wish it could take it a few steps further:

What about ‘mouse-attraction’.. giving gravity values to elements. I don’t mean 10,000 units that would disallow ANY mouse movement, but a 0-to-1 point scale that would still allow for pixel-width mouse intervals.

Lack of borders/boundaries is also tough.  Many times on the site you’re thrown into another undesired world based simply on a lack of clear borders: the padding around the text is unknown to the user, the multitude of gradients (why not use ‘em as a visual timer– the stronger the gradient, the less time you have before action?)..

As well, for some users like myself who REALLY like to use the mouse to track their reading, this design implementation on the front page drives us up a wall. But that is rule-changable, and honestly, just a front-page issue - the rest of the site is well done.

Finally, X windows generically supports this kind of HCI, and since seeing this site, I’ve been using mouse-over window focusing for a few months. It drives my girlfriend batty to use it, but with repeated use, (and a HORRID Macbook mouse-button), it’s worth keeping.

My specific XFCE implementation allows for, what is in no uncertain terms: GRACE! If I mouse-over something else, that something else will alight in focus for a second before fully coming into focus cuing me to say, “Do you want to focus on this window?” And if I don’t, in the brief second, I can move back and never lose window focus.

JS element justification 04Mar08 | 0

So in an over-exuberant (and low-sleep) moment, I coded this one up. Say ya wanna have 5 elements on a page evenly spaced out, but you can’t depend on .. anything. Under normal circumstances a sane person would just use a table, with text-align:center & td.padding. Nope. Not me. I do things the hard way:

<script>
function justifier(par){
ulist=document.getElementById(par);
plain=0;
count=0;
/* determine widths for padding. Must set paddings to 0 for an accurate count */
for(i=0;i<ulist.childNodes.length;i++){
  if(ulist.childNodes[i].nodeType==1 && ulist.childNodes[i].nodeName=="LI"){
	pad(ulist.childNodes[i],0);
	plain+=parseInt(ulist.childNodes[i].offsetWidth);
	count++;
  }
}
wide=ulist.offsetWidth;
x=parseInt((wide-plain-count)/(count*2));	//due to FF, count !=ulist.childNodes.length
//alert("w:"+wide+" cw:"+plain+" ="+(wide-plain)+"/"+count+" pad:"+x);
/* change paddings: */
for(i=0;i<ulist.childNodes.length;i++){
  if(ulist.childNodes[i].nodeType==1 && ulist.childNodes[i].nodeName=="LI"){
	mainMenuItem=ulist.childNodes[i];
	pad(mainMenuItem,x);
  }
}

}

function pad(ele,x){
ele.style.paddingLeft=x+"px";
ele.style.paddingRight=x+"px";
}
</script>

<style>
#list{
width:40%;
border:1px red solid;
}
ul{
padding:0px;
margin:0px;
float:left;
list-style-type:none;
}
li{
padding:0px;
margin:0px;
float:left;
list-style-type:none;
}
</style>
<body onload="justifier(’list’)" onresize="justifier(’list’)">
<ul id="list">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>

</body>