0

Dealing with the unfamiliar concept (to me) of asynchronous and event driven programming I struggle with the correct structure of my programs in AS3.

What I want to do is write a class which presents the user with a dialogue box with some text (sent to the constructor) and two buttons 'Yes' and 'No'. Thankfully this is no longer a problem to me. The problem comes in when deciding the correct structure of the program to get which button has been clicked.

Let's say I have Main.as which at some point calls new MyDialog, my theoretical dialogue class. I have a getter in MyDialog.as which sets up a variable myButton which is 0 by default, 1 if Yes is clicked and 2 if No is clicked.

Obviously I can't create a dialogue then immediately use the getter, the user won't have had time to click either button, they may take any amount of time to do so. As I see it I'd have to have a timer (or frame event) loop in Main.as which continually uses the getter to look at myButton then act accordingly when it changes.

This seems wrong to me. My Main.as would have to have lots of state flags for each dialogue I create. It would be messy and seems to go against the principles of event driven programming.

I realise I could have MyDialog just create the graphics and then in Main.as have an event triggered when the buttons in that dialogue are clicked but that seems to go against the encapsulation of Object Orientated Programming where MyDialog should be completely standalone.

So, are either of my possibilities the right way of doing it or have I overlooked something? Any thoughts on the matter would be gratefully received. I'm not the world's best coder and this newfangled stuff starts hurting my old head after a while :)

Jerry
  • 17
  • 4
  • 1
    That's what event are for. dispatch event when any button are clicked and catch them then take action. – BotMaster Sep 05 '14 at 19:27

2 Answers2

-1

Just register the buttons to the functions you want them to call when clicked. You can pass them through the constructor like so...

package {
    public class Modal extends Sprite {
        public function Modal(message:String, yes:Function, no:Function) {
            // Draw Background
            graphics.beginFill(0xd9d9d9, 1);
            graphics.drawRect(0, 0, 500, 300);
            graphics.endFill();

            // Create Text
            var txt:TextField = new TextField();
            txt.text = message;
            addChild(txt);

            // Create Yes Button
            var btnYes = new Button();
            addChild(btnYes)
            btnYes.addEventListener("click", yes);

            // Create No Button
            var btnNo = new Button();
            addChild(btnNo)
            btnYes.addEventListener("click", no);
        }
    }
}

Mind that you'll need to import the appropriate classes, and you might be using an entirely different class for your buttons, but the gist remains. In fact, if you don't want to use event listeners on the remote functions, you could just pass arbitrary functions with custom arguments.

package {
    private var funcYes:Function;
    private var argsYes:Array;

    private var funcNo:Function;
    private var argsNo:Array;

    public class Modal extends Sprite {
        public function Modal(message:String, yes:Function, no:Function, yesArgs:Array = null, noArgs:Array = null) {
            // Draw Background
            graphics.beginFill(0xd9d9d9, 1);
            graphics.drawRect(0, 0, 500, 300);
            graphics.endFill();

            // Create Text
            var txt:TextField = new TextField();
            txt.text = message;
            addChild(txt);

            // Create Yes Button
            var btnYes = new Button();
            btnYes.name = "yes";
            addChild(btnYes)
            btnYes.addEventListener("click", buttonListener);

            // Create No Button
            var btnNo = new Button();
            btnNo.name = "no";
            addChild(btnNo)
            btnYes.addEventListener("click", buttonListener);

            // Store functions and args for later use.
            funcYes = yes;
            argsYes = yesArgs;
            funcNo = no;
            argsNo = noArgs;
        }

        private function buttonListener(e:MouseEvent):void {
            switch (e.currentTarget.name) {
                case "yes":
                    if (argsYes != null) {
                        funcYes.apply(null, args);
                    } else {
                        funcYes();
                    }
                case "no":
                    if (argsNo != null) {
                        funcNo.apply(null, args);
                    } else {
                        funcNo();
                    }
                    break;
            }
        }
    }
}
Atriace
  • 2,572
  • 1
  • 14
  • 27
  • Thank you, I hadn't considered the possibility of passing functions this would seem to satisfy the demands of both OOP and event-driven programming. That's very smart indeed. – Jerry Sep 06 '14 at 02:25
  • I take it at some point I need to make funcYes = yes; and funcNo = no; ? And should the 'args' variable be argsYes and argsNo as appropriate in the .apply methods? – Jerry Sep 06 '14 at 02:36
  • I totally forgot to add that in the second example (oops). Correct, you can see the appropriate assignment at the end of the constructor now. – Atriace Sep 08 '14 at 13:41
-1

The answer, which I should write is way to extensive ... this is why I try to break it down into some little steps.

  1. Learn more about the concepts of OOP. OOP is basically slicing the different concerns of your application into state, behaviour and communication between aspects of your application.
  2. Get a good understanding of MVC(S). The MVC(S) pattern (Model View Controller Service) Pattern is the de facto standard pattern to deconstruct your application into separate layers.
  3. Read about frameworks like http://www.robotlegs.org/. This is basically a good implementation of MVCS and how to apply it.

robotlegs should provide you a nice idea of how things should be structured. The hard part is basically making all components get to know each other. robotlegs tackles this problem really nice.

The more technical answer: You have a model, which is basically a data object.

package samples.model {

  public class SimpleValueModel extends EventDispatcher {

    private var _value:int;

    public function get value():int { return _value; }

    public function set value(v:int):void {
      if (_value == v) return;
      _value = v;
      dispatchEvent(new Event('valueChanged'));
    }
  }
}

Now every class interested in the data and if it changes, needs a reference to it, to read its value:

private var _model:SimpleValueModel;

public function get model():SimpleValueModel { return _model; }

public function set model(value:SimpleValueModel):void {
  if (_model) _model.removeEventListener('valueChanged', model_valueChangeListener);
  _model = value;
  if (_model) {
    _model.addEventListener('valueChanged', model_valueChangeListener);
  }
  setModel(_model);
}

protected function setModel(value:SimpleValueModel):void {
  if (value) {
    // value is set
  } else {
    // value is null. 
  }
}

protected function model_valueChangeListener(event:Event):void {
  setModel(_model);
}

The tricky part is now, to pass the references around. This is done best via dependency injection (another design pattern commonly used). The d.i. framework will take care of it (like robotlegs).

I hope I could provide some pointers.

Florian Salihovic
  • 3,921
  • 2
  • 19
  • 26
  • Thank you for your answer. I certainly have a lot of reading to do. Especially interested to find out about Robotlegs. I'd have liked to have given both of the current answers the tick mark but the site doesn't allow it. – Jerry Sep 06 '14 at 02:20