Scala Swing: How to pan an image using your mouse? - swing

I have been trying to replicate some Java Swing work in Scala. In the code below this extends Scala Swings ScrollPane.
myPanel.reactions +={
case MouseDragged(_,x,_) => {
//val viewport = this.viewportView.get.asInstanceOf[JViewport]
val viewport = this.contents.head.self.asInstanceOf[JComponent].getParent.asInstanceOf[JViewport]
println(viewport.getViewPosition)
val vp = viewport.getViewPosition
var cp = x.getLocation
vp.translate(point.x-cp.x,point.y-cp.y)
//newLoc = new Point(vp.getX)
val jComp = contents.head.asInstanceOf[JComponent]
jComp.scrollRectToVisible(new Rectangle(vp,viewport.getSize: Dimension))
}
case MousePressed(_,x,_,_,_) => point = x.getLocation
case MouseClicked(_,_,_,_,_) => println("click")
}
I can't see how to get access to the Viewport object as in the Java code in the related question. How should this be implemented in Scala Swing?

myPanel.reactions +={
case MouseDragged(myPanel,x,_) => {
val cp = x.getLocation
this.horizontalScrollBar.value += point.x-cp.x
this.verticalScrollBar.value += point.y-cp.y
point = cp
}
case MousePressed(_,x,_,_,_) => point = x.getLocation
}

Related

Generate dynamic bundle

In my project, I have many different custom Bundle.
They can be completely different.
For example these ones:
class MyBundle extends Bundle {
val v1 = UInt(8.W)
val v2 = Bool()
val v3 = UInt(4.W)
}
class MyBundle2 extends Bundle {
val v4 = UInt(18.W)
val v5 = UInt(2.W)
}
...
Instead of manually creating new Bundle to perform each operation, I want to be able to generate for all of them the corresponding Bundle.
So for MyBundle, I want to do:
// Must be generated for each Bundle
class MyGenBundle extends Bundle {
val v1 = UInt(log2Ceil(8 + 1).W) // width = MyBundle.v1 width + 1
val v2 = UInt(log2Ceil(1 + 1).W) // width = MyBundle.v2 width + 1
val v3 = UInt(log2Ceil(4 + 1).W) // width = MyBundle.v3 width + 1
}
class MyModule extends Module {
...
...
val w_b = Wire(new MyBundle())
val w_gb = Wire(new MyGenBundle())
// Must be automated for each Bundle
w_gb.v1 := PopCount(w_b.v1)
w_gb.v2 := PopCount(w_b.v2)
w_gb.v3 := PopCount(w_b.v3)
}
My goal is to automatically generate MyGenBundle (or similar directly in MyModule) from MyBundle, and perform in MyModule the same operation to all signals.
It also means that I need to dynamically address all signals in each Bundle.
Finally, I think the solution can have the following form:
val w_b = Wire(new MyBundle())
val w_gb = Wire(new AutoGenBundle(new MyBundle())) // AutoGenBundle generates a Bundle from parameter
val v_sig = Seq(v1, v1, v3) // Can be recovered automatically
// from w_b.elements I think
foreach (s <- v_sig) {
w_gb."s" := PopCount(w_b."s") // Here "s" the dynamic name of the signal
} // But how in the real case ?
Is this working technically possible in Chisel/Scala?
If so, do you have any ideas for implementing it?
Basic solution is to use the MixedVec chisel type for GenericBundle
class MyBundle extends Bundle {
val v1 = UInt(8.W)
val v2 = Bool()
val v3 = UInt(4.W)
}
class GenericBundle[T <: Bundle](data: T) extends Bundle {
val v = MixedVec(data.elements.values.map(x => UInt(log2Ceil(x.getWidth + 1).W)).toList)
def popCount(data: T): Unit = {
data.elements.values.zip(v).foreach { case (left, right) =>
right := PopCount(left.asUInt())
}
}
}
class MyModule extends MultiIOModule {
val w_b = IO(Input(new MyBundle))
val w_gb = IO(Output(new GenericBundle(w_b)))
w_gb.popCount(w_b)
}

Comma separated list with Enumerator

I've just started working with Scala in my new project (Scala 2.10.3, Play2 2.2.1, Reactivemongo 0.10.0), and encountered a pretty standard use case, which is - stream all the users in MongoDB to the external client. After navigating Enumerator, Enumeratee API I have not found a solid solution for that, and so I solved this in following way:
val users = collection.find(Json.obj()).cursor[User].enumerate(Integer.MAX_VALUE, false)
var first:Boolean = true
val indexedUsers = (users.map(u => {
if(first) {
first = false;
Json.stringify(Json.toJson(u))
} else {
"," + Json.stringify(Json.toJson(u))
}
}))
Which, from my point of view, is a little bit tricky - mainly because I needed to add Json Start Array, Json End Array and comma separators in element list, and I was not able to provide it as a pure Json stream, so I converted it to String steam.
What is a standard solution for that, using reactivemongo in play?
I wrote a helper function which does what you want to achieve:
def intersperse[E](e: E, enum: Enumerator[E]): Enumerator[E] = new Enumerator[E] {
val element = Input.El(e)
override def apply[A](i1: Iteratee[E, A]): Future[Iteratee[E, A]] = {
var iter = i1
val loop: Iteratee[E, Unit] = {
lazy val contStep = Cont(step)
def step(in: Input[E]): Iteratee[E, Unit] = in match {
case Input.Empty ⇒ contStep
case Input.EOF ⇒ Done((), Input.Empty)
case e # Input.El(_) ⇒
iter = Iteratee.flatten(iter.feed(element).flatMap(_.feed(e)))
contStep
}
lazy val contFirst = Cont(firstStep)
def firstStep(in: Input[E]): Iteratee[E, Unit] = in match {
case Input.EOF ⇒ Done((), Input.Empty)
case Input.Empty ⇒
iter = Iteratee.flatten(iter.feed(in))
contFirst
case Input.El(x) ⇒
iter = Iteratee.flatten(iter.feed(in))
contStep
}
contFirst
}
enum(loop).map { _ ⇒ iter }
}
}
Usage:
val prefix = Enumerator("[")
val suffix = Enumerator("]")
val asStrings = Enumeratee.map[User] { u => Json.stringify(Json.toJson(u)) }
val result = prefix >>> intersperse(",", users &> asStrings) >>> suffix
Ok.chunked(result)

Listeners and reactions in Scala Swing

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
}

Scala Lift - Dynamically called function

I've got a function which loads various models, and currently have this kind of setup:
if(message == "user") {
var model = User.findAll(
("room" -> "demo")
)
} else if (message == "chat") {
var model = Chat.findAll(
("room" -> "demo")
)
}
This is really clunky as I aim to add lots more models in future, I know in javascript you can do something like this:
var models = {
"user" : load_user,
"chat" : load_chat
}
Where "load_user" and "load_chat" would load the respective models, and so I can streamline the whole thing by doing:
var model = models[message]();
Is there a way I can do something similar in Scala, so I can have a simple function which just passes the "message" var to a List or Object of some kind to return the relevant data?
Thanks in advance for any help, much appreciated :)
In Scala you can do:
val model = message match {
case "user" => loadUser() // custom function
case "chat" => loadChat() // another custom function
case _ => handleFailure()
}
You can as well work with a Map like you did in your JavaScript example like so:
scala> def loadUser() = 1 // custom function
loadUser: Int
scala> def loadChat() = 2 // another custom function
loadChat: Int
scala> val foo = Map("user" -> loadUser _, "chat" -> loadChat _)
foo: scala.collection.immutable.Map[java.lang.String,() => Int] = Map(user -> <function0>, chat -> <function0>)
scala> foo("user")()
res1: Int = 1
Pay attention to the use of "_" in order to prevent evaluation of loadUser or loadChat when creating the map.
Personally, I'd stick with pattern matching.

scala swing: draggable / resizable component trait

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.