Getting javascript error while using smart-gwt - exception

I am using a combination of gwt and smart gwt. However i get the following eror whenever my page loads. please help me with fixing this. .
Please find the attached screenshot for the image of the popup error for the same
15:06:02.097 [ERROR] [detectfiles] 15:06:02.097:TMR5:WARN:Log:Error:
'Object required'
in /sc/modules/ISC_Core.js
at line 1782
[c]Element.getOffsetLeft(_1=>[DIVElement]{ID:isc_0})
[c]Element.getOffset(_1=>"left", _2=>[ImgButton ID:isc_ImgButton_0], _3=>undef, _4=>false, _5=>true)
Canvas.getLeftOffset(_1=>undef)
Canvas.getPageLeft()
Canvas.$414(null, undef)
[c]Page.handleEvent(_1=>null, _2=>"resize", _3=>undef)
[c]EventHandler.$78p(_1=>"landscape")
[c]EventHandler.$hr(_1=>undef)
callback(undefined=>undef)
"isc.EH.$hr()"
[c]Class.fireCallback(_1=>"isc.EH.$hr()", _2=>undef, _3=>Array[0], _4=>Obj{length:2}, _5=>true) on [Class Timer]
[c]Timer.$in(_1=>"$ir28")
anonymous()
"isc.Timer.$in('$ir28')"
com.smartgwt.client.core.JsObject$SGWT_WARN: 15:06:02.097:TMR5:WARN:Log:Error:
'Object required'
in /sc/modules/ISC_Core.js
at line 1782
[c]Element.getOffsetLeft(_1=>[DIVElement]{ID:isc_0})
[c]Element.getOffset(_1=>"left", _2=>[ImgButton ID:isc_ImgButton_0], _3=>undef, _4=>false, _5=>true)
Canvas.getLeftOffset(_1=>undef)
Canvas.getPageLeft()
Canvas.$414(null, undef)
[c]Page.handleEvent(_1=>null, _2=>"resize", _3=>undef)
[c]EventHandler.$78p(_1=>"landscape")
[c]EventHandler.$hr(_1=>undef)
callback(undefined=>undef)
"isc.EH.$hr()"
[c]Class.fireCallback(_1=>"isc.EH.$hr()", _2=>undef, _3=>Array[0], _4=>Obj{length:2}, _5=>true) on [Class Timer]
[c]Timer.$in(_1=>"$ir28")
anonymous()
"isc.Timer.$in('$ir28')"
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:292)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:546)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:363)
at java.lang.Thread.run(Thread.java:662)
Here is a piece of code in which I am using TreeGrid inside a popup
private PopupPanel createConsoPopup() {
final PopupPanel consoPopup = new PopupPanel();
consoPopup.setAnimationEnabled(true);
consoPopup.setAutoHideEnabled(true);
VerticalPanel consoContent = new VerticalPanel();
VEPConsoTree consoTree = new VEPConsoTree(lineTreeNode, vepConsoTree);
vepConsoTree = consoTree.getCheckStationTree();
vepConsoTree.addSelectionChangedHandler(new SelectionChangedHandler() {
#Override
public void onSelectionChanged(SelectionEvent event) {
((TextBox)flexTable.getWidget(selectedRowNum, 0)).setText(vepConsoTree.getSelectedRecord().getAttribute("vepAreaName")+"_"+vepConsoTree.getSelectedRecord().getAttribute("checkStationName"));
consoPopup.hide();
}
});
consoContent.add(consoTree);
consoPopup.add(consoContent);
return consoPopup;
}
Following is my VEPConsoTree class:
package com.renault.veppwrstats.client;
import com.google.gwt.user.client.ui.Composite;
import com.smartgwt.client.types.TreeModelType;
import com.smartgwt.client.widgets.events.DrawEvent;
import com.smartgwt.client.widgets.events.DrawHandler;
import com.smartgwt.client.widgets.tree.Tree;
import com.smartgwt.client.widgets.tree.TreeGrid;
import com.smartgwt.client.widgets.tree.TreeNode;
public class VEPConsoTree extends Composite {
private TreeNode[] lineTreeNode;
private TreeGrid vepConsoTree;
//private DFConstantsForLocale constants = GWT.create(DFConstantsForLocale.class);
/**
* #param lineTreeNode
* #param vepConsoTree
*/
public VEPConsoTree(TreeNode[] lineTreeNode, TreeGrid vepConsoTree) {
super();
this.lineTreeNode = lineTreeNode;
this.vepConsoTree = vepConsoTree;
//Grid grid = chckStatRestrictPanel();
initWidget(vepConsoTree());
}
/**
* This function creates checkStation Restriction Panel(tree) along with
* Select/Unselect All button
*
* #return
*/
private TreeGrid vepConsoTree() {
return createTreeGrid();
}
/**
* This function creates CheckStation Restriction Tree
*
* #return {#link TreeGrid}
*/
private TreeGrid createTreeGrid() {
final TreeGrid chckTreeGrid = new TreeGrid();
chckTreeGrid.setHeight("269px");
chckTreeGrid.setWidth("250px");
chckTreeGrid.setData(createChkStationTree());
//chckTreeGrid.setSelectionAppearance(SelectionAppearance.CHECKBOX);
//chckTreeGrid.setShowPartialSelection(false);
//chckTreeGrid.setCascadeSelection(true);
chckTreeGrid.setFolderIcon(null);
chckTreeGrid.setNodeIcon(null);
chckTreeGrid.setShowConnectors(true);
chckTreeGrid.setShowHeader(false);
//addSelectionChangeHndler(chckTreeGrid);
chckTreeGrid.addDrawHandler(new DrawHandler() {
public void onDraw(DrawEvent event) {
chckTreeGrid.getTree().openAll();
}
});
vepConsoTree = chckTreeGrid;
return chckTreeGrid;
}
/**
* This function creates a basic tree
*/
private Tree createChkStationTree() {
Tree chckStationTree = new Tree();
chckStationTree.setModelType(TreeModelType.PARENT);
chckStationTree.setRootValue(1);
chckStationTree.setIdField("id");
chckStationTree.setOpenProperty("isOpen");
chckStationTree.setData(lineTreeNode);
chckStationTree.setParentIdField("parent");
return chckStationTree;
}
/**
* #return the vepConsoTree
*/
public final TreeGrid getCheckStationTree() {
return vepConsoTree;
}
/**
* #param vepConsoTree the vepConsoTree to set
*/
public final void setCheckStationTree(TreeGrid checkStationTree) {
this.vepConsoTree = checkStationTree;
}
/**
* #return the lineTreeNode
*/
public final TreeNode[] getLineTreeNode() {
return lineTreeNode;
}
/**
* #param lineTreeNode the lineTreeNode to set
*/
public final void setLineTreeNode(TreeNode[] lineTreeNode) {
this.lineTreeNode = lineTreeNode;
}
}

Short answer : don't mix GWT and SmartGWT when you can
Can I mix Smart GWT and GWT widgets?
Yes, with caveats.
Smart GWT has interoperability support that allows a Smart GWT widget
to be added to a GWT container and allows a GWT widget to be added to
a Smart GWT container, and it's appropriate to use this for:
incremental migration to Smart GWT, such as introducing singular,
sophisticated Smart GWT components like the Calendar or CubeGrid to an
existing GWT application
using sophisticated third-party GWT widgets within Smart GWT, where
Smart GWT doesn't have corresponding built-in functionality
However it does not make sense to freely intermix Smart GWT and GWT
(or other) components, that is, for example, you should not place GWT
widgets within a Smart GWT container that is in turn within a GWT
container. In general, don't intermix widgets unless the need for a
feature forces you to.
The reason for this is that there are limits to the maximum degree
that two Ajax widget kits (including GWT) can interoperate - there are
no standards that allow interoperability in the areas of management of
tab order, zIndex management, pixel-perfect layout, section 508
accessibility and multi-level modality.
Note that "bugs" reported when intermixing GWT and Smart GWT
inappropriately (that is, in contradiction to these guidelines) are
generally going to be marked WONTFIX, although we will revisit this in
the future if core GWT begins to support APIs that would allow better
interoperability.
If you really need to mix GWT and SmartGWT here are the guidelines from Isomorphic
Because Smart GWT's pixel-perfect layout and auto-sizing support goes
beyond the capabilities of simple CSS layout, components need to know
the actual pixel width they have been allocated by their parent
element; they cannot "flow into" an HTML element of unspecified size.
The issue here is that GWT's containers do not provide an API similar
to Smart GWT's Canvas.getInnerWidth(), which in Smart GWT can be used
by child components to find out the available space to draw themselves
in, and hence recursively lay out out their own children. Nor do GWT
containers fire events when they are resized, or when the available
width changes for various reasons (e.g. scrollbar(s) introduced, or
CSS style changes add borders and hence reduce space).
A lot of parent<->child coordination and signaling is required to
really create an extensible pixel-perfect layout system. Smart
GWT/SmartClient has implemented all the necessary hooks to allow a
third-party widget to be embedded inside a Canvas and participate in a
precise layout, but GWT is not there yet.
If you absolutely must place a Smart GWT interface inside a GWT
container and you want it to fill the container, the best approach is
to listen for a window-level resize event and run your own layout
calculations that ultimately call resizeTo() on your topmost Smart GWT
widget. All Smart GWT widgets nested under that topmost widget will
then handle layout normally.
NOTE: Don't bother trying to find a way to insert width:100% into
Smart GWT's rendered HTML, this won't work.
source : http://forums.smartclient.com/showthread.php?t=8159

Related

axon event handler in another class

I am using axon 2.3.1 , I have one aggregate class
public class MyAggregate extends AbstractAnnotatedAggregateRoot<MBAggregate> {
#AggregateIdentifier
private MyId Id;
private Circle circle;
EventDispatcher a=new EventDispatcher();
public MyAggregate() {
}
#CommandHandler
public MyAggregate(NewCommand command ) {
apply(new SmallEvent(command.getId(), command.getCircle()));
}
#CommandHandler
public MyAggregate( StoreDestinationsCommand command ) {
apply(new BigEvent(command.getId(), command.getCircle()));
}
//And some event handlers like
#EventHandler
public void onSmallEvent(SmallEvent event)
{
//Some logic here
}
#EventHandler
public void onBigEvent(BigEvent event)
{
//Some logic here
}
Now i want these event handlers to be contained in some other class and be called when that event get triggered
public class EventContainer {
private static final long serialVersionUID = -6640657879730853388L;
#EventHandler
public void onSmallEvent(SmallEvent event)
{
//Some logic here
}
#EventHandler
public void onBigEvent(BigEvent event)
{
//Some logic here
}
I tried putting them in another class but those events are not triggered.
Any idea how can i achieve this in AXON.
Thanks,
Short Answer: You need to tell Axon that your EventContainer class can handle events published to an Event Bus.
AnnotationEventListenerAdapter.subscribe(new EventContainer(), eventBus);
Longer Answer:
To achieve what you want to do, taking a step back to understand the building blocks provided by Axon to build a CQRS application would be of help...
Axon Framework is a framework that provides you with the building blocks to build a CQRS application. And a CQRS application, in layman's term is just an architecture that allows you to separate the part of your application that executes actions (write) and the part that display your application state (read).
To do this Axon provide a couple of building blocks.
1) CommandBus
The Command Bus is the component within Axon Framework that provides the mechanism of having commands routed to their respective Command Handlers. For example from your code sample, the #CommandHandler annotation on MyAggregate means that when NewCommand is created, your MyAggregate method would be called. The Command Bus is the component that makes this possible.
2) CommandGateway
Command GateWay is a component that exposes a more friendly API to the CommnadBus. While you are not required to use a Gateway to dispatch Commands, it is generally the easiest option to do so.
3) EventBus
The EventBus handles the dispatching mechanism for events. Much like the Command Bus does for Commands. So when you have apply(new BigEvent(command.getId(), command.getCircle())); which fires a BigEvent it is the Event Bus that is responsibly for making sure the necessary event handler is called. And in your case, the question you are asking is how to have the Event handlers defined in a separate Class and still have Axon be able to route the events to them.
This is quite straight forward. I would assume you are not using Spring and that you are manually setting up the Axon components together by hand and creating the NewCommand that triggers the SmallEvent that you want to handle in EventContainer#onSmallEvent method. A way to get this done may look like this:
public class FireCommandAndCaptureEventInAnotherClass {
public static void main(String[] args) {
// We use the simple Command Bus.
// There are different implementation available. For example axon provides a distributed command bus that can be used to distribute commands over multiple nodes
CommandBus commandBus = new SimpleCommandBus();
// The friendlier API to send commands with
CommandGateway commandGateway = new DefaultCommandGateway(commandBus);
// You may skip this as it may not pertain to your question but since we are using event sourcing, we need a place to store the events. we'll store Events on the FileSystem, in the "events" folder
EventStore eventStore = new FileSystemEventStore(new SimpleEventFileResolver(new File("./events")));
// a Simple Event Bus will do
EventBus eventBus = new SimpleEventBus();
// You may skip this as it may not pertain to your question but since event sourcing is used in this example we need to configure the repository: an event sourcing repository.
EventSourcingRepository<MyAggregate> repository = new EventSourcingRepository<MyAggregate>(MyAggregate.class,
eventStore);
// Sets the event bus to which newly stored events should be published
repository.setEventBus(eventBus);
// Tells Axon that MyAggregate can handle commands
AggregateAnnotationCommandHandler.subscribe(MyAggregate.class, repository, commandBus);
// This is the part you need. With this We register an event listener to be able to handle events published on to an event bus. In this case EventContainer.
AnnotationEventListenerAdapter.subscribe(new EventContainer(), eventBus);
// and let's send some Commands on the CommandBus.
commandGateway.send(id, circle);
}
}
With this setup, the handlers in the EventContainer would be able to react to events triggered from MyAggregate

mvvmcross: NavigationService.Navigate throws an MvxException "Unable to find incoming mvxviewmodelrequest"

In my WP8 app, I have MainView referencing MainViewModel. MainView is a menu where users can navigate to other views to do some tasks. Navigating from MainView works perfectly as I use ShowViewModel. However, navigating from other views when user completes a task, back to MainView using NavigationService.Navigate(URI) throws an exception "Unable to find incoming mvxviewmodelrequest".
To avoid this exception, I have construct the URI like below
var req = "{\"ViewModelType\":\"MyApp.Core.ViewModels.MainViewModel, MyApp.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\",\"ClearTop\":\"true\",\"ParameterValues\":null,\"RequestedBy\":null}";
NavigationService.Navigate(new Uri("/MainView.xaml?ApplicationUrl=" + Uri.EscapeDataString(req), UriKind.Relative));
Does anyone have a better way to use NavigationService.Navigate?
Most navigations in the MvvmCross samples are initiated by either MvxAppStart objects or by MvxViewModels. Both of these classes inherit from MvxNavigatingObject and use the ShowViewModel methods exposed there - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNavigatingObject.cs
From MvxNavigatingObject, you can see that MvvmCross routes the navigation call to the IMvxViewDispatcher which in WindowsPhone is a very thin object - all it does is marshall all calls to the UI thread and to pass them on to the IMvxViewPresenter - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneViewDispatcher.cs
The presenter is an object created in Setup - and the default implementation uses an IMvxPhoneViewModelRequestTranslator to convert the navigation call into a uri-based navigation - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneViewPresenter.cs
Silverlight/WindowsPhone then uses this uri for navigation, creates the necessary Xaml page, and then calls OnNavigatedTo on this page. As part of the base.OnNavigatedTo(); handing in MvxPhonePage, MvvmCross then calls the OnViewCreated extension method. This method checks if there is already a ViewModel - if there isn't one then it attempts to locate one using the information in the uri - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxPhoneExtensionMethods.cs
With this explanation in mind, if any app ever wants to initiate an MvvmCross navigation from a class which doesn't already inherit from MvxNavigatingObject - e.g. from some Service or from some other class, then there are several options:
You can provide a shim object to do the navigation - e.g.:
public class MyNavigator : MvxNavigatingObject {
public void DoIt() {
ShowViewModel<MyViewModel>();
}
}
// used as:
var m = new MyNavigator();
m.DoIt();
You can instead use IoC to locate the IMvxViewDispatcher or IMvxViewPresenter and can call their Show methods directly
var request = MvxViewModelRequest<MyViewModel>.GetDefaultRequest();
var presenter = Mvx.Resolve<IMvxViewPresenter>();
presenter.Show(request);
You can write manual code which mimics what the IMvxViewPresenter does - exactly as you have in your code - although it might be "safer" to use the IMvxPhoneViewModelRequestTranslator.cs to assist with generate the url - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/IMvxPhoneViewModelRequestTranslator.cs
var request = MvxViewModelRequest<MyViewModel>.GetDefaultRequest();
var translator = Mvx.Resolve<IMvxPhoneViewModelRequestTranslator>();
var uri = translator.GetXamlUriFor(request);
One other option that Views always have is that they don't have to use the standard MvvmCross navigation and ViewModel location. In WindowsPhone, your code can easily set the ViewModel directly using your own logic like:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (ViewModel == null) {
ViewModel = // something I locate
}
// if you are doing your own logic then `base.OnNavigatedTo` isn't really needed in winphone
// but I always call it anyway
base.OnNavigatedTo(e);
}
Alternatively in WindowsPhone, you can even replace MvxPhonePage with a different base class that uses it's own logic for viewmodel location. This is easy to do in WindowsPhone as all Xaml pages have built-in data-binding support.

Application Design Patterns AS3

just thought I would share something I have found to help delivering data across an application I am wondering what others think about this I wanted to have a way to capture event bubbling up back down to other components but in a way that it would make it easy to use anywhere in may app so this is what i came up with.
I Extend the Application class and wrap in an abstract function registering a function of any component anywhere and capture it at the top most level and pass to where ever i chose to.
public class AxApplication extends Application
{
public var ___registeredEvents:Array = new Array();
public var ___registeredFunctions:Array = new Array();
function AxApplication()
{
super();
}
public function localRegisterForEvent(e:Event,func:*,caller:*):void
{
caller.addEventListener(e.type,localCallerEventHandler,true,3);
caller.addEventListener(e.type,localCallerEventHandler,false,3);
___registeredEvents.push(e);
___registeredFunctions.push(func);
}
public function localCallerEventHandler(e:*):void
{
if(e!=null)
{
for(var i:int = 0 ; i< ___registeredEvents.length; i++)
{
if(e.type == ___registeredEvents[i].type)
{
___registeredFunctions[i](e);
//the registered function gets called
//there no garbage collection implemented!
}
}
}
}
}
I think that is not a very useful solution. Why? Because you scatter AxApplication references around the application. Views and Model instance don't need any references to the application at all. It would be better to to implement a controller layer which uses a simple eventBus property, which could look like:
private static const _EVENT_BUS:IEventDispatcher = FlexGlobals.topLevelApplication;
protected final function eventBus():IEventDispatcher {
return _EVENT_BUS;
}
If you implement a base view controller/mediator (depending from which framework you're coming), you don't have any reference to non-framework classes at all, which makes it highly reusable. It is just a simple reuse of the Application singleton which you use to dispatch system wide events. You register listeners in the view controller/mediator and update the views or models accordingly. RobotLegs for example uses a system wide event dispatcher as well.
Why not just using the parentApplication approach? Because you can't implement tests (the generated test-runner of IDEs won't extend your AxApplication) or just yank the components/models in a different application - that is basically not possible.

Is there a way to clear out embedded Bitmap assets in AS3/AIR

first time posting on here.
I'm creating an AIR 3.0 app.
For a lot of my graphical assets I'm using the Flex embed metadata to embed bitmap objects as Classes and then instantiating them.
The problem is that it seems these never get garbage collected. I haven't found much info online but I've seen a couple of posts that seem to confirm this.
Anytime one of my classes gets instantiated that has these embedded assets, they always create new instances of the Bitmaps and BitmapDatas rather than reusing what's already in memory. This is a huge problem for memory. And I can't find any way of de-referenciong them or getting them to leave memory.
So the only solution I can think is to just load the graphics from disk rather than using the embed tag. But I'd rather not do this seeing as how when the app is packaged and installed, all of those graphcial assets will be on the end users computer rather than contained within the SWF.
Anyoen run into this? Have a solution? Or an alternate solution than the one I can think of?
Thanks!
Kyle
Well, I guess this is expected behaviour, because the new operator should always create new objects. But those new objects should get garbage collected, just the asset class will not, since it is a class.
You could build a cache that acts like a singleton factory. You request your image by specifying an id, the cache then either creates that image if it doesn't exist already, or just return the single instance if it does. It's been a while since I last coded ActionScript, so maybe you should take this as pseudo-code ;)
public class Cache {
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
[Embed(source="example.gif")]
public static var ExampleGif:Class;
/**
* The single instance of the cache.
*/
private static var instance:Cache;
/**
* Gets the Cache instance.
*
* #return
* The Cache
*/
public static function getInstance():Cache {
if (Cache.instance == null) {
Cache.instance = new Cache();
}
return Cache.instance;
}
/**
* The cached assets are in here.
*/
private var dictionary:Dictionary
public function Chache() {
if (Cache.instance != null) {
throw new Error("Can not instanciate more than once.");
}
this.dictionary = new Dictionary();
}
/**
* Gets the single instantiated asset its name.
*
* #param assetName
* The name of the variable that was used to store the embeded class
*/
public function getAsset(assetName:String):Object {
if (this.dictionary[assetName] == null) {
var AssetClass = getDefinitionByName(assetName) as Class;
this.dictionary[assetName] = new AssetClass();
}
return this.dicionary[assetName];
}
}
You could then use it like this:
public class Example {
public static function main() {
Bitmap exampleGif1 = Cache.getInstance().getAsset("ExampleGif") as Bitmap;
Bitmap exampleGif2 = Cache.getInstance().getAsset("ExampleGif") as Bitmap;
trace("both should be the same instance: " + (exampleGif1 == exampleGif2));
}
}
I didn't test this, so let me know if it works.
I think what you're looking for is dispose() http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html?#dispose()
If you decide to go with a caching system, here is a link with some code that is tested http://thanksmister.com/2009/01/29/flex-imagecache-a-cheap-way-to-cache-images/ . The link it has to another technique, using SuperImage, is broken, but I managed to find this http://demo.quietlyscheming.com/superImage/app.html .

How do you manage this inheritance problem?

Lets's say you have 2 classes ToolBar and DrawingToolBar. ToolBar is to serve as a base class for various other toolbars, for various tools. ToolBar handles the basic 'toolbar-y' stuff like opening, closing, dragging, dropping, etc. The DrawingToolBar adds functionality that is specific to a particular tool - tool-specific buttons, etc.
public class ToolBar extends Sprite {
public var closeBtn:Sprite
public function ToolBar():void {
addChild(closeBtn)
closeBtn.addEventListener(MouseEvent.CLICK, closeBtn_onClick)
}
protected function closeBtn_onClick(e:Event):void {
close()
}
public function open():void {
// blah
}
public function close():void {
// blah
}
}
and:
public class DrawingToolBar extends ToolBar{
public var penBtn: Sprite
public var paintbrushBtn: Sprite
public var colorPicker: ColorPicker
public function DrawingToolBar():void {
super()
}
public function getColour():int {
return colorPicker.color;
}
}
Now, we also have another 2 classes - Tool and DrawingTool. Again, Tool is a base class for various tools (incl. DrawingTool). If I make a ToolBar member in Tool (typed as ToolBar), we can delegate common tasks, eg. when the Tool is enabled, it adds the ToolBar to the stage, etc. The DrawingTool can instantiate the ToolBar as a DrawingToolBar so the correct library asset is used (but the instance is still typed as ToolBar).
public class Tool {
public var toolBar:ToolBar
public function Tool():void {
initToolBar()
}
protected function initToolBar():void {
addChild(toolBar)
}
}
and:
public class DrawingTool extends Tool {
public function DrawingTool():void {
super()
}
override protected function initToolBar():void {
toolbar = new DrawingToolBar() // this is probably very naughty
super.initToolBar()
}
public function getColor():int {
return toolBar.getColor() // this fails because toolBar is type as ToolBar not DrawingToolBar
}
}
The problem comes when, in DrawingTool I want to call a method of DrawingToolBar. Because the toolbar is typed as ToolBar, I can't call methods of DrawingToolBar on it. What do I do?
cast it every time I want to call a method of DrawingToolBar?
create a member in DrawingTool (eg. var drawingToolBar: DrawingToolBar), instantiate that and then make toolBar = drawingToolBar?
The first seems clunky, I don't even know if it would work. The second seems better but it feels a bit 'wrong'.
Is there another way? Or am I mis-using inheritance here?
You are probably mixing too many functionnalities into your toolbar. If you think MVC, you are missing a Model.
More precisely, the color picker should not be a property of your toolbar. The color picker is a property of your tool. Each drawing tool should be self contained and have its own color picker. If all drawing tools need to share the same color, you'll need a Drawing model that is shared by all drawing tools.
I would also change the relation between the Toolbar and its tools in the other direction. A toolbar has tools, but the tools themselves should be self contained and not have a reference to their containing toolbar.
The Java / Swing model is very clean on how it works with toolbars. You could get some inspiration from it : http://download.oracle.com/javase/tutorial/uiswing/components/toolbar.html