PDA

View Full Version : help creating a subclass



mindfriction
March 18th, 2004, 09:20 AM
Hi

Im in the progress of creating a XML menu component, and Im going well so far. My code is a bit messy and currently for the button press actions for the menu items I have done this below:
(a method used by Senocular in the xml menu tute)


Actions = Object();
Actions.loadGallery = function(xml, name) {
_root.transfer = new Object();
_root.transfer.galleryData = xml;
_root.transfer.galleryLabel = name;
_root.content_mc.target_mc.loadMovie("gallery.swf");
};
Actions.gotoURL = function(url, name) {
getURL(urlVar, "_blank");
};
Actions.message = function(msg, name) {
message_txt.text = msg;
};
Actions.newMenu = function(menuxml, name) {
menu_xml.load(menuxml);
};



This is just sitting in my main class (called XMLMenuClass) and is not a prototype as you can see, I would like to know how to reimplement this as a class of its own,ie a subclass of the main class called MenuActions?

Thanks in advance

Voetsjoeba
March 18th, 2004, 01:14 PM
Something like this ?



Actions = function () {
_root.transfer = new Object();
};
Actions.prototype = new MenuActions();
Actions.prototype.loadGallery = function(xml, name) {
_root.transfer.galleryData = xml;
_root.transfer.galleryLabel = name;
_root.content_mc.target_mc.loadMovie("gallery.swf");
};
Actions.prototype.gotoURL = function(url, name) {
getURL(urlVar, "_blank");
};
Actions.prototype.messageS = function(msg, name) {
message_txt.text = msg;
};
Actions.prototype.newMenu = function(menuxml, name) {
menu_xml.load(menuxml);
};


I don't know your main class so you'll probably have to make some changes to it to get it to work. Also, pay attention to the paths used in the prototype methods.

mindfriction
March 19th, 2004, 01:23 AM
ok thats going to work.

Here's my AS,



#initclip
XMLMenuClass = function(){
this.init();
};
XMLMenuClass.prototype = new MovieClip();
XMLMenuClass.prototype.init = function () {

this.cnt=0;

this.createTextField("load_txt", 1,0,0,400,200);
this.mytextFormat = new TextFormat(this);
this.myTextFormat.align = "left";
this.myTextFormat.font = this.fname;
this.myTextFormat.color = this.bcolor;
this.myTextFormat.size = this.fsize;
load_txt.setTextFormat(this.myTextFormat);

this.cnt++;

menu_xml = new XML();
menu_xml.ignoreWhite = true;
menu_xml.load(this.xmlFile);
//trace(this.xmlFile)
button_sfx = new Sound();
button_sfx.loadSound(this.sfxFile);
//trace(this.sfxFile);
this.loadComplete = setInterval(this, "showLoading", 50, menu_xml, button_sfx);
};


XMLMenuClass.prototype.showLoading = function(xmlObj, soundObj){
//trace("showLoading: " + soundObj);
var bTotal = (xmlObj.getBytesTotal()/1024)+(soundObj.getBytesTotal()/1024);
var bLoaded = (xmlObj.getBytesLoaded()/1024)+(soundObj.getBytesLoaded()/1024);
//trace("bTotal: " +bTotal+" bLoaded: " +bLoaded);
this.load_txt.text="loading "+Math.floor(bLoaded)+" kb"+" of "+Math.floor(bTotal)+" kb";

if ((bTotal == bLoaded) && (bLoaded>1)) {
trace("b_total==b_loaded");
this.generateMenu(this, "mainmenu_mc", this.cnt, 0, 0, xmlObj.firstChild);
this.cnt++;
this.mainmenu_mc.onMouseUp = function() {
if (this.mainmenu_mc.submenu_mc && !this.mainmenu_mc.hitTest(_root._xmouse, _root._ymouse, true)) {
this._parent.closeSubmenus();
}
};
this.load_txt.removeTextField()
clearInterval(this.loadComplete);//all loading done
}

};


XMLMenuClass.prototype.mainRollOver = function() {

if (this.hitTest(_root._xmouse, _root._ymouse)) {
trace("over " + this._parent._parent);
button_sfx.start(0,0);
this.highlight_mc.gotoAndPlay("over");
var x = this._x;
var y = this._y+this._height;
trace(this.generateMenu());
this.generateMenu(this.curr_menu, "submenu_mc", x, y, 1000, this.node_xml);
//trace("this.mainRollOut: " + this._parent.mainRollOut);
this.onEnterFrame = this._parent._parent.mainRollOut;
}
};
XMLMenuClass.prototype.mainRollOut = function() {
if (!this.hitTest(_root._xmouse, _root._ymouse)) {
trace("out");
//button_sfx.stop();
this.highlight_mc.gotoAndPlay("out");
this.onEnterFrame = this._parent._parent.mainRollOver;
}
};
XMLMenuClass.prototype.subRollOver = function() {
//trace("subRollOver: " + this);
if (this.hitTest(_root._xmouse, _root._ymouse)) {
trace("over " + this);
button_sfx.start(0,0);
this.highlight_mc.gotoAndPlay("over");
this.curr_menu.submenu_mc.removeMovieClip();
this.onEnterFrame = this._parent._parent.subRollOut;
}
};
XMLMenuClass.prototype.subRollOut = function() {
if (!this.hitTest(_root._xmouse, _root._ymouse)) {
trace("out");
//button_sfx.stop();
this.highlight_mc.gotoAndPlay("out");
this.onEnterFrame = this._parent._parent.subRollOver;
}
};
XMLMenuClass.prototype.generateMenu = function (container, name, x, y, depth, node_xml) {
trace("generateMenu()");
var curr_node;
var curr_item;
this.curr_menu = container.createEmptyMovieClip(name, depth);
for (var i = 0; i<node_xml.childNodes.length; i++) {
if (name == "mainmenu_mc") {
curr_item = this.curr_menu.attachMovie("mainItemClip", "mainItem"+i+"_mc", i);
curr_item._x = x+(i*(curr_item._width-8));
curr_item._y = y;
} else {
curr_item = this.curr_menu.attachMovie("subItemClip", "subItem"+i+"_mc", i);
curr_item._x = x+(curr_item._width/4);
curr_item._y = y+(i*curr_item._height);
}
curr_item.trackAsMenu = true;
this.curr_node = node_xml.childNodes[i];
curr_item.name = curr_item.label_txt.text = this.curr_node.attributes.name;
curr_item.action = this.curr_node.attributes.action;
curr_item.variables = this.curr_node.attributes.variables;

if (node_xml.childNodes[i].nodeName == "menu") {
curr_item.node_xml = this.curr_node;
curr_item.onEnterFrame = this.mainRollOver;
} else {
curr_item.arrow._visible = false;
curr_item.onEnterFrame = this.subRollOver;
}
//////<-----How to associate each menu item with Action class?------>//////
curr_item.onRelease = function() {
Actions[this.action](this.variables, this.name);
trace(this.name.text);
this.closeSubmenus();
};
}
};
XMLMenuClass.prototype.closeSubmenus = function () {
this.mainmenu_mc.submenu_mc.removeMovieClip();
};
/////<-----new Action class------>//////
Actions = function () {
_root.transfer = new Object();
};
Actions.prototype = new MenuActions();
Actions.prototype.loadGallery = function(xml, name) {
_root.transfer.galleryData = xml;
_root.transfer.galleryLabel = name;
_root.content_mc.target_mc.loadMovie("gallery.swf");
};
Actions.prototype.gotoURL = function(url, name) {
getURL(urlVar, "_blank");
};
Actions.prototype.messageS = function(msg, name) {
message_txt.text = msg;
};
Actions.prototype.newMenu = function(menuxml, name) {
menu_xml.load(menuxml);
};

/////<--------end Action class----------------->/////

Object.registerClass("XMLMenu", XMLMenuClass);
#endinitclip


I don't think it is as logical as it should be.

So how would you associate the Actions class with the menu items? Would you just


Object.registerClass("subItemClip", Action);

Object.registerClass("mainItemClip", Action);


Because each Action has variables associted with it taken from the XML which is read in the main XMLMenuCLass

Voetsjoeba
March 19th, 2004, 02:40 PM
Object.registerClass("subItemClip", Actions);
Object.registerClass("mainItemClip", Actions);


Those would indeed associate the sub and main items with the actions class, and each of those items will then have access to loadGallery, gotoURL, ... I don't know what the items are supposed to do, or what they should be able to do. Can you explain so that I'm following you ?

mindfriction
March 19th, 2004, 10:50 PM
Hi again Voets :), I really appreciate your help.

The Actions class should be associated to the XML file 'actions' attribute(see below).
(follows Senoculars XML menu format here (http://www.kirupa.com/developer/actionscript/xml_dropdown_menu2.htm)). It just seemed that since Senocular creates a sperate obj for actions called 'Actions' that it would make sense to re- interpret this into an Actions subclass / or seperate class for the purpose of recreating it as a component, Make sense?

*Anyway Im not sure I 100% understand inheritance in Flash as yet - i've programmed in Java and C++ before, so I do understand the concept

Here's an example of the XML file


<?xml version="1.0"?>
<menu name="links">
<menu name="macromedia">
<item name="flash" action="gotoURL" variables="http://www.macromedia.com/software/flash"/>
<item name="dreamweaver" action="gotoURL" variables="http://www.macromedia.com/software/dreamweaver"/>
<item name="fireworks" action="gotoURL" variables="http://www.macromedia.com/software/fireworks"/>
</menu>
<menu name="kirupa">
<item name="home" action="gotoURL" variables="http://www.kirupa.com"/>
<item name="forums" action="gotoURL" variables="http://www.kirupaforum.com/forums/index.php"/>
<menu name="tutorials">
<item name="actionscript" action="gotoURL" variables="http://www.kirupa.com/developer/actionscript/index.htm"/>
<item name="photoshop" action="gotoURL" variables="http://www.kirupa.com/photoshop/index.htm"/>
<item name="web" action="gotoURL" variables="http://www.kirupa.com/web/index.htm"/>
</menu>
</menu>
<item name="google" action="gotoURL" variables="http://www.google.com"/>
</menu>

Voetsjoeba
March 20th, 2004, 06:28 AM
Well, by looking at that XML file it seems that every item needs access to the actions. A word should pop into your head now - prototype ! Maybe it's a good idea to create the Actions object in the prototype object of your class, so that every item has access to them. Like this:



XMLMenuClass = function(){
//...
}
//...
XMLMenuClass.prototype.actions = new Object();
XMLMenuClass.prototype.actions.gotoURL = function(){
//...
}
XMLMenuClass.prototype.actions.message = function(msg){
//...
}
//....

mindfriction
March 21st, 2004, 12:28 AM
like this?


curr_item.onRelease = function() {
this._parent.actions[this.action](this.variables, this.name);
this._parent.closeSubmenus();
};


Im getting closer Voets.:love: You see the main problem iv'e been having is encapsulation. Because the menu items have their own attributes (as gathered from the XML file) and the functions they use depend on the value of these attributes i.e. if actions=gotoURL then 'this' item needs access to the gotoURL function. Im not sure whether to have one BIG class, and just assign menu items to their appropriate functions via this._parent.[function name] OR to register each menu item with its very own class...BAH the confusion and decisions in OOP!!! :(.

It seems to me that just having one main class is the better way to go..whats your opinion mate?

Voetsjoeba
March 21st, 2004, 06:56 AM
I'd go with one class to control the whole component. It gets messy to oversee if you've got multiple classes. Keeping it all in one class is easier to oversee and control. About the actions: I suppose curr_item is associated with the XMLMenuClass class using Object.registerClass ?



curr_item.onRelease = function() {
this.actions[this.action](this.variables, this.name);
this.closeSubmenus();
};


I don't see why you would want to use _parent. The clip is also associated with the class, and therefore has access to everything in the prototype object as well as the other movieclips of this class. That's the strength of the prototype object.

Add a trace action to the functions in the prototype methods and then press that button. If everything is done correctly, you will see the output of the trace. Of course, make sure that this.action etc exist ;)

mindfriction
March 29th, 2004, 07:28 AM
Hiya Votes, its been a while since I replied i know, but I thought I had solved the problem.

In answer to:

I suppose curr_item is associated with the XMLMenuClass class using Object.registerClass ?

curr_item is not associated with XMLMenu class, it is a var that simply holds the path to an attached movie clip (the clickable items on the menu).
This is where the heart of my problem lies. You see these attached movies clips (curr_items) are dynamically assigned button events which currently call on a select few functions in the XMLMenu class. And at the momment the XMLMenu class is registered to the 'container' of the menu (instance name: XMLMenu), and for these curr_items to access the functions I have to use the realtive path; this._parent._parent.<function name> (or absolute path: _root.XMLMenu.mainmenu_mc.mainItem0_mc.<function name>).

This works for the main menu items but will not work for sub menu items as the path is different; this._parent._parent._parent.<function name> (or absolute path:_root.XMLMenu.mainmenu_mc.submenu_mc.subItem6 _mc.<function name>). As you can see, confusing and bad architecture.

Now, the easy option is to just give each seperate item a different onRelease function depending on whether they are a mainItem or a subItem. This way I can have the correct pathing to the functions.

But really is this the best way? Should I seriously reconsider the structure of my code?

Anyway would love you're feedback,

Cheers ;)

Voetsjoeba
March 30th, 2004, 05:57 AM
curr_item is not associated with XMLMenu class, it is a var that simply holds the path to an attached movie clip (the clickable items on the menu).


Yeah, I was with ya so far, I was wondering whether that attached movieclip was registered to the class ;) I'd think it would be easiest to also register the seperate items (mainItem and subItem to the XMLMenu class, just to have the actions available which the class has in its prototype object. So then each movieclip in the component is registered to the class rather than just the main movieclip, so you don't need to worry about the path to the main movieclip to be able to access the actions. This was also what I was talking about in my previous post when supposing that curr_item has also been registered to the XMLMenu class.