Sunday, July 24, 2011

Experimenting with ribbons in LiveCode (part 2)

In an earlier post, we kicked off a series on the construction of a ribbon using pure LiveCode. For now, we'll concentrate on the Windows 7 Wordpad look-and-feel, but future posts will refine it for cross-platform consistency. After looking into Ribbon components such as Infragistics NetAdvantage WinToolbars and Microsoft Ribbon for WPF, I pondered how a developer would define and customize our ribbon implementation.

For better or worse, I opted for the developer setting the 'ribbonText' property of our custom control group, and that this property would be an XML structure. So let's break the ribbon down into pieces, break those pieces down into other pieces, and so on. Initially, one would think that the ribbon is a set of tabs, each with their own groups, each with their own buttons. But there's a little more to the story: there are several types of buttons, and aren't we glossing over the application menu and quick access toolbar?
Plus, we need some centralized management for what these buttons, menuitems, etc. look like, what label they have, etc. Microsoft calls it a 'RibbonCommand', but since theseribbon items could also be toggle buttons, that spounded odd - so I think we'll stick with the Infragistics terminology of 'Tool' - far more neutral. This tool will also include a 'message' that is sent when the user clicks the button.

So here's a first example of our ribbonText XML structure:

<?xml version="1.0"?>
<Ribbon>
<Tools>
<Tool ToolId="CutTool" Label="Cut" Message="CutText"/>
<Tool ToolId="CopyTool" Label="Copy" Message="CopyText"/>
</Tools>
<Tabs>
<Tab TabId="HomeTab" Label="Home">
<Groups>
<Group GroupId="ClipboardGroup" Label="Clipboard">
<GroupItems>
<CommandButton ToolId="CutTool" />
<CommandButton ToolId="CopyTool" />
</GroupItems>
</Group>
</Groups>
</Tab>
</Tabs>
</Ribbon>


As you can see, the Tool entry defines the label and message, and the CommandButton instance referenes that Tool via its ToolId attribute. Now let's add two more things to the mix: DropMenuButton and SplitMenuButton - clicking on the former will drop down a menu, whereas clicking the latter has different results, depending on where you click: it either executes the main function, or drops down a menu.

<?xml version="1.0"?>
<Ribbon>
<Tools>
<Tool ToolId="CutTool" Label="Cut" Message="CutText"/>
<Tool ToolId="CopyTool" Label="Copy" Message="CopyText"/>
<Tool ToolId="PasteTool" Label="Paste" Message="PasteText"/>
<Tool ToolId="PasteSpecialTool" Label="Paste special" Message="PasteSpecial"/>
<Tool ToolId="InchesTool" Label="Inches" Message="SwitchToInches"/>
<Tool ToolId="CentimetersTool" Label="Centimeters" Message="SwitchToCentimeters"/>
<Tool ToolId="PointsTool" Label="Points" Message="SwitchToPoints"/>
</Tools>
<Tabs>
<Tab TabId="HomeTab" Label="Home">
<Groups>
<Group GroupId="ClipboardGroup" Label="Clipboard">
<GroupItems>
<SplitMenuButton ToolId="PasteTool">
<MenuItems>
<CommandButton ToolId="PasteTool" />
<CommandButton ToolId="PasteSpecialTool" />
</MenuItems>
</SplitMenuButton>
<CommandButton ToolId="CutTool" />
<CommandButton ToolId="CopyTool" />
</GroupItems>
</Group>
</Groups>
</Tab>
<Tab TabId="ViewTab" Label="View">
<Groups>
<Group GroupId="SettingsGroup" Label="Settings">
<GroupItems>
<DropMenuButton Label="Measurement units">
<MenuItems>
<CommandButton ToolId="InchesTool" />
<CommandButton ToolId="CentimetersTool" />
<CommandButton ToolId="PointsTool" />
</MenuItems>
</DropMenuButton>
</GroupItems>
</Group>
</Groups>
</Tab>
</Tabs>
</Ribbon>


I think this will do as a first draft of the ribbonText XML structure. Of course, an XML structure needs a good document type definition to verify that what is coming in is valid data; and since the XML structure is still evolving, we should define some specification version into the structure. So let's call the current structure SpecificationVersion 0.1 - and here's the DTD:

<!ELEMENT Ribbon
(Tools,Tabs)>
<!ELEMENT Tools (Tool)+>
<!ELEMENT Tool EMPTY>
<!ELEMENT Tabs (Tab)+>
<!ELEMENT Tab (Groups)>
<!ELEMENT Groups (Group)+>
<!ELEMENT Group (GroupItems)>
<!ELEMENT GroupItems
(CommandButton|SplitMenuButton|DropMenuButton)*>
<!ELEMENT CommandButton EMPTY>
<!ELEMENT ToggleButton EMPTY>
<!ELEMENT SplitMenuButton (MenuItems)>
<!ELEMENT DropMenuButton (MenuItems)>
<!ELEMENT MenuItems
(CommandButton|SplitMenuButton|DropMenuButton)*>
<!ATTLIST Ribbon
SpecificationVersion CDATA #FIXED "0.1">
<!ATTLIST Tool
ToolId ID #REQUIRED
Label CDATA #REQUIRED
Message CDATA #IMPLIED
SmallIcon CDATA #IMPLIED
LargeIcon CDATA #IMPLIED>
<!ATTLIST Tab
TabId ID #REQUIRED
Label CDATA #REQUIRED>
<!ATTLIST Group
GroupId ID #REQUIRED
Label CDATA #REQUIRED>
<!ATTLIST CommandButton
ToolId IDREF #REQUIRED>
<!ATTLIST SplitMenuButton
ToolId IDREF #REQUIRED>
<!ATTLIST DropMenuButton
Label CDATA #REQUIRED>


Since we added a required attribute to the Ribbon root tag, our example XML needs to be slightly amended to:

<?xml version="1.0"?>
<Ribbon SpecificationVersion="0.1">
<Tools>
<Tool ToolId="CutTool" Label="Cut" Message="CutText"/>
<Tool ToolId="CopyTool" Label="Copy" Message="CopyText"/>
<Tool ToolId="PasteTool" Label="Paste" Message="PasteText"/>
<Tool ToolId="PasteSpecialTool" Label="Paste special" Message="PasteSpecial"/>
<Tool ToolId="InchesTool" Label="Inches" Message="SwitchToInches"/>
<Tool ToolId="CentimetersTool" Label="Centimeters" Message="SwitchToCentimeters"/>
<Tool ToolId="PointsTool" Label="Points" Message="SwitchToPoints"/>
</Tools>
<Tabs>
<Tab TabId="HomeTab" Label="Home">
<Groups>
<Group GroupId="ClipboardGroup" Label="Clipboard">
<GroupItems>
<SplitMenuButton ToolId="PasteTool">
<MenuItems>
<CommandButton ToolId="PasteTool" />
<CommandButton ToolId="PasteSpecialTool" />
</MenuItems>
</SplitMenuButton>
<CommandButton ToolId="CutTool" />
<CommandButton ToolId="CopyTool" />
</GroupItems>
</Group>
</Groups>
</Tab>
<Tab TabId="ViewTab" Label="View">
<Groups>
<Group GroupId="SettingsGroup" Label="Settings">
<GroupItems>
<DropMenuButton Label="Measurement units">
<MenuItems>
<CommandButton ToolId="InchesTool" />
<CommandButton ToolId="CentimetersTool" />
<CommandButton ToolId="PointsTool" />
</MenuItems>
</DropMenuButton>
</GroupItems>
</Group>
</Groups>
</Tab>
</Tabs>
</Ribbon>


Next time, we'll work on actually turning such definitions into actual controls in our LiveCode custom control group, so stay tuned!

No comments: