We all know that PB has the HProgressBar and VProgressBar controls, and they work exactly as advertised. You set the min/max position properties, the starting position, and the step increment, and then just call the StepIt() method to kick the progress bar into action. You get two basic styles – a “blocky” style and a “smooth” style. That’s it…
Well, I was goofing around on iTunes today, and was quite enamored with their nice animated progress bar. If you haven’t seen it, it’s got a nice angled gradient that moves along the bar from left to right, as the bar creeps forward. It looks like this:
It’s just a nice GUI effect, and I wondered if I could implement that same effect in PowerBuilder… And I got pretty close! Here’s a screen shot showing the application in action.
There were a couple of challenges I had to overcome in this project: a) how to get the “progress bar” column to increment like an HProgressBar, and how to animate it with a sliding gradient image. Let’s tackle these one at a time.
First – what would I use to simulate a progress bar? I decided to create an external freeform datawindow, with two columns in the result set. The first column is the actual bar itself, which I called “progress_bar” (because I’m clever like that…). I placed this column into the detail band, and gave it an angled gradient from Silver to Gray. The first image below is the Design pane of the datawindow painter, showing the column itself (ignore that computed column for now, we’ll discuss that in a moment), and the second image is of the Background properties pane.
I decided I would make the progress_bar column “grow” by dynamically changing its .Width property, starting at 0 and growing to it’s fully defined length 1% at a time in 100 increments. I also decided to do this with a datawindow expression, rather than coding Modify() calls from the PowerScript.
Which leads us to the second column in the result set – a hidden numeric value named “col_width”. In each iteration of the Timer event, I do a SetItem() of the value 1 to 100 into this column. By placing an expression that used that value as the “current percentage” of the original width, the datawindow rendering engine does all the heavy lifting! Now I just had to come up with the correct expression for the width property.
Since I was going to trigger 100 Timer events, and increment the progress_bar.width property by 1% of its “painted” length, my expression needed to determine what the original “painted” length of the column was. However, I can’t just use
Describe( 'progress_bar.width'), because there’s an expression in there, separated from the original width value by the “~t” (tab) character. I had to monkey around with it to extract out the initial width value. That tricky part of the expression was placed into the aforementioned computed column – just for legibility.
long( mid( describe( 'progress_bar.width'), 2, (pos( describe( 'progress_bar.width'), '~t') - 1)))
That says, strip out the characters in the Width property between position 2 and the first ~t, and cast those into a Long value. The actual expression in the .Width property is then simply
compute_1 * (col_width / 100.0), or “the original width of the column times the current iteration percentage. That gets our “progress_bar” incrementing right in step with the HProgressBar control!
Now let’s look at how I accomplished the gradient animation trick…
The first step is creating the angled gradient itself, and understanding how the new properties affect that gradient’s appearance. The Focus property of a background gradient changes the “center” of the gradient – in other words, the point at which there is 100% of the primary color. By choosing an gradient angled to about 45 degrees, and sliding the Focus property from 0 to 100, you can achieve that animated “sliding” effect that we’re after. The trick is to do it quickly, so it doesn’t appear jumpy. This relies on a little-used characteristic of the datawindow: it has it’s own internal Timer mechanism! By setting the “Timer interval” property on the General tab of the datawindow, any expression that uses the Now() function automatically gets recalculated at that interval.
I set the Timer Interval property to 10 milliseconds (1/100th of a second), and then wrote the following expression into the Focus property of the “progress_bar” background gradient:
integer( string( Now(), 'ff')). That takes the current time, gets the current 100th of a second from it, and makes an integer out of that. This causes the Focus of the gradient to move smoothly from the left side of the column, and then start over when it reaches the end.
As I said at the start, this doesn’t exactly duplicate the iTunes effect, but it does get close. The iTunes progress bar has more than one Focus point, i.e., it’s more heavily striped, whereas the datawindow only gets one Focus point, but it’s still a neat effect!
Download the TipsnTricks.zip file to see the code for this. It’s the uo_gradient_progressbar object. Let me know your thoughts on it.