I've done a fair bit of searching and some trial and error in eclipse, but there seems to be a gap in my understanding of listeners and reactions when writing a GUI in Scala using Swing.
Does each listener get a reactions block, or do I register listeners on all components that might generate an event and react to each on in a large reactions block with case statements?
Where exactly do the listeners and reaction blocks belong.
Here's an abbreviated version of my GUI code:
import scala.swing._
import scala.swing.event.ButtonClicked
import scala.swing.event.KeyTyped
import scala.swing.event.KeyPressed
object HumanGUI extends SimpleGUIApplication {
val basicPane = new java.awt.Dimension(800, 200)
val botPane = new java.awt.Dimension(400, 200)
val felt = new java.awt.Color(35, 125, 35)
def top = new MainFrame {
title = "Blackjack GUI"
val ConnectionPanel = new BoxPanel(Orientation.Vertical) {
background = felt
preferredSize = new java.awt.Dimension(155, 90)
minimumSize = preferredSize
maximumSize = preferredSize
val ipAddressLabel = new Label("House IP:")
ipAddressLabel.foreground = java.awt.Color.WHITE
ipAddressLabel.horizontalTextPosition = scala.swing.Alignment.Left
val portLabel = new Label("House port:")
portLabel.foreground = java.awt.Color.WHITE
portLabel.horizontalTextPosition = scala.swing.Alignment.Left
val ipAddressTextField = new TextField
val portTextField = new TextField
contents += ipAddressLabel
contents += ipAddressTextField
contents += portLabel
contents += portTextField
}
val DetailPanel = new BoxPanel(Orientation.Vertical) {
background = felt
preferredSize = new java.awt.Dimension(100, 160)
minimumSize = preferredSize
maximumSize = preferredSize
val nameLabel = new Label("Your name:")
nameLabel.foreground = java.awt.Color.WHITE
nameLabel.horizontalTextPosition = scala.swing.Alignment.Left
val bankrollLabel = new Label("Bankroll:")
bankrollLabel.foreground = java.awt.Color.WHITE
bankrollLabel.horizontalTextPosition = scala.swing.Alignment.Left
val betLabel = new Label("Bet:")
betLabel.foreground = java.awt.Color.WHITE
betLabel.horizontalTextPosition = scala.swing.Alignment.Left
val nameTextField = new TextField
val bankrollTextField = new TextField
val betTextField = new TextField
val goButton = new Button("Go!")
contents += nameLabel
contents += nameTextField
contents += bankrollLabel
contents += bankrollTextField
contents += betLabel
contents += betTextField
contents += goButton
}
val PlayPanel = new BoxPanel(Orientation.Vertical) {
background = felt
val hitButton = new Button("Hit")
val stayButton = new Button("Stay")
val doubleButton = new Button("Double")
val quitButton = new Button("Quit")
contents += hitButton
contents += stayButton
contents += doubleButton
contents += quitButton
}
val playerPanel = new BoxPanel(Orientation.Horizontal) {
background = felt
border = new javax.swing.border.LineBorder(java.awt.Color.WHITE)
preferredSize = basicPane
minimumSize = basicPane
maximumSize = basicPane
opaque = true
contents += ConnectionPanel
contents += DetailPanel
contents += PlayPanel
}
contents = new BoxPanel(Orientation.Vertical) {
contents += playerPanel
}
}
}
So the question is where do I put my listeners and reaction blocks?
I want to react to the buttons in PlayPanel, and the text fields in both ConnectionPanel and DetailPanel.
Do I put the listeners and reaction blocks as close to the elements that I'm interested as possible, or do I put a big block of listeners and reactions at the end of the MainFrame section?
Does it even matter?
EDIT
I've made significant progress and have much of what I need working, along with a better understanding of the concepts I wasn't getting before.
This excerpt from Odersky's "Programming in Scala" was what helped me the most. Specifically, the example from this page:
http://www.artima.com/pins1ed/gui-programming.html
The code is from the first edition of the text, so I question whether or not there's a better way in Scala 2.9, but it was clear an concise and summed up what I was misunderstanding.
From the example, which is a simple fahrenheit to celsius converter, I came to understand that the listener and the reactions blocks belongs after the contents block for the MainFrame.
so I ended up with:
object HumanGUI extends SimpleSwingGUIApplication {
def top = new MainFrame {
title = "My Blackjack GUI"
//The fields I want to work with are instantiated as object
object ipAddressTextField extends TextField { columns = 15 }
object portNumberTextField extends TextField {columns = 5 }
//other panels, objects, etc would go here
val OtherPanel = new BoxPanel(Orientation.Horizontal) {
label = "Other Panel"
}
//and here we have the contents += block for the mainframe, other panels, etc from
//above would be added to the main frame here
contents = new BoxPanel(Orientation.Vertical) {
contents += ipAddressTextField
contents += portNumberTextField
}
//here's the listen to, listening on the object created above, and it's enclosed in
//in backticks, a good explanation of that is found in the link below
listenTo(`ipAddressTextField`)
reactions += {
case EditDone('ipAddressTextField`) =>
//do something!
}
}
Need clarification on Scala literal identifiers (backticks)
So it seems that the answer to my question is that the listenTo and reactions blocks belong in the MainFrame block, but should appear after it's contents += { //contents } block.
Additional trial and error in eclipse shows that while this solution works for me, there is clearly much more that I don't understand. For example, while I was unable to get listeners for KeyPress events to work if I tried to listen and react to them in within the
val OtherPanel = new BoxPanel(Orientation.Horizontal) { }
portion of the above code, I was able to get a button registered and working like this:
val OtherPanel = new BoxPanel(Orientation.Horizontal) {
val betLabel = new Label("Bet:")
val betTextField = new TextField
val goButton = new Button("Go!")
listenTo(goButton)
reactions += {
case ButtonClicked(b) =>
betTextField.text = "Go!"
}
contents += betLabel
contents += betTextField
contents += goButton
}
Why this worked but my attempts to do something along the lines of
val OtherPanel = new BoxPanel(Orientation.Horizontal) {
val betLabel = new Label("Bet:")
val betTextField = new TextField
val goButton = new Button("Go!")
listenTo(betTextField)
reactions += {
case KeyTyped(betTextField, Enter, _, _) => {
println("Caught enter")
}
contents += betLabel
contents += betTextField
contents += goButton
}
didn't work is still baffling me. I'm assuming that it should work and I'm just doing something wrong. Perhaps that melding that approach with a case EditDone instead of a case KeyTyped(,,,) would have worked but I'm a little too burnt out right now to follow up on that.
I haven't accepted an answer yet because I'm hoping that someone who sees this can clarify the points I still don't understand. Should that not happen and the question remain unanswered for a few days I will likely accept #som-snytt's answer as his code was very helpful.
Swing is educational, and Scala-Swing is educational. Especially if the course is "History of Swing: The Rise and Fall."
My first Scala program also used Swing. I've forgotten the details, but I'll share what I can see in the source.
Apparently, I had a main UI component called LightBox that handled some UI events, and also a mediator component LightBoxMediator that coordinated.
The interesting part would be, using cake pattern for composition, and moving business logic (or game logic) interaction into a component that "mediates" for the UI proper. The LightBox publishes events, too.
So the answer to your question would be: exploit the publisher framework, but distinguish UI events from application events. (This little game also had actor-based controllers.)
Maybe this suffices to illustrate the separation of concerns:
/**
* Draws the House of Mirrors.
* The LightBox is just a list of rays (line segments) and gates (various objects).
* The UI emits requests to move and rotate gates.
*/
class LightBox extends Panel {
this.peer.addComponentListener(
new ComponentAdapter {
override def componentResized(e: ComponentEvent) {
if (e.getID == ComponentEvent.COMPONENT_RESIZED && e.getComponent == LightBox.this.peer) {
calculateScale()
}
}
}
)
listenTo(mouse.clicks, mouse.moves, mouse.wheel, keys)
reactions += {
case KeyPressed(_, Key.N, _, _) => highlightNextMoveableGate()
case KeyPressed(_, Key.P, _, _) => highlightPreviousMoveableGate()
case e: MousePressed => startDrag(e)
case e: MouseDragged => doDrag(e)
case e: MouseReleased => endDrag(e)
case e: MouseWheelMoved => wheeling(e)
case _ => null // println ("Unreacted event")
}
and the mediator
trait ViewComponents {
this: ControllerComponents with ModelComponents =>
val lightBoxMediator: LightBoxMediator
val statusBarMediator: StatusBarMediator
val statusIconMediator: StatusIconMediator
val applicationMediator: ApplicationMediator
/**
* Handles update notifications from the application
* and user input from the LightBox.
*/
class LightBoxMediator(val ui: LightBox) extends Reactor with Observing {
/** Attempt to track our selection across updates: the point is where the gate should end up. */
private var selectionContinuity: (Option[Gate], Option[Point]) = (None, None)
listenTo(ui, ui.keys, ui.mouse.clicks)
reactions += {
case KeyPressed(_, Key.Q, _, _) => sys.exit()
case KeyPressed(_, Key.Space, _, _) => rotateSelectedGate()
case KeyPressed(_, Key.Enter, _, _) => rotateOtherwiseSelectedGate()
case KeyPressed(_, Key.Up, _, _) => moveUp()
case KeyPressed(_, Key.Down, _, _) => moveDown()
case KeyPressed(_, Key.Left, _, _) => moveLeft()
case KeyPressed(_, Key.Right, _, _) => moveRight()
case KeyPressed(_, Key.PageUp, _, _) => previousLevel()
case KeyPressed(_, Key.PageDown, _, _) => nextLevel()
case DragEvent(from, to) => handleDrag(from, to)
case ClickEvent(where, button) => handleClick(where, button)
//case x => println("Unreacted event " + x)
}
observe(controller.modelEvents) { e => e match {
case LevelLoaded(v) => onLevelLoaded(v)
case TraceResult(s) => onTrace(s)
case unknown => println("Lightbox mediator ignored: "+ unknown)
}
true
}
Just noticed the additional questions. By coincidence, I was cleaning up old code, actually a tiny app to grab images from sfgate.com (which stopped working when they changed the site, of course; but usually you can right-click-save now), and I happened to notice the following comment about resubscribing. I vaguely remember the bit about UIElement being a LazyPublisher, because I remember the head slap. But if I hadn't written the meager comment, that info would have been lost to ancient history.
I think somebody wants to support scala-swing and will probably take care of the head slaps.
package com.maqicode.sfg.jfc
import java.awt.Color
import java.awt.Color.{WHITE => White, RED => Red}
import java.net.{URI, URISyntaxException}
import javax.swing._
import swing.TextField
import swing.event.{EditDone, MouseEntered, ValueChanged}
import com.maqicode.sfg.BadGateURLException
import com.maqicode.sfg.GateUrlTranslator.translate
abstract class URIField extends TextField {
reactions += {
case e: EditDone => editDone(e)
case other: ValueChanged => editing(other)
case m: MouseEntered => onMouseEntered()
case _ => null
}
// necessary to resubscribe this so that onFirstSubscribe registers ActionListener
listenTo(this, mouse.moves)
def onMouseEntered() {
val t: Option[String] = ClipboardInput.contents
if (t.isDefined && t.get != this.text) {
this.text = t.get
submitURL(t.get)
}
}
def editing(e: ValueChanged) {
clearError()
}
def editDone(e: EditDone) {
submitURL(this.text)
}
def submitURL(s: String) {
val u = s.trim
if (!u.isEmpty)
try {
submitURI(translate(new URI(u)))
clearError()
} catch {
case t: BadGateURLException => flagError()
case t: URISyntaxException => flagError()
}
}
def flagError() {
colorCode(Red)
}
def clearError() {
colorCode(White)
}
private def colorCode(c: Color) {
if (this.background != c) this.background = c
}
def submitURI(uri: URI): Unit
}
Related
I'm currently trying to code a minesweeper using scala, but I can't find the way to listen to a right click on a button.
I've already searched on the Internet the way to do it, but I definitely was not able to find it.
If anyone could help me out, I would be really grateful :)
Thanks,
Schnipp
(Note: Scala is a new language to me and I am not a Java user, so I am sorry if my questions sound dumb)
EDIT:
I am trying to find (or implement) a function 'ButtonClickedRight' that could listen to a right-click on a button.
like this
import scala.swing._
import scala._
import scala.swing.event._
object Right extends MainFrame with App {
title = ""
visible = true
val b = new button("")
listenTo(b)
reactions += {
case ButtonClicked(`b`) => *code*
case ButtonClickedRight(`b`) => *code*
}
}
EDIT 2 --
I would like to know if the user has clicked on the Button "1" or not. The problem I have is that this code prints "Mouse clicked at " + e.point+" type "+e.modifiers when I click on the label but not on the button.
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
contents += new Button("1")
contents += new Label("2")
listenTo(mouse.clicks)
reactions += {
case e: MouseClicked =>
println("Mouse clicked at " + e.point+" type "+e.modifiers)
}
}
def top = new MainFrame {
contents = ui
visible = true
preferredSize = new Dimension(500,500)
}
}
Button events are fired through a specific publisher .mouse.clicks.
import scala.swing._
import scala.swing.event._
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
val button = new Button("1")
contents += button
contents += new Label("2")
listenTo(button.mouse.clicks) // !
reactions += {
case evt # MouseClicked(`button`, pt, _, _, _) =>
val which = evt.peer.getButton
if (which > 1) {
println(s"Mouse clicked at (${pt.x}; ${pt.y}) - button: $which")
}
}
}
lazy val top = new MainFrame {
contents = ui
size = new Dimension(500,500)
}
}
Note that at least on Linux my right button has number 3 not 2. You could also use the triggersPopup flag, but then you must ensure to monitor both MousePressed and MouseReleased, as this flag is platform-dependent.
I think that you are on the right path, for my understanding of scala swings I think that the problem is that you are not attaching the listener correctly. For one I would assign the button to a value and call listenTo only on it:
val button = new Button("1")
listenTo(button)
Then, in the reactions, I would write the pattern checking in the event that it comes from the button (probably redundant if you only call listenTo passing the button) and that it has the correct button:
case ButtonClicked(b) if b == button && b.peer.getButton == MouseEvent.BUTTON_2 => ...
So the code you provided in your edit would become:
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
val button = new Button("1")
contents += button
contents += new Label("2")
listenTo(button)
reactions += {
case evt # MouseClicked(b, pt, _, _, _) if b == button && evt.peer.getButton == java.awt.event.MouseEvent.BUTTON2 =>
println(s"Mouse clicked at (${pt.x}; ${pt.y}) - button: ${evt.peer.getButton}")
}
}
def top = new MainFrame {
contents = ui
visible = true
preferredSize = new Dimension(500,500)
}
}
The following works for me:
new Button {
listenTo(mouse.clicks)
reactions += {
case MouseClicked(_, _, c, _, _) => handleClick(c == 0)
}
}
def handleClick(isLeftClick: Boolean): Unit = {
//
}
With reference to this,
How do I change the contents of a ListView in Scala?
I could change ListView contents by changing listData. However, I couldn't get ListView to publish these events, ListElementsAdded, ListElementsAdded and ListChanged. From the looks of ListView source, it would only adds a listener to a read-only empty model. How do I go about this?
Thanks
Later on, I managed to figure out a way to have ListView published these events., please refer to the code.
Is the right to go about it? Is there a better way to do this? Please advise.
Thanks
** code borrowed and modified **
object ListViewTest extends SimpleSwingApplication
{
lazy val top = new MainFrame
{
title = "ListView Test"
contents = new BoxPanel(Orientation.Vertical)
{
border = Swing.EmptyBorder(2, 2, 2, 2)
val listModel = new DefaultListModel
List("First", "Second", "Third", "Fourth", "Fifth").map(listModel.addElement(_))
val myList = ListBuffer()
val listView = new ListView[String](myList)
{
selection.intervalMode = ListView.IntervalMode.Single
peer.setModel(listModel)
//listData = myList
}
listView.peer.getModel.addListDataListener(new ListDataListener {
def contentsChanged(e: ListDataEvent) { publish(ListChanged(listView)) }
def intervalRemoved(e: ListDataEvent) { publish(ListElementsRemoved(listView, e.getIndex0 to e.getIndex1)) }
def intervalAdded(e: ListDataEvent) { publish(ListElementsAdded(listView, e.getIndex0 to e.getIndex1)) }
})
contents += new ScrollPane(listView)
val label = new Label("No selection")
contents += label
val b = new Button("Remove")
contents += b
listenTo(listView.selection, listView, b)
reactions +=
{
case ListSelectionChanged(list, range, live) =>
label.text = "Selection: " + range
case e: ButtonClicked =>
if (listView.listData.isEmpty)
{
b.enabled = false
}
else
{
listView.peer.getModel.asInstanceOf[DefaultListModel].remove(listView.selection.anchorIndex)
}
case ListElementsRemoved(source, range) =>
println("Element at " + (range.start + 1) + " is removed.")
}
}
pack
}
I'm looking for a scala trait that I can mix in to a scala.swing.Component that will allow that component to be positioned and resized using mouse input.
Ideally it would add little boxes as "handles" to indicate to the user that the component can be resized:
I feel like this is a fairly common task, and there should be some traits out there that support it.
I'm using these in my current project. You probably need to replace the Vector library with your own and add implicit defs. Or use Point/Dimension from swing. The Components need to be in a panel which allows custom positions and sizes, like NullPanel from http://dgronau.wordpress.com/2010/08/28/eine-frage-des-layouts/
trait Movable extends Component{
var dragstart:Vec2i = null
listenTo(mouse.clicks, mouse.moves)
reactions += {
case e:MouseDragged =>
if( dragstart != null )
peer.setLocation(location - dragstart + e.point)
case e:MousePressed =>
this match {
case component:Resizable =>
if( component.resizestart == null )
dragstart = e.point
case _ =>
dragstart = e.point
}
case e:MouseReleased =>
dragstart = null
}
}
trait Resizable extends Component{
var resizestart:Vec2i = null
var oldsize = Vec2i(0)
def resized(delta:Vec2i) {}
listenTo(mouse.clicks, mouse.moves)
reactions += {
case e:MouseDragged =>
if( resizestart != null ){
val delta = e.point - resizestart
peer.setSize(max(oldsize + delta, minimumSize))
oldsize += delta
resizestart += delta
resized(delta)
revalidate
}
case e:MousePressed =>
if( size.width - e.point.x < 15
&& size.height - e.point.y < 15 ){
resizestart = e.point
oldsize = size
this match {
case component:Movable =>
component.dragstart = null
case _ =>
}
}
case e:MouseReleased =>
resizestart = null
}
}
The only Component I know of that is Dragable and resizeable is the InternalFrame on the JDesktop. here is an example:
import swing._
import event._
import javax.swing.{UIManager,JComponent}
import javax.swing.KeyStroke.getKeyStroke
import java.awt.{Graphics2D,Graphics}
object InternalFrameDemo extends SimpleSwingApplication{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
val top = new MainFrame{
title = "InternalFrameDemo"
preferredSize = new Dimension(640,480)
val desktop = new javax.swing.JDesktopPane
val jc = new JComponent{
override def paint(g:Graphics){
import g._
drawLine(0,0,20,20)
drawLine(0,20,20,0)
println("yay draw")
}
setVisible(true)
}
desktop add jc
contents = Component.wrap(desktop)
menuBar = new MenuBar{
contents += new Menu("Document"){
mnemonic = Key.D
contents += new MenuItem("New"){
mnemonic = Key.N
action = new Action("new"){
def apply = createFrame
accelerator = Some(getKeyStroke("alt N"))
}
}
contents += new MenuItem("Quit"){
mnemonic = Key.Q
action = new Action("quit"){
def apply(){
quit()
}
accelerator = Some(getKeyStroke("alt Q"))
}
}
}
}
def createFrame{
val newFrame = MyInternalFrame()
newFrame setVisible true
desktop add newFrame
newFrame setSelected true
}
}
}
import javax.swing.{JDesktopPane,JInternalFrame}
import collection.mutable.ArrayBuffer
object MyInternalFrame{
var openFrameCount = 0;
val xOffset, yOffset = 30;
def apply() = {
openFrameCount += 1
val jframe = new javax.swing.JInternalFrame("Document #" + openFrameCount,true,true,true,true)
jframe.setSize(300,300)
jframe.setLocation(xOffset*openFrameCount,yOffset*openFrameCount)
jframe //Component.wrap(jframe)
}
}
but both JInternalFrame and JDesktop are not integrated in the scala swing package and need to be wrapped manually.
I have an iterator (actually a Source.getLines) that's reading an infinite stream of data from a URL. Occasionally the iterator throws a java.io.IOException when there is a connection problem. In such situations, I need to re-connect and re-start the iterator. I want this to be seamless so that the iterator just looks like a normal iterator to the consumer, but underneath is restarting itself as necessary.
For example, I'd like to see the following behavior:
scala> val iter = restartingIterator(() => new Iterator[Int]{
var i = -1
def hasNext = {
if (this.i < 3) {
true
} else {
throw new IOException
}
}
def next = {
this.i += 1
i
}
})
res0: ...
scala> iter.take(6).toList
res1: List[Int] = List(0, 1, 2, 3, 0, 1)
I have a partial solution to this problem, but it will fail on some corner cases (e.g. an IOException on the first item after a restart) and it's pretty ugly:
def restartingIterator[T](getIter: () => Iterator[T]) = new Iterator[T] {
var iter = getIter()
def hasNext = {
try {
iter.hasNext
} catch {
case e: IOException => {
this.iter = getIter()
iter.hasNext
}
}
}
def next = {
try {
iter.next
} catch {
case e: IOException => {
this.iter = getIter()
iter.next
}
}
}
}
I keep feeling like there's a better solution to this, maybe some combination of Iterator.continually and util.control.Exception or something like that, but I couldn't figure one out. Any ideas?
This is fairly close to your version and using scala.util.control.Exception:
def restartingIterator[T](getIter: () => Iterator[T]) = new Iterator[T] {
import util.control.Exception.allCatch
private[this] var i = getIter()
private[this] def replace() = i = getIter()
def hasNext: Boolean = allCatch.opt(i.hasNext).getOrElse{replace(); hasNext}
def next(): T = allCatch.opt(i.next).getOrElse{replace(); next}
}
For some reason this is not tail recursive but it that can be fixed by using a slightly more verbose version:
def restartingIterator2[T](getIter: () => Iterator[T]) = new Iterator[T] {
import util.control.Exception.allCatch
private[this] var i = getIter()
private[this] def replace() = i = getIter()
#annotation.tailrec def hasNext: Boolean = {
val v = allCatch.opt(i.hasNext)
if (v.isDefined) v.get else {replace(); hasNext}
}
#annotation.tailrec def next(): T = {
val v = allCatch.opt(i.next)
if (v.isDefined) v.get else {replace(); next}
}
}
Edit: There is a solution with util.control.Exception and Iterator.continually:
def restartingIterator[T](getIter: () => Iterator[T]) = {
import util.control.Exception.allCatch
var iter = getIter()
def f: T = allCatch.opt(iter.next).getOrElse{iter = getIter(); f}
Iterator.continually { f }
}
There is a better solution, the Iteratee:
http://apocalisp.wordpress.com/2010/10/17/scalaz-tutorial-enumeration-based-io-with-iteratees/
Here is for example an enumerator that restarts on encountering an exception.
def enumReader[A](r: => BufferedReader, it: IterV[String, A]): IO[IterV[String, A]] = {
val tmpReader = r
def loop: IterV[String, A] => IO[IterV[String, A]] = {
case i#Done(_, _) => IO { i }
case Cont(k) => for {
s <- IO { try { val x = tmpReader.readLine; IO(x) }
catch { case e => enumReader(r, it) }}.join
a <- if (s == null) k(EOF) else loop(k(El(s)))
} yield a
}
loop(it)
}
The inner loop advances the Iteratee, but the outer function still holds on to the original. Since Iteratee is a persistent data structure, to restart you just have to call the function again.
I'm passing the Reader by name here so that r is essentially a function that gives you a fresh (restarted) reader. In practise you will want to bracket this more effectively (close the existing reader on exception).
Here's an answer that doesn't work, but feels like it should:
def restartingIterator[T](getIter: () => Iterator[T]): Iterator[T] = {
new Traversable[T] {
def foreach[U](f: T => U): Unit = {
try {
for (item <- getIter()) {
f(item)
}
} catch {
case e: IOException => this.foreach(f)
}
}
}.toIterator
}
I think this very clearly describes the control flow, which is great.
This code will throw a StackOverflowError in Scala 2.8.0 because of a bug in Traversable.toStream, but even after the fix for that bug, this code still won't work for my use case because toIterator calls toStream, which means that it will store all items in memory.
I'd love to be able to define an Iterator by just writing a foreach method, but there doesn't seem to be any easy way to do that.
how can I handle a situation, where a filed listens to a buttons that is not declared yet?
val detail = new BoxPanel(Orientation.Vertical){
listenTo(button)
}
val seznam = new BoxPanel(Orientation.Vertical){
val button = new Button("But"){
reactions += {
case ButtonClicked(_) =>
detail.contents.clear
detail.contents += new Label("Anystring")
}
}
I can't declare seznam first either, because it reference the field detail. So how can I write this?
listenTo is a public method. The easiest thing to do, therefore, is to create them as you've shown above, but add detail.listenTo(button) after you've created the button:
val detail = new BoxPanel(Orientation.Vertical){ }
val seznam:BoxPanel = new BoxPanel(Orientation.Vertical){
val button = new Button("But"){
reactions += {
case ButtonClicked(_) =>
detail.contents.clear
detail.contents += new Label("Anystring")
}
}
detail.listenTo(button)
}