How do you manage this inheritance problem? - actionscript-3

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

Related

linking fla files together in actionscript using document classes

I am working in actionscript3, and since I'm self-taught, I think I've developed some bad habits, including coding on the timeline and using multiple scenes.
I am hoping to rectify this now that I'm working on a larger project.
Based on what I've read, linking multiple .fla files together is a better practice, each with their own document class. Is that correct?
If so, how do I load one .fla with its document class and then link that into the subsequent .fla file (instead of using scenes)? Or am I misinterpreting what was recommended?
Thanks!
There's no point to split your application in several loadable modules unless you have any of the following preconditions:
you have smart resource management to load and unload content
if you put everything into one file it gets just too big and hard to work with in design time or it takes far too long to compile
Regular AS3 alternative to working with scenes is creating/destroying content instances and using the main document class as their manager. You design content in the library and create behavior AS3 classes for them. Lets say, you have two content classes A and B. At the start the manager should show one of them and wait for the signal to show next one:
private var APage:A;
private var BPage:B;
gotoA();
function gotoA():void
{
if (BPage)
{
BPage.destroy();
removeChild(BPage);
BPage.removeEventListener(Event.CLOSE, gotoA);
}
APage = new A;
APage.addEventListener(Event.CLOSE, gotoB);
addChild(APage);
}
function gotoB():void
{
if (APage)
{
APage.destroy();
removeChild(APage);
APage.removeEventListener(Event.CLOSE, gotoB);
}
BPage = new B;
BPage.addEventListener(Event.CLOSE, gotoA);
addChild(BPage);
}
So, both A and B should have respective methods .destroy() that release used resources, unsubscribes methods from events, remove display objects, and so on, and they both should fire Event.CLOSE when they're done.
If you have many pages like that, you need to go for more algorithmic approach. For example, to create class BasicPage which will interact with manager and have the methods needed in all pages already declared:
package
{
import flash.display.Sprite;
class BasicPage extends Sprite
{
// A reference to the page manager instance.
public var Manager:PageManager;
public function destroy():void
{
while (numChildren > 0) removeChildAt(0);
Manager = null;
}
// Subclasses will have an access to this method to tell manager to show another page.
protected function showOtherPage(pageClass:Class):void
{
Manager.showPage(pageClass);
}
// A method that is called by manager when everything is ready.
// If page should take any actions on start it is a good idea to override this method.
public function startEngine():void
{
}
}
}
Then, example page A:
package
{
import flash.events.MouseEvent;
public class A extends BasicPage
{
// Lets say, class A in library have a designed button named Click.
public var Click:SimpleButton;
// We have things to undo here.
override public function destroy():void
{
Click.removeEventListener(MouseEvent.CLICK, onClick);
Click = null;
// Pass the destruction to superclass so it wraps its existence either.
super.destroy();
}
override public function startEngine():void
{
Click.addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(e:MouseEvent):void
{
// Lets use inherited method to show other page.
showOtherPage(B);
}
}
}
So, PageManager will be like:
package
{
public class PageManager extends Sprite
{
private var Page:BasicPage;
// constructor
function PageManager()
{
super();
showPage(A);
}
function showPage(pageClass:Class):void
{
if (Page)
{
Page.destroy();
removeChild(Page);
Page = null;
}
Page = new pageClass;
Page.Manager = this;
addChild(Page);
Page.startEngine();
}
}
}
This all could look scary at first, but it really isn't. PageManager will always have a current page, once there's a need to show another page, the current will be destroyed on a regular basis. Each page class will tend to its own content, which makes coding simpler, for you don't need to see the whole picture. If you need any persistent data, keep it in the PageManager so each page will have access to the data with no need for the pages to communicate with each other.

In Flash Pro, how to get the Properties panel to expose the properties you define on your ActionScript class?

I'm dabbling with game design and trying to create some characters for the game. Right now I've just created a single MovieClip that contains a rectangle. The MovieClip symbol extends a class that I've created in Flash Builder that implements the logic of a monster. I can then drag an instance of this monster symbol from the library to the stage and the code works when I run the simulation. So far, so good.
Now I want to create several monsters, all slightly different:
public class Monster extends MovieClip
{
public var isFriendly:Boolean = true;
public var strength:int = 10;
public var catchPhrase:String = "Booyah!";
public function Monster()
{
}
}
One way to do this is to write a new class for each monster that extends Monster and sets the properties I want in the constructor (I'd also have to create a unique symbol in the library for each of these variations too). However, this seems to be overkill if my monsters only differ by their property values.
Looking at the Flash Professional use interface, I see that at the very bottom of the Properties panel is a section that looks like a small table headed by 'Properties/Value'. Can I use this to somehow set the properties of my classes from within the Flash Professional UI? I can't find any info on how this is used.
Okay, I figured it out. The key is converting my symbol into a flash Component.
First I edited my ActionScript class to export the properties I wanted to set (including the Inspectable tag):
public class Monster
{
private var _catchPhrase:String;
public function Monster()
{
}
public function get catchPhrase():String
{
return _catchPhrase;
}
[Inspectable(name = "catchPhrase", type = String, defaultValue = "Booyah!")]
public function set catchPhrase(value:String):void
{
_catchPhrase = value;
}
}
Then I right clicked on the Monster symbol in my library and selected 'Component Definition...'. This brought up the Component Definition dialog. I then entered the name of my ActionScript class in the Class field and clicked the checkmark to validate it. Flash then automatically generated the properties I needed.
I also found this tutorial helpful:
http://redbjarne.wordpress.com/actionscript-3-0-custom-components-from-hell/

AS3 MVC Design Multiple Controllers

So I'm reading a book about MVC and the author create a controller (PlayerController) and put some functions for keyboard use and some for mouse use. But he just comment out every keyboard use.
It gave me a idea to create 2 controllers, PlayerMouseController and PlayerKeyboardController so I can decide how to control the player changing one line. And if I can design this way, later I can add a AIController for monsters that use the same view and model but are controlled by AI and so on...
I have my model Player and it do the physics stuff. Now I want two controller, one for mouse and other for keyboard. So I create a PlayerMouseController and PlayerKeyboardController.
The PlayerMouseController has 2 functions: processUpdate() and processMouseDown()
The PlayerKeyboardController has 2 functions: processKeyDown() and processKeyUp()
I create the object like this:
_player = new Player();
_playerController = new PlayerMouseController(_player);
_playerView = new PlayerView(_player, _playerController, stage);
addChild(_playerView);
If I want to change the controller I can just change the _playerController line for this:
_playerController = new PlayerKeyboardController(_player);
And it works fine... But I dont know if the design I use is fine for a large project
To make this work I have to create a Controller class with nothing so I can extends the others controllers and my view can call all methods.
public class Controller
{
public function processKeyDown(e:KeyboardEvent):void
{
}
public function processKeyUp(e:KeyboardEvent):void
{
}
public function processUpdate(stage:Stage):void
{
}
public function processMouseDown(e:MouseEvent):void
{
}
}
In my view (PlayerView) I accept any Controller:
public function PlayerView(model:Player, controller:Controller, stage:Stage)
{
_model = model;
_controller = controller;
_stage = stage;
}
and I decide what to use based on its type:
if (_controller is PlayerKeyboardController)
{
_stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
_stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
else if (_controller is PlayerMouseController)
{
_model.addEventListener(Model.UPDATE, onUpdate);
_stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
// EVENTS PlayerKeyboardController
private function onKeyDown(e:KeyboardEvent):void
{
_controller.processKeyDown(e);
}
private function onKeyUp(e:KeyboardEvent):void
{
_controller.processKeyUp(e);
}
// EVENTS PlayerMouseController
private function onUpdate(e:Event):void
{
_controller.processUpdate(_stage);
}
private function onMouseDown(e:MouseEvent):void
{
_controller.processMouseDown(e);
}
So... this is a good idea? How should I improve??
I think as your project evolves you'll soon hit a bottleneck with this kind of approach. I'd suggest creating a general IEntityController interface (don't be afraid of the word 'entity', it just shows that this is a game object controller, something that is parent for example of hero, enemy, etc.).
Then I'd create two separate implementations of this interface: BasicMouseController and BasicKeyboardController, so that I'd have two branches of these controllers with basic logic. If you need additional functionality for the Hero, you'd create a HeroMouseController class which would extend the BasicMouseController class and would have the advantage of calling super methods and adding the extended functionality easily.
You'd still have the benefit of passing different controllers to the PlayerView class as it's constructor would receive an IEntityController as a parameter, meaning anything implementing this class can be passed in.
There are many approaches for problems like this and StackOverflow is usually not meant to give these kind of answers, as every answer to these kind of questions is subjective and this website more fit for Problem/Solution kind of posts.

Flex custom toggleswitch not working in actionscript

I have a custom Flex Toggleswitch component that changes the text values of the switch.
package skins
{
import spark.skins.mobile.ToggleSwitchSkin;
public class MyToggleSwitchSkin extends ToggleSwitchSkin
{
public function MyToggleSwitchSkin()
{
super();
selectedLabel="Serviceable";
unselectedLabel="Fault";
}
}
}
If I add the control using the MXML tag, it works fine. However, when I add the component using action script, it does not.
import skins.MyToggleSwitchSkin;
public function addToggle():void {
var myCustomToggle:MyToggleSwitchSkin = new MyToggleSwitchSkin();
hgroup.addElement(myCustomToggle);
}
The control dsiplays but will not activate.
Any ideas what I have missed?
Without seeing your MXML Code, it's tough to compare your two approaches, but I believe #al_Birdy addressed the problem. You've created a custom ToggleSwitchSkin; not a custom ToggleSwitch.
Modify your addToggle() method like this:
public function addToggle():void {
var myCustomToggle:MyToggleSwitch = new MyToggleSwitch();
myCustomToggle.setStyle('skinClass',skins.MyToggleSwitchSkin);
hgroup.addElement(myCustomToggle);
}
I suspect you'll have better luck.

How to add a JPopupMenu to a JMenuBar?

I have an application with a popup menu. I'd like to use the popup in the usual way (i.e., it should appear when the user right-clicks anywhere in the window), but I'd also like to attach it to the main MenuBar at the top of the window. I'm not sure how to do this.
I'd thought it would as simple as calling
myJMenuBar.add(myPopupMenu)
but this doesn't work.
JMenuBar.add() wants a JMenu parameter, not a JPopupMenu.
Does anyone have any suggestions?
Instead of trying to reuse the JPopupMenu object, the best approach would be to encapsulate the actions that the menus perform, and reuse those. The popup would trigger those actions, as would the menu items.
From the Action JavaDoc:
In addition to the actionPerformed method defined by the ActionListener interface, this interface allows the application to define, in a single place:
One or more text strings that describe the function. These strings can be used, for example, to display the flyover text for a button or to set the text in a menu item.
One or more icons that depict the function. These icons can be used for the images in a menu control, or for composite entries in a more sophisticated user interface.
The enabled/disabled state of the functionality. Instead of having to separately disable the menu item and the toolbar button, the application can disable the function that implements this interface. All components which are registered as listeners for the state change then know to disable event generation for that item and to modify the display accordingly.
and
JPopupMenu, JToolBar and JMenu all provide convenience methods for creating a component and setting the Action on the corresponding component. Refer to each of these classes for more information.
I had the same issue. A right-mouse-click as well as a top menu with exactly the same (complicated) set of menu items. The 'Action' class is something to consider if you are talking about enablement choices, but it's not dealing with visibility and in my case there was also a dynamic list of entries based on a current selection that I wanted to reuse.
So I ended up implementing a 'Bridge' design pattern (I think) for the methods I actually use (add() and addSeparator()):
public static class MenuBridge
{
private JPopupMenu popupMenu;
private JMenu menu;
public MenuBridge(JPopupMenu popupMenu)
{
this.popupMenu = popupMenu;
}
public MenuBridge(JMenu menu)
{
this.menu = menu;
}
public void addSeparator()
{
if(popupMenu!=null) popupMenu.addSeparator();
else menu.addSeparator();
}
public void add(JMenuItem item)
{
if(popupMenu!=null) popupMenu.add(item);
else menu.add(item);
}
}
So then I can write a reusable method that computes the menu items and synchronize my right mouse click with the top-level menu:
public void addTaskMenuItems(DefaultMenu menu, List<MDProcTask> taskList)
{
...
menu.add()/menu.addSeparator()
...
}
addTaskMenuItems(new DefaultMenu(popupMenu),taskList);
...
taskMenu.addMenuListener( new MenuListener() {
public void menuCanceled(MenuEvent menuevent)
{
}
public void menuDeselected(MenuEvent menuevent)
{
}
public void menuSelected(MenuEvent menuevent)
{
taskMenu.removeAll();
addTaskMenuItems( new DefaultMenu(taskMenu),getSelectedTasks());
taskMenu.revalidate();
}});