Sunday, August 7, 2011

Experimenting with ribbons in LiveCode (part 4)

Four weeks ago, we kicked off an experiment to create a ribbon control for LiveCode. By now, we have a clear goal, derived an initial XML structure for our 'ribbonText' property, and in the previous post, we finally got some visual work done, implementing the ribbon background and tabs. The next logical step is to implement our ribbon groups.

There is actually more to a ribbon group than initially meets the eye:
- groups can have an associated dialog (e.g. to call up the classic font dialog)
- groups can be added to the quick access toolbar
- groups have rules for dynamic resizing

But we'll postpone these juicier bits until wev'e gotten around to actually adding buttons to our ribbon control. Right now, our goal is to turn the group definitions in our ribbonText property into controls. First we extend the ribbonText parser to also read the <Group> definitions from the XML structure, and then we add the necessary code to the 'qrtRexRibbonGroupsBehavior' script. Under our current (limited) interpretation, each ribbon group is nothing more than the label and the separator next to it.

Once more, I've opted for the approach of taking a slice from a screenshot to represent the separator, as I couldn't get the stroke and fill gradients of a graphic to exactly mirror the original. Maybe one of these days I'll throw out the images and replace them all with gradients anyway - but today is not that day. At any rate, with just two controls to add, it's pretty easy.

And by separating out the code that triggers the population of the nested RibbonGroups custom control group, we can easily switch ribbon group sets by handling the custom 'ribbonTabPick' event that is dispatched from the nested RibbonTabs custom control group which we implemented in the previous post.

Download the current revision here - next time we'll tackle the buttons.

Tuesday, August 2, 2011

Experimenting with ribbons in LiveCode (part 3)

Three weeks ago, we kicked off an experiment to create a ribbon control for LiveCode. By now, we have a good plan, and we have an initial XML structure for our 'ribbonText' property. So let's go ahead and start putting together some code! Let's start with the ribbon background and header area, shall we?

I really wanted to do it all with only graphics, gradients and effects, but I couldn't quite get the details right. So eventually I went for the oldest trick in the book: I took a screenshot and then extracted three slices from the image: 2 pixels wide for the left and right edge, and then 1 pixel wide for the center. You can't use this slice as a pattern for another control, but you can just stretch that image as far as you need: because the original is only 1 pixel wide, the engine can stretch the image at a low cost, because there is no interpolation required.

Let's get a head start on the ApplicationMenu button: take some more screenshots while hovering and after clicking on it; then extract the images for each state of the ApplicationMenu; drop in a button and assign the appropriate image ids for the button's regular, hilited and hover icons. This is not the time for the menu itself, though, so let's move on to the ribbon tabs.

But before that, let's drop in a field "RibbonText" and a button to apply it to our ribbon group. It will make testing a whole lot easier as we go and add features.

Pop quiz: what is the fastest way to create a custom tab header in LiveCode?
Answer: why, a radio button group, of course!

I know what you're thinking: "Hold on, have you lost your marbles? Doesn't a group of radio buttons look like this?"

Well, sure - but by setting a few properties, you can turn that into this:

And that's accomplished without any scripting; once you start handling mouse event messages, you can dynamically apply graphic effects to even greater effect. But that won't suffice for our ribbon, I'm afraid, altough the principle stands: a group of 'radio buttons' can adequately implement our ribbon tabs, as long as they don't have to do it on their own...

There are several factors that determine a ribbon tab's appearance: whether or not the tab is hilited, whether or not the mouse is hovering over the tab, and whether the mouse is pressed or not. So we will enlist the help of some image controls to achieve each of these appearances.

Once again, we use an image editor to take three slices from a screenshot: one for the left edge, one for the right edge, and another to serve as a background, stretched accross the gap. Rinse and repeat for each appearance. The width of the tab can be calculated by taking the formattedWidth of the button, and adding another eighteen pixels, nine on each side, for good measure.

With our approach clearly outlined, we can code the creation of all the controls needed for the ribbon tabs, one tab at a time: first we create the three images for the tab, and then a heavily modified radio button; next, we determine the correct rectangle of our tab button, and adapt the rectangles of our three backing images to match; finally, we inject copies of the appropriate original images; and all the layers are as we would expect.

As soon as we have the tab creation working correctly, we can code the necessary engine event message handlers, property getters and setters, as well as a splash of private commands to make the ribbon tabs act like the real deal. I'd rather not paste the code into this blog post - just download the current revision here and take a good look at the scripts in the qrtRexBehaviors substack.

Next time, we'll tackle the creation of groups - and the weather is still a bit of a mess, so check back soon!