using afIocusing gfxusing fwt** Panels are widget panes that decorate the edges of the main window.** Only one instance of each panel type may exist.** They are (typically) created at application startup and live until the application shuts down.**** 'Panel' implementations should be *autobuilt* and contributed to the 'Panels' service:**** syntax: fantom** ** @Contribute { serviceType=Panels# }** static Void contributePanels(Configuration config) {** config["myPanel"] = config.autobuild(MyPanel#)** }**** 'Panels' are automatically added to the 'EventHub', so to receive events they only need to implement the required event mixin.abstractclass Panel { @Inject private Log _log @Inject private Registry _registry @Inject private Errors _errors @Inject private RefluxIcons _icons @Inject private RefluxEvents _eventsinternal |->Widget|? _parentFunc** The content displayed in the panel. May be set / re-set at any time. Widget? content { set { _parent := (Widget?) _parentFunc?.call()// nullable, as content may be set in the ctorif(_parent is ContentPane){((ContentPane) _parent).content = it}else{ _parent?.remove(&content) _parent?.add(it)} &content = it _parent?.relayout}}** Return this panel's preferred alignment which is used to** determine its default position. Valid values are:** - 'Halign.left' (default)** - 'Halign.right'** - 'Valign.bottom' Obj prefAlign := Halign.left** As displayed in the panel's tab.** If no name is given, it defaults the the Panel's type, minus any 'Panel' suffix. Str name := ""{ set { &name = it; if(content?.parent is Tab || content?.parent is CTab) content.parent->text = it; if(isShowing)this->onModify }}** As displayed in the panel's tab. Image? icon { set { &icon = it; if(content?.parent is Tab || content?.parent is CTab) content.parent->image = it; if(isShowing)this->onModify }}** Subclasses should define the following ctor:**** new make(|This| in) : super(in) { ... }new make(|This| in){ in(this) baseName := typeof.nameif(baseName.endsWith("Panel")) baseName = baseName[0..<-"Panel".size]if(baseName.endsWith("View")) baseName = baseName[0..<-"View".size]this.name = baseName.toDisplayNamethis.icon = _icons.get("ico${typeof.name}", false)} @PostInjectionprivate Void _setup(EventHub eventHub){ eventHub.register(this, false)}** Is the panel currently the active tab in the tab pane? Bool isActive := false{internal set }** Is the panel currently showing in the tab pane? Bool isShowing := false{internal set }** Callback when this panel is shown in the tab pane.virtual Void onShow(){}** Callback when this panel is removed from the tab pane.virtual Void onHide(){}** Callback when this panel is selected as the active tab.virtual Void onActivate(){}** Callback when this panel is unselected as the active tab.virtual Void onDeactivate(){}** Callback when panel details are modified, such as the name or icon.virtual Void onModify(){}** Callback for when the panel should refresh it's contents.** Typically, active panels are asked to refresh when the 'refresh' button is clicked.**** The given resource is a hint as to what's been updated.** If 'null' then all content should be refreshedvirtual Void refresh(Resource? resource := null){}** It is common to handle events from FWT widgets, such as 'onSelect()', but should these throw** an Err they are usually just swallowed (or at best traced to std err). To work around it,** follow this pattern:**** pre>** class MyPanel : Panel {** new make(|This| in) : super(in) {** content = Tree() { it.onSelect.add |e| { this->onSelect(e) } }** }**** private Void onSelect(Event e) {** ...** }** }** <pre**** Routing the event to a handler method via '->' calls this 'trap()' method. Should that** method return 'Void' and be prefixed with 'onXXX' then any Err thrown is logged with the** Errors service.**** Simple Err handling without fuss!override Obj? trap(Str name, Obj?[]? args := null){if(name.startsWith("on")) _log.debug("${name} - $this") retVal := nulltry retVal = super.trap(name, args)catch(Err err){// because we handle the err and return null, we want to make sure we only do it for fwt eventsif(name.startsWith("on") && typeof.method(name, false)?.returns == Void#){ _errors.add(err)returnnull}elsethrow err}if(thisis View)switch(name){case #onShow.name : _events.onViewShown(this)case #onHide.name : _events.onViewHidden(this)case #onActivate.name : _events.onViewActivated(this)case #onDeactivate.name : _events.onViewDeactivated(this)case #onModify.name : _events.onViewModified(this)}elseswitch(name){case #onShow.name : _events.onPanelShown(this)case #onHide.name : _events.onPanelHidden(this)case #onActivate.name : _events.onPanelActivated(this)case #onDeactivate.name : _events.onPanelDeactivated(this)case #onModify.name : _events.onPanelModified(this)}return retVal}}