Question

I'm trying to understand how the following class works taken from an answer from this thread: Scala Popup Menu

Since the thread is pretty old I decided to just start a new question. I'm new to Scala with a Java background and I'm wondering how this class works. I read that an object with the same name as a class is like a class with a singleton object? I'm not sure how this fits in to achieving the wrapper though.. (why do we need the object?)

And what exactly does the SuperMixin trait do? The API says "This trait is used to redirect certain calls from the peer to the wrapper and back. Useful to expose methods that can be customized by overriding." which doesn't do a very good job of explaining to a beginner.

I would really appreciate it if someone can help explain to a beginner how this class and object (it seems to me, magically) get me a wrapper class for the JPopupMenu and lets me call the show method which makes a popupMenu appear on the screen.. and it also seems like I can set its content (contents+= some scala.swing.menuItem) without it being defined in the class below?

import javax.swing.JPopupMenu
import scala.swing.{ Component, MenuItem }
import scala.swing.SequentialContainer.Wrapper

object PopupMenu {
  private[PopupMenu] trait JPopupMenuMixin { def popupMenuWrapper: PopupMenu }
}

class PopupMenu extends Component with Wrapper {

  override lazy val peer: JPopupMenu = new JPopupMenu with PopupMenu.JPopupMenuMixin with SuperMixin {
    def popupMenuWrapper = PopupMenu.this
  }

  def show(invoker: Component, x: Int, y: Int): Unit = peer.show(invoker.peer, x, y)

  /* Create any other peer methods here */
}
Was it helpful?

Solution

The companion object for PopupMenu doesn't serve any specific purpose here except functioning as a name space for the auxiliary trait JPopupMenuMixin. This trait can then be "hidden" by making it private[PopupMenu], so it is only known by class PopupMenu and its companion object.

Frankly, I don't see the purpose of that trait. It defines a method popupMenuWrapper pointing to the outer Scala Swing component, but that method is not used at all. So a less confusing version would be simply:

import scala.swing._
import javax.swing.JPopupMenu

class PopupMenu extends Component with SequentialContainer.Wrapper {
  override lazy val peer: JPopupMenu = new JPopupMenu with SuperMixin

  def show(invoker: Component, x: Int, y: Int): Unit = peer.show(invoker.peer, x, y)
}

Test:

val pop = new PopupMenu {
  contents += new MenuItem("Foo")
}
lazy val but: Button = Button("Test") {
  pop.show(but, 0, 0)
}
val f = new Frame {
  contents = but
  pack().centerOnScreen()
  open()
}

The only thing necessary for a wrapper is to extend scala.swing.Component and override the peer value with the underlying javax.swing component. The mixin with SuperMixin overrides a few methods of that component, such as paintComponent, in order to forward them to the outer wrapper component. That's all.

The wrapper mixes in SequentialContainer.Wrapper which allows for the contents += operation to add the menu items.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top