4

I'm trying to implement the MVC design pattern in a Rubik's cube Scala application.

In Java, I would do this by adding an ActionListener to the buttons with the listeners in the controller class. In Scala, I've found this extremely difficult. Can anyone give me some examples of how to do this?

Andrew Swan
  • 13,427
  • 22
  • 69
  • 98
oscar
  • 41
  • 1
  • 2

1 Answers1

12

You can of course do it in the exact same way as you did in Java. Using Scala however, you can also use the Scala swing library to do this, which provides a set of wrappers around the Java Swing classes. It uses the concepts of publishers and reactors to observe and react to events. An introduction on the design of the library, including samples, can be found here.

The Publisher trait defines a publish(e: Event) method to notify all registered Reactors about an event. You can make any class a publisher by simply extending this trait, and call the publish method to publish your events. A reactor can be registered by using the method def listenTo(ps: Publisher), and deregistered by using def deafTo(ps: Publisher). When listening to a publisher, you can react to an event from this publisher by adding a reaction, which has the type PartialFunction[Event, Unit], as follows


  class MyComponent {
    listenTo(publisher)
    reactions += {
      case e: MyEvent => println("Got event " + e)
    }

  }

Here is some (fully incomplete) code using Scala-swing that hopefully provides you with an idea how to use this in a MVC pattern. Also you may want to check out the test package that is part of the library, where you can find a number of examples.


import scala.swing
import scala.swing.event._

case object MyBusinessEvent extends Event

class MyController extends Publisher {
    val form = new MyForm
    listenTo(form)
    reactions += {
      case MyBusinessEvent => //handle event code here
    }
}

class MyForm extends Publisher {
  val ui = new GridBagPanel {
    val c = new Constraints
    .... more code here
  }

  val button1 = new Button("Button 1") 
  //add button to panel


  listenTo(button1) 
  reactions += {
    case ButtonClicked(_) => publish(MyBusinessEvent)
  }  
}

The listenTo(button1) method in the form will notify the form about any button events. In this case it will react to the ButtonClicked event, which is an event that is defined in the scala-swing library. In this sample, the form just re-publishes the button event to some custom defined business event. The controller class in its turn listens to the form, and can react in an appropriate way to the business event.

zellus
  • 9,617
  • 5
  • 39
  • 56
Arjan Blokzijl
  • 6,878
  • 2
  • 31
  • 27
  • Gets my vote for greatest contrast between quality of question and answer! – Duncan McGregor Jun 07 '11 at 08:30
  • @Arjan thank you! this is in fact a great response, however it doesnt seem to work on my project, as my controller class and my ui class are on separeted files it also gives me a type error on the publish(event) line, i have uploaded my project here: [link](https://rapidshare.com/files/1639863150/rubik.rar) if u could take a look i would appreciate it so much.. untill then ill try to make it work.. thanks a lot! – oscar Jun 07 '11 at 14:32
  • @oscar: separated files cannot be any problem. From a quick glance at your code it seems that you call the publish(event) from within the MainFrame class. You should move that block to the top level RubikUI class in order for it to work properly. It would help if you just post the code and the error message in your question (you can edit it still), since this manner makes giving a proper answer a rather protracted experience. – Arjan Blokzijl Jun 07 '11 at 17:09
  • ok thx for ur help ive tried a couple of things but still no luck.. moved it out of the MainFrame but still get the same error. pattern type is incompatible with expected type; found : object rubik.MyEvent required: scala.swing.event.Event – oscar Jun 08 '11 at 00:53
  • @oscar: your custom event needs to inherit from the Event trait, as the error says. I updated the example for this. – Arjan Blokzijl Jun 08 '11 at 02:55
  • omg! it works! :D! ty so much! i could not have done it without you! my final project note is gonna be for you! haha thanks again! :) – oscar Jun 08 '11 at 16:51
  • i have found a new error while completing the code for the rest of the buttons, i made a new event b2Event for the second button of the app with its own publish(b2Event) and i get error: unreachable code case b2Event=> if i take it out the b2 button reacts the same as the b1 button... any suggestions? – oscar Jun 10 '11 at 16:40
  • i have changed the code to: sealed abstract class Evento(val id: Int )extends Event {} case object B1Event extends Evento(0) case object B2Event extends Evento(1) class RubikUI (modelo : modRubik, factory:Factory)extends Publisher{ now i dont get compiler errors however button2 still redirects to button1 behavior heres the project in case u need to take a look [link](https://rapidshare.com/files/3111727471/rubik.rar) – oscar Jun 10 '11 at 17:52
  • Once again, post you code in a question on SO, instead of on rapidshare. As for your question: most likely you haven't specified to which button you want to react. So you need to react to the ButtonClicked(`button`) (note the backticks) or ButtonClicked(`button2`) in order to differentiate – Arjan Blokzijl Jun 11 '11 at 05:08