Navigating between panels in scala - swing

I want to know how to navigate between panels in scala swinng. the current code I have is:
val top = new MainFrame {
title = "Predator and Prey Agent simulation"
val buttonExit = new Button {
text = "Exit"
//foo
}
val buttonStart = new Button {
top.visible = false
text = "Play"
}
I want the buttonStart button to take me to another frame that I define in another class. How exactly do I implement that in scala. I get a recursive value error from what I have above.

Do you want to start a new window, or just switch the contents of the current window? If it's the latter, CardLayout is what you're looking for.
Which line in your example causes the error? I suspect it's the top.visible = false. This would be because the compiler needs to know the type of top but can't infer it because you have a reference to it in its definition. Adding a type annotation should fix this error:
val top: MainFrame = new MainFrame {

Related

Using Scala's Swing wrapper, how can I update a panel's contents via foreach with a collection?

I have a panel and a list full of filenames that reference .png files.
I can turn the string in any singe index of that list in to an image using:
val label = new Label {
icon = new ImageIcon(myList(0))
}
Then I can add that label to the panel:
object myGUI extends SimpleGUIApplication {
def top = new MainFrame {
title = "My simple GUI"
contents += label
}
}
but what if I want to construct a label for each entry in the list, and add each of those labels to myGUI?
I'd like to do so using foreach or some other idiomatic scala concept. If a different collection type makes more sense, I don't need to use a List. As I understand it, however, the collection processing functions are the same for an Array or Map as they are for a List so the collection type shouldn't matter (in the scope of this particular GUI related problem, anyway)
I'm not sure I understand correctly your question, but does this not work?:
object myGUI extends SimpleGUIApplication {
def top = new MainFrame {
title = "My simple GUI"
val list = ... //your list of filepaths
list foreach { filepath =>
contents += new Label { icon = new ImageIcon(filepath) }
}
}
}

Handle swing ScrollBar events in scala

I am trying to add a ScrollBar. The ScrollBar will iterate through the documents displayed. However, I am having trouble receiving an event when the scroll bar changes. I'm not sure what I need to listen to, and I'm not sure what event I should be responding to. I tried the following and I get some events, but I don't think these are the ideal events to handle.
listenTo(scrollBar)
listenTo(scrollBar.keys)
listenTo(scrollBar.mouse.moves)
listenTo(scrollBar.mouse.wheel)
listenTo(scrollBar.mouse.clicks)
For example, I only get MouseClicked, MousePressed, and MouseReleased when I click inside the scrollbar--not when I click on the arrows to actually change the value.
I found this discussion about scroll bars not receiving events properly, but it's two years old. As far as I can tell, the author did not follow up an file a ticket. Maybe he found a workaround.
Any ideas?
Good question. Clicking on arrows isn't handled by ScrollBar, it's handled by ScrollBarUI. I believe that default implementation(or at least base class for majority of ScrollBarUI implementations) is BasicScrollBarUI.
If you'll take a look into source of javax.swing.plaf.basic.BasicScrollBarUI it has incrButton and decrButton buttons and they are components you want listen to.
P.S. I had similar need for having custom (key) listener for my Slider and having custom ui which exposes needed components/model(as you could see almost all components are protected so you easy could access them in subclasses and expose via public getters) worked great for me. I did that in plain java though, maybe in scala you can listen to buttons just by specyfying property name.
One more piece of Scala Swing being broken. The Adjustable trait seems to be completely hollow, nothing is wired.
The following works:
class ScrollBarAlive extends swing.ScrollBar {
me =>
peer.addAdjustmentListener(new java.awt.event.AdjustmentListener {
def adjustmentValueChanged(e: java.awt.event.AdjustmentEvent) {
publish(new swing.event.ValueChanged(me))
}
})
}
Test:
import swing._
object ScrollBarTest extends SimpleSwingApplication {
lazy val top = new Frame {
val label = new Label { text = "0" }
val scroll = new ScrollBarAlive {
orientation = Orientation.Horizontal
listenTo(this)
reactions += {
case event.ValueChanged(_) =>
label.text = value.toString + (if (valueIsAjusting) " A" else "")
}
}
contents = new BorderPanel {
add(label, BorderPanel.Position.North)
add(scroll, BorderPanel.Position.South)
}
pack().centerOnScreen()
open()
}
}
A proper implementation would also introduce a subtype of AdjustingEvent.

Scala Swing Popup menu on ListView

What would be the best way of adding a mouseListener to a Scala Swing ListView that what ever item in the list is clicked on it'll create a PopupMenu with options pertaining to that specific item that is clicked on?
Am I stuck with doing this with Java style code for now or has Scala Swing evolved a bit more since 2.8.1
A bit of what I got currently and maybe I'm listening to the wrong thing and am over looking the ScalaDocs on the ListView.
lazy val ui = new FlowPanel {
val listView = ListView(items) {
renderer = Renderer(_.name)
listenTo(this.mouse.clicks)
reactions += {
case e: MouseClicked =>
// How do I determine what item was clicked?
}
}
}
lazy val ui = new FlowPanel {
val listView = new ListView( Seq("spam", "eggs", "ham") )
listenTo(listView.selection)
reactions += {
case SelectionChanged(`listView`) => println(listView.selection.items(0))
}
contents += listView
}
This should produce output such as
spam
spam
eggs
eggs
ham
ham
as you click on the various items. I've never done this before but I had a look at the UIDemo example which can be found in the scala.swing.test package. To read the source, if you have IntelliJ, it's as simple as clicking on the relevant object in the scala-swing.jar in External Libraries in the Projects pane.
As for PopupMenu, I don't know - it doesn't look like that one has a scala-swing wrapper in 2.9.1, but I found one on GitHub here. Or you could just use the normal Swing version.
This is unacceptably late, but I feel compelled to offer the "java-style" solution for anyone who might be interested (leaving aside the specific details of my implementation, the essence is in the 'reactions'):
val listView = new ListView[Int] {
selection.intervalMode = ListView.IntervalMode.Single
listData = (1 to 20).toList
listenTo(mouse.clicks)
reactions += {
case m : MouseClicked if m.clicks == 2 =>
doSomethingWith( listData(selection.anchorIndex) )
//where doSomethingWith is your desired result of this event
}
}
Assuming single interval mode, the key to getting the list item that was just double-clicked is in simply using anchorIndex.

Scala swing newbie questions

This is my first experiment using Swing with Scala, and have a few questions about my code below. All it does is to produce a window with a coloured rectangle that changes colour. Please feel free to answer one or any of the questions.
1) I used a Java ActionListener below because I couldn't work out how to get javax.swing.Timer to work as a Publisher. Is there a way to use the Scala model, i.e. listenTo() - or is this the way to do it?
2) My overridden preferredSize value in the Panel doesn't seem to work: the window comes up minimized. In my Java version I override the getPreferredSize method, but there is no such method in Panel, so I assumed this is the way to do it, but why doesn't it work?
3) paintComponent isn't documented at all in the Scala API documentation. I assume this is because it is protected access in Java, but it seems like an oversight. Am I correct to override paintComponent or is it hidden because I'm supposed to use the documented paint method instead?
4) Scala doesn't seem to have getWidth() and getHeight() methods on components - is it standard to use size.width and size.height?
import swing._
import java.awt.{Graphics, Color}
import java.awt.event.{ActionEvent, ActionListener}
import javax.swing.Timer
object ColorPanel extends SimpleSwingApplication {
private var c: Color = new Color(0)
def top = new MainFrame {
title = "Flash!"
contents = p
}
val p = new Panel with ActionListener {
override val preferredSize = new Dimension(200, 200)
override def paintComponent(g: Graphics2D) {
g.setColor(c)
g.fillRect(0, 0, size.width, size.height)
}
def actionPerformed(e: ActionEvent) {
c = new Color((c.getRGB() + 1000) % 16777216)
repaint
}
}
val timer = new Timer(100, p)
timer.start()
}
No immediate answer. But your approach is certainly ok. I don't see though why your observer should be the panel. I would create an anonymous ActionListener directly with the timer, and instead add a specific method to that panel, like def animateColor() { ... }
You can use preferredSize = new Dimension(200, 200)
According to this quasi official document, yes : http://www.scala-lang.org/sid/8 (section 6 Custom Painting)
A bit stupid indeed to instantiate a new Dimension all the time. But if you look exactly at the example the SID, it does the same, uses size.height. In super high performance code, you may want to call directly into the underlying peer (peer.getWidth)

Responding to key events in scala

I'm experimenting with a bit of Scala gui programming (my first project in scala, so I thought I'd start with something simple). But I seem to have got stuck at something that seems like it should be relatively trivial. I have a class that extends scala.swing.MainFrame, and I'd like to detect when a user presses a key when that window has focus. Funny thing is I don't seem to be able to find any way to get that event to fire.
I found an example of how someone else had got around the problem here: http://houseofmirrors.googlecode.com/svn/trunk/src/src/main/scala/HouseGui.scala but they seem to have reverted to using the Java Swing API, which is a little disappointing. Does anyone know if there's a more idiomatic way of intercepting events?
This seems to work with Scala 2.9
package fi.harjum.swing
import scala.swing._
import scala.swing.event._
import java.awt.event._
object KeyEventTest extends SimpleSwingApplication {
def top = new MainFrame {
val label = new Label {
text = "No click yet"
}
contents = new BoxPanel(Orientation.Vertical) {
contents += label
border = Swing.EmptyBorder(30,30,10,10)
listenTo(keys)
reactions += {
case KeyPressed(_, Key.Space, _, _) =>
label.text = "Space is down"
case KeyReleased(_, Key.Space, _, _) =>
label.text = "Space is up"
}
focusable = true
requestFocus
}
}
}
In addition to listening to this.keys you should also call requestFocus on the component or set focusable=true, if it is Panel or derived class.
I expect you need to listen to this.keys (where this is the element of the GUI receiving the keyboard events). See the equivalent question about mouse event.
My solution for this required me to do the following:
class MyFrame extends MainFrame {
this.peer.addKeyListener(new KeyListener() {
def keyPressed(e:KeyEvent) {
println("key pressed")
}
def keyReleased(e:KeyEvent) {
println("key released")
}
def keyTyped(e:KeyEvent) {
println("key typed")
}
})
}
This only seemed to work though if there were no button objects attached to this component, or any of it's children.
Rather than falling back to java events all components have keys that publishes these events (so MainFrame does not). Not sure what the best solution is but it's always possible to wrap everything in the frame inside a Component and listen to its keys.