antun
08-06-2003, 10:12 AM
Laszlo has been mentioned on Boxes And Arrows recently, and one post in particular (concerning the usability of Calendar) caught my attention:
http://www.boxesandarrows.com/archives/usability_heuristics_for_rich_internet_application s.php?page=discuss#8408
Tim's suggestions are extremely insightful. I decided to try my hand at implementing a couple of them.
Here is my updated calendar with the tweaks Tim suggested:
http://www.mylaszlo.com/lps-v1/antun/calendar/calendar.lzx
Some disclosures:
Although I work for Laszlo, I did not work on the code for the Calendar demo app, nor have I looked at it previously.
I did not ask for any assistance from anyone on this venture. (In fact I didn't tell anyone I was going to do this).
In short, I wanted to see just how easy it would be to make some adjustments to an app that I had not worked on previously. Here is what I decided to work on:
Tim mentioned that it would be a good idea to have a status bar.
Tim also recommended that once we were in after clicking "Add Event", clicking on a day should move the newly created event to that day.
[/list=1]
General
To find my way around the code I stuck to some basic debugging guidelines:
[list]
Using grep (a *NIX string search-for-string utility) to look for strings in a number of files. For example, to find the "Add Event" button in the LZX code, I did: grep "Add Event".
Changing background colors of views to highlight a view that I found in the code, in the app. For example, when I was homing in on the bit of LZX code that dealt with a single "day" cell (in the month view), I changed the bgcolor attribute of some suspect views to "red" to see what ended up being red in the app. That way I knew if the view I was looking at was indeed the main "day" view.
Using the debugger, and writing to it in different methods to find out when they were being called.
Status Bar
I decided to put the status bar in the top right, as there was some room there.I allowed myself 10 minutes of photoshop time to come up with one, which I saved as a .gif file.
As for the LZX code that makes the Status Bar happen, I saved it in a separate file called "statusbar.lzx". I defined a class called mystatusbar, and gave it two methods, setText() and clear():
<class name="mystatusbar"
resource="images/status/status_bar_background.gif">
<method name="setText" args="t">
this.disp.setText( t );
</method>
<method name="clear">
this.disp.setText( '' );
</method>
<!-- This is where the text will show -->
<text name="disp" x="4" valign="middle" width="170" />
</class>
Next, I included statusbar.lzx in the main calendar.lzx file, and instantiated it there:
...
<!-- Included like so: -->
<include href="statusbar.lzx"/>
...
<!-- Instantiated and positioned like so: -->
<mystatusbar id="statusBar" x="635" y="3"/>
...
Now obviously I wasn't going to set up my status bar to respond to everything - I don't have that much spare time! Instead I chose to set it up for these "hotspots":
[list=1]
The Add Event button
Any day cell in the calendar
A calendar event
The "+" or "-" (minimize/maximize) button in the top-right hand side of the current day.
The "i" information button that pops up when an event has been clicked and is "hot".
[/list=1]
I found the Add Event button in calendar.lzx, and added the red text below to it:
<calButton label="Add Event" x="496" y="5"
onclick="eventDataMgr.addevent(); infopanel.open()"
onmouseover="statusBar.setText('Add a new event')"
onmouseout="statusBar.clear()" />
The other four "hotspots" were similar. Some already had onmouseover event handlers/methods associated with them, so I just added them to the script. The only one that was any different was the maximize/minimize button, which needed a different message depending on whether it was already large or still small. Here is the code for that button, again the red text is what I added:
<multistatebutton name="r" resource="tpRgt_multi" maxstate="1"
statelength="3"
x="parent.m.x + parent.m.width - 1" y="-1">
<method event="onclick">
if (this.statenum ==0){
calgrid.openday(classroot.dayview,true)
}else{
if (calgrid.gridlayout.displaymode=='cell'){
calgrid.locked=true;
calgrid.gridlayout.setopenview(null,false);
calgrid.locked=false;
calgrid.showMonthView();
} else calgrid.gridlayout.setopenview(null,true);
}
</method>
<method event="onmouseover">
if (this.statenum == 0)
var msg = 'Maximize this day';
else if (this.statenum == 1)
var msg = 'Minimize this day';
statusBar.setText(msg);
</method>
<method event="onmouseout">
statusBar.clear();
</method>
</multistatebutton>
Changing the Day of a Newly Created Event
This bit was a little more tricky. I found that there was already an onclick method for a day (my code in red):
<method event="onclick" >
if (infopanel.opened)
infopanel.setDay(this);
calgrid.openday(this);
</method>
infopanel is the id of the info panel, and I discovered it had an opened attribute (by looking at the code), so I created a new method, called setDay():
<!-- Set the day in the info panel (called when a day is clicked) -->
<method name="setDay" args="dayStr">
// For debugging: debug.write( dayStr );
// The day arrives in a string in the form: calendar_day 2003.8.15
// break out the date, month and year components
var s = String( dayStr );
var a = s.split( '.' );
var month = Number( a[1] );
var day = Number( a[2] );
a = a[0].split( ' ' );
var year = Number( a[1] );
eventDataMgr.changeDayTo( year, month, day );
</method>
I discovered (by printing out dayStr) that I could parse out the date of the day cell that was clicked. I also discovered that there was this "eventDataMgr" that was an abstract handler of changes to the current event. I found it by looking at the code for the buttons next to the date. They incrementally moved the date, so I had to write the changeDayTo method to be able to jump to a specific date:
<method name="changeDayTo" args="year, month, day">
// Moves the current event to the specified date
var tempDate = new Date();
tempDate.setFullYear(year);
tempDate.setMonth(month);
tempDate.setDate(day);
moveEvent(year,month,day);
</method>
That's it!
Conclusion
I allowed myself 10 minutes to create the graphic for the status bar, and the coding took less that two and a half hours (including familiarizing myself with the code).
-Antun
http://www.boxesandarrows.com/archives/usability_heuristics_for_rich_internet_application s.php?page=discuss#8408
Tim's suggestions are extremely insightful. I decided to try my hand at implementing a couple of them.
Here is my updated calendar with the tweaks Tim suggested:
http://www.mylaszlo.com/lps-v1/antun/calendar/calendar.lzx
Some disclosures:
Although I work for Laszlo, I did not work on the code for the Calendar demo app, nor have I looked at it previously.
I did not ask for any assistance from anyone on this venture. (In fact I didn't tell anyone I was going to do this).
In short, I wanted to see just how easy it would be to make some adjustments to an app that I had not worked on previously. Here is what I decided to work on:
Tim mentioned that it would be a good idea to have a status bar.
Tim also recommended that once we were in after clicking "Add Event", clicking on a day should move the newly created event to that day.
[/list=1]
General
To find my way around the code I stuck to some basic debugging guidelines:
[list]
Using grep (a *NIX string search-for-string utility) to look for strings in a number of files. For example, to find the "Add Event" button in the LZX code, I did: grep "Add Event".
Changing background colors of views to highlight a view that I found in the code, in the app. For example, when I was homing in on the bit of LZX code that dealt with a single "day" cell (in the month view), I changed the bgcolor attribute of some suspect views to "red" to see what ended up being red in the app. That way I knew if the view I was looking at was indeed the main "day" view.
Using the debugger, and writing to it in different methods to find out when they were being called.
Status Bar
I decided to put the status bar in the top right, as there was some room there.I allowed myself 10 minutes of photoshop time to come up with one, which I saved as a .gif file.
As for the LZX code that makes the Status Bar happen, I saved it in a separate file called "statusbar.lzx". I defined a class called mystatusbar, and gave it two methods, setText() and clear():
<class name="mystatusbar"
resource="images/status/status_bar_background.gif">
<method name="setText" args="t">
this.disp.setText( t );
</method>
<method name="clear">
this.disp.setText( '' );
</method>
<!-- This is where the text will show -->
<text name="disp" x="4" valign="middle" width="170" />
</class>
Next, I included statusbar.lzx in the main calendar.lzx file, and instantiated it there:
...
<!-- Included like so: -->
<include href="statusbar.lzx"/>
...
<!-- Instantiated and positioned like so: -->
<mystatusbar id="statusBar" x="635" y="3"/>
...
Now obviously I wasn't going to set up my status bar to respond to everything - I don't have that much spare time! Instead I chose to set it up for these "hotspots":
[list=1]
The Add Event button
Any day cell in the calendar
A calendar event
The "+" or "-" (minimize/maximize) button in the top-right hand side of the current day.
The "i" information button that pops up when an event has been clicked and is "hot".
[/list=1]
I found the Add Event button in calendar.lzx, and added the red text below to it:
<calButton label="Add Event" x="496" y="5"
onclick="eventDataMgr.addevent(); infopanel.open()"
onmouseover="statusBar.setText('Add a new event')"
onmouseout="statusBar.clear()" />
The other four "hotspots" were similar. Some already had onmouseover event handlers/methods associated with them, so I just added them to the script. The only one that was any different was the maximize/minimize button, which needed a different message depending on whether it was already large or still small. Here is the code for that button, again the red text is what I added:
<multistatebutton name="r" resource="tpRgt_multi" maxstate="1"
statelength="3"
x="parent.m.x + parent.m.width - 1" y="-1">
<method event="onclick">
if (this.statenum ==0){
calgrid.openday(classroot.dayview,true)
}else{
if (calgrid.gridlayout.displaymode=='cell'){
calgrid.locked=true;
calgrid.gridlayout.setopenview(null,false);
calgrid.locked=false;
calgrid.showMonthView();
} else calgrid.gridlayout.setopenview(null,true);
}
</method>
<method event="onmouseover">
if (this.statenum == 0)
var msg = 'Maximize this day';
else if (this.statenum == 1)
var msg = 'Minimize this day';
statusBar.setText(msg);
</method>
<method event="onmouseout">
statusBar.clear();
</method>
</multistatebutton>
Changing the Day of a Newly Created Event
This bit was a little more tricky. I found that there was already an onclick method for a day (my code in red):
<method event="onclick" >
if (infopanel.opened)
infopanel.setDay(this);
calgrid.openday(this);
</method>
infopanel is the id of the info panel, and I discovered it had an opened attribute (by looking at the code), so I created a new method, called setDay():
<!-- Set the day in the info panel (called when a day is clicked) -->
<method name="setDay" args="dayStr">
// For debugging: debug.write( dayStr );
// The day arrives in a string in the form: calendar_day 2003.8.15
// break out the date, month and year components
var s = String( dayStr );
var a = s.split( '.' );
var month = Number( a[1] );
var day = Number( a[2] );
a = a[0].split( ' ' );
var year = Number( a[1] );
eventDataMgr.changeDayTo( year, month, day );
</method>
I discovered (by printing out dayStr) that I could parse out the date of the day cell that was clicked. I also discovered that there was this "eventDataMgr" that was an abstract handler of changes to the current event. I found it by looking at the code for the buttons next to the date. They incrementally moved the date, so I had to write the changeDayTo method to be able to jump to a specific date:
<method name="changeDayTo" args="year, month, day">
// Moves the current event to the specified date
var tempDate = new Date();
tempDate.setFullYear(year);
tempDate.setMonth(month);
tempDate.setDate(day);
moveEvent(year,month,day);
</method>
That's it!
Conclusion
I allowed myself 10 minutes to create the graphic for the status bar, and the coding took less that two and a half hours (including familiarizing myself with the code).
-Antun