I promised Cincom
(before the decision) to have a look at whether Widgetry (sub)applications or
just panes could be embedded in old style UIs and the other way around. And here
it is (only embedding Widgetry in Wrapper though).
The package
[WidgetryWrapper] is published in the public store. For demos see the next
posting about "WidgetryWrapper Demo". The package comment is below. For best
results, please load the (currently) lastest release of Widgetry (1.33) with
which this was developed. 1.0 and 1.12 might work as well.
This work was an
experiment to see how difficult a compatibility layer between Widgetry
and Wrapper might be and the result is quite promissing. Try it out and see for
yourself.
Before the
unfortunate decision was made, this was one of the approaches (besides automatic
rewrite facilities) which promised a smooth migration path for old
style applications to move gradually to Widgetry. With this compatibility
package it is possible to add your favorite Widgetry widgets without
rewriting the entire applications. Gradually, more and more widgets could be
replaced and larger and larger Widgetry subapplications could be
adopted.
After the Cincom
decision was made, I decided to finish the Widgetry-in-Wrapper part, but drop
the Wrapper-in-Widgetry part (which would be quite possible as well IMHO).
Wrapper-in-Widgetry could also be useful for embedding old style subapplications
in the modern TabControls or just to replace the window of an application to get
the better Menus and toolbars and profit from the enhanced event behavior
(having Announcements for window events and therefore better control over the
various things happening to windows).
Cheers and have
fun,
Christian
===================
[WidgetryWrapper] package comment =====================
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: <YourWidgetryForm>]. To empty it use: (self widgetAt: #MyWidgetrySubCanvas) ifNotNil: [:widget | widget client: nil]. To create
<YourWidgetryForm> do:
| form | form := Form new. form addComponent: <expression returning a Widgetry pane>. form addComponent: <expression returning a Widgetry pane>. ... (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. |
Free forum by Nabble | Edit this page |