PackageDescription: WidgetryWrapper


Widgetry Wrapper

Last published: September 18, 2007 by 'chaider'

Defines 8 Classes
Extends 28 Classes


This package allows embedding Widgetry panes or full Widgetry applications in Wrapper windows.

Usage:
Instead of putting a SubCanvasSpec in your windowSpec, use a WidgetrySubCanvasSpec. Except for the usual attributes (#layout, #name and #flags) the other attributes are not uses (especially not #majorKey, #minorKey and #clientKey). You have to edit the spec by hand to put it in (just add a regular SubCanvas using the UIPainter and add "Widgetry" in front of "SubCanvasSpec"). This is preserved when you use the UIPainter on it later.

In the code, for example in the #postOpenWith: method, your write:
(self widgetAt: #MyWidgetrySubCanvas) ifNotNil: [:widget | widget client: ].
To empty it use:
(self widgetAt: #MyWidgetrySubCanvas) ifNotNil: [:widget | widget client: nil].

To create do:
| form |
form := Form new.
form addComponent: .
form addComponent: .
... (more panes)
form frame: FractionalFrame fullyAttached.
^form
or, if you are going to install a UserInterface do:
| form |
form := Form new.
form addComponentsFromInterface: MyUserInterface new.
form frame: FractionalFrame fullyAttached.
^form
or, if you use a UserInterface with a XML-spec:
| form |
form := Form from: MyUserInterface new specification: #myWidgetrySpec.
form frame: FractionalFrame fullyAttached.
^form

Design:
On the Wrapper side I used a subclass of SubCanvas (WidgetrySubCanvas) to hold the Widgetry Form. During build-up, the WidgetrySubCanvas creates a WrapperForm as its counterpart on the Widgetry side. These two (WidgetrySubCanvas and WrapperForm) can access each other and can do most of the protocol conversions of the messages traveling up and down the visual hierarchy.
I am not sure if these 3 layers (WidgetrySubCanvas, WrapperForm and the Form you embed) are really necessary. Maybe it is possible to drop one or two of them. But for this experiment this just works well.
There are two application global objects which need to be handled as well: ApplicationWindow and KeyboardProcessor.
For both I created explicit proxys: TopPane for ApplicationWindow and WidgetryKeyboardProcessor for KeyboardProcessor. I did not use generic tricks ala #doesNotUnderstand for them, but this would be an option.
Any Widgetry Pane asking for its #topPane (the window) will get a TopPane which knows the real (Wrapper) ApplicationWindow and acts like a (Widgetry) ApplicationWindow. Since the protocols and the capabilities are not much different, most of the methods of TopPane just delegate the messages to the ApplicationWindow.
The TopPane also holds a KeyboardProcessor proxy. Here most of the methods are copied from the Widgetry counterpart and are not delegated. Maybe it would have been easier to just subclass the Widgetry.KeyboardProcessor and give it the Wrapper KeyboardProcessor as part holding the primary objects.
Several compatibility methods where added in various places, and only one override was needed (Pane>>handleEvent: which was empty in the original and doesnt seem to change the behavior of the original Widgetry system).

Limitations:
Embedding Widgetry Panes and UserInterfaces into Wrapper applications works just fine. But as this is an experiment, I didnt try to be complete. I am sure that there might be still issues here and there, but I think these would be easily solvable given the design with the proxys where you could hook into anything if necessary.
I didnt try other window types, namely Dialogs or transient windows, but again I think that they would not pose real problems.
I didnt try drag and drop. I believe that this might just work inside each world, but probably need a bit more glue to work between them. However, there is one problem which I didnt resolve: dragging the header of a grid shows the header misplaced during the drag (I think, that if I really look at it again, this could be solved).
Also, I didnt use it in a real application and, therefore, didnt try out how you would access the Widgetry panes from the old style application and how to access the old style views from the embedded Widgetry panes. I am, however, confident that this would only need a bit of syntactic sugar to make this easy.
Minor issues:
- there is more flicker than in Widgetry even if you enable double buffering in the window, since the old system does not handle this well and often wants to draw the background.
- the explicit tab order which you can define in Widgetry is not maintained - instead the old model of simple widget ordering is used
- when #mainWindow is accessed in the setup of an embedded UserInterface (for example to establish an announcement handler), but the window is not open yet, anything done to this window will not have any effect, because a fresh window will be lazily created and discarted when the form is added to the proper window. This can be seen in the demo with 'Fun With Images' where a handler toggling the enablement is added to the mainWindow. Stand alone it works, but when embedded it doesnt. Either #mainWindow should not be used when embedding an application or it should be added to the configuration (the DelayedConfigurationScript) if the window is not yet open.
- one small display glitch in one of the examples (Fun with TreeViews) where the label of the root node is initially not at the right place. The first redraw fixes this.

Fixes for Widgetry:
While doing the demo application which shows the order of widgets in the KeyboardProcessor, I found that some Widgetry widgets would not remove themselfs from the keyboard. The fixes are incuded in this package (new #release methods for DropDownList, MenuButton SpinButton, Slider and one override of #release in TabControl). A proper implementation would probably best be done in the superclass ComponentPair with the appropriate changes in the subclasses.
The problem of panes not removing themselfs from the KeyboardProcessor is still there when you have widgets in a Form in a Grid cell, but I think that I will leave this for now since it does not pose any real problem. You can see the problem in the Demo with the workspace work when you select #windowWithSimpleFormColumnGrid.
A related problem is that a ComponentPane adds its embedded panes twice to the KeyboardProcessor (but it also removes all of them when released). This can be seen in the Demo with the workspace work selecting #windowWithComponentPane or #windowWithComponentPaneWithFlags. I'll leave that as well for the moment.

There was a problem with RelationalFrame: when a Form contains only a RelationalFrame and other FractionalFrames, an endless loop occured. This is fixed with an override of method ComponentPaneArtist>>allComponentsFractional.
The fixes are included in this package, but should be moved to Widgetry as soon as it is is clear how to deal with future developments of Widgetry.

Future plans:
My ambitions with this are rather limited. I am quite happy having Widgetry inside Widgetry.
I did this experiment to provide an easy migration path for the conversion of old applications to Widgetry. But the unfolding events rendered this approach useless. I wanted to do the other direction as well: embedding Wrapper applications in a Widgetry Form. But since Widgetry has been dumped by Cincom, there is not much sense in doing so anymore.

Licence:
Do wantever you want with it, but dont sue me :-) I will not take any legal responsibility for it.