As promised, today we add a Toolbar to our ClassHierarchyBrowser. First a few details. As we saw in the last few posts, the Menu and MenuItem objects are combined to become "model" for a MenuBar, Submenu, or popup menu. In Wrapper, these same objects are used to create a Toolbar. It then just uses the action and the image values and ignores all of the rest of the menu stuff.
In Pollock, Toolbars, besides allowing the standard buttons, it can have InputFields and DropDownLists, and in the future, CheckBoxes. Each of these can have an optional label.
Obviously, reusing the Menu model objects isn't the best solution. Sure, we could extend the Menu model objects with all of the Toolbar model information... but that's a lot of bloat. Or we could have refactored and then subclassed the Menu objects and come up with an answer that way, but again, we're talking about only three things that are common: The name key, the acton and least of all the Image, which a Menu Item isn't required to have, but some Toolbar items are.
The answer I chose a new set of model objects: ToolInventory and ToolItem.
ToolInventory is the collection of ToolItems. It, like the Menu, manages things like groups of items, as well as giving access to items. Where a MenuBar or popup menu has Menu object accessed via #menu, the Toolbar has a ToolInventory object accessed via #inventory.
To have the UserInterface framwork create our Toolbar, we create a #getToolInventory method, and answer an instance of ToolInventory:
Here, you see a similar API to the one used in #getMainMenu. We use #addToolItem: to add a new ToolItem and #addSeparator to group our Toolbar items.
The ToolItem has a large API for creating different types of ToolItems. Those that start with #image:..., will create a ToolbarButton. Those that start with #inputFieldWith:... will create an ToolbarInputField. Those that start with dropDownListWith:... will create a ToolbarDropDownList.
Here, we create two ToolbarButtons, using the same nameKeys, images and actions as their Menu cousins. We only need to write a single line of code to get these to work exactly as they do in our menu... we'll get to that in a moment.
At the end, we add a ToolItem that will model an InputField for us (a ToolbarInputField to be exact). Like all ToolItems and MenuItems, we can give it a nameKey and an action. We need to discuss how that action acts though. In Pollock, all input fields trigger a #changed event whenever the model changes. That means in effect, on every keystroke or action that modifies what you see. In Wrapper, this only happened in two circumstances: First, in the default mode, only when an explicit #accept was sent to the input field. Second, when #continuousAccept was turned on. In effect, Pollock's InputFields have no other mode than what used to be called #continuousAccept. If you wish to buffer changes, then you can use the Pollock BufferedValue in the InputField model.
What this means is that every edit the ToolbarInputField sees will trigger a #changed event under the hood, and that changed event is hooked up to call the ToolItem's action.
Before we get to writing the action, we'll talk about the two other values we have specified on our InputField ToolItem. First, is the width. This allows you to specify how wide the input field is within your Toolbar. If you don't specify a width, the default is 30. The other is the label string. Note that the width only affects the size of the actual input field, it does not have any effect on the label. In other words, the label will take up as much space as it needs without regard to any setting of the width.
Now we write our #lookupClass method:
| lookupString element |
lookupString := aToolbarInputField model value asString, '*'.
element := self classTree tree
detect: [:each | lookupString match: each object name]
self classTree selection: element
Simple code for a simple example. We get the model's current value (which being an AdvancedText, needs to be sent asString). Then we detect any matching elements in the tree. When one iterates over a Tree, the items are the currently visible TreeNodes. Each TreeNode has inside the object that it is representing in the tree. Since we are using class objects, for our simple example, we use the #name method to get the stringified name of the class object. At the end, we set the class tree with the selected element. Setting it to nil is the same as clearing the selection.
We've only one more step to go... We want our open on class tool bar button to be disabled if there is no selection in the class tree. The easiest way to do this is to piggyback on the #selectionChanged event from the classTree. We do this by adding a single line to the already existing #fillProtocolList:
And.... we're done. We didn't demonstrate how to add a DropDownList, or many of the other features of Menus and Toolbars... but I need to leave something for our documenters to do.
The above is published as version 1.22 in the Package named "PollockBlog" on the Cincom public repository.
Next time... I'm going to start on a suggestion from my readers. The idea presented to me was: "Creating A User Widget In Pollock." My thinking is to create some kind of Calender widget. You know, looks like a DropDownList, but instead of a List, it presents a Calender you can navigate through, and select a date. And we can create Unit tests, and I can tell you all how we do that in Pollock, and explain the GUI testing framework for Pollock at the same time.
That's one heck of a lot of stuff. I'm not sure if I'm brave or stupid, but stupidity hasn't stopped me yet.