Scala Swing REPL: Resolved
23 March 2011 Comments off
Reading time:
4 minutes
Word count:
836
Well after a night’s sleep and a good rest, which always sorts out the best engineers among us, I realised that the issue is my lack of brain cells on Scala’s Uniform Access Principle. If you noticed in yesterday’s program, the editor panes were created like this:-
def outputPane = new EditorPane { text="?" } def inputPane = new EditorPane { text="List(1,2,3,4,5)" } def splitPane = new SplitPane( Orientation.Horizontal, inputPane, outputPane )
So I am sorry to say, there is no pint of beer for you, because I resolved my own issue. However the definitions are actual method declarations. That is every time I referenced outputPane or inputPane in the older program I was calling a method that instantiated a EditorPane wrapper and for good measure a real JEditorPane. Of course in a real program that could disasterous if the underlying peer was a expensive resource to create. The solution is to change the def to a val declaration and therefore the Swing wrapper is created only once in the program.
val outputPane = new EditorPane { text="?" } val inputPane = new EditorPane { text="List(1,2,3,4,5)" } def splitPane = new SplitPane( Orientation.Horizontal, inputPane, outputPane )
In the end, there was no problem with Swing Scala or asynchronous concurrency threading in Java Swing bar the usual caveat emptor on the Event Dispatch Thread. The correct behaviour is shown in the program towards the end of this blog entry. Suffice to say, I would strongly recommend investing in the e-Book edition of Programming in Scala 2nd Edition, 2010 of Martin Odersky, Lex Spoon and Bill Venners from Artima press. I can particularly that the Kindle edition of book is worthwhile, it really formats well and it works out well. There are two chapter in the book that discuss the Scala Swing library in depth and, indeed, more than complement the free PDF in the Scala docs.
Now here is the working program. You need to add the program’s classpath the scala-compiler-2.8.1.jar and jline-0.9.91.jar from the Maven and Scala-Tools repos. I also changed EditorPane to TextArea from yesterday’s code extract:
package swingrepl import java.io._ import scala.swing._ import scala.swing.event._ import javax.swing.SwingUtilities import scala.tools.nsc._ import scala.tools.nsc.interpreter._ /** * Swing version of the REPL * User: Peter * Date: 22/03/11 * Time: 14:34 */ class SwingRepl { def create(): MainFrame = { new MainFrame { title = "Swing REPL (cc) XeNoNiQUe Peter Pilgrim - Very Basic Console" preferredSize = new Dimension(900, 900 ) contents = splitPane } } def debug( x: AnyRef ): String = { x match { case Nil => "nil" case null => "null" case _ => x.getClass().getName() +"@"+Integer.toHexString( java.lang.System.identityHashCode(x) ) } } val outputPane = new TextArea { editable = false text = "Text output" } System.out.println("outputPane="+debug( outputPane) ) System.out.println("outputPane.peer="+debug( outputPane.peer) ) val inputPane: TextArea = new TextArea { text = "List(1,2,3,4,5) map ( n => n * n )" editable = true listenTo(keys) reactions += { case e: KeyPressed => { // println("keyCode =" +e.peer.getKeyCode+", keyChar = "+e.peer.getKeyChar) if ( e.peer.isControlDown && e.key == Key.Enter ) { interpret( text ) } } case _ => } } System.out.println("inputPane="+debug( inputPane) ) def scrollInputPane = new ScrollPane( inputPane ) def scrollOutputPane = new ScrollPane( outputPane ) def splitPane = new SplitPane( Orientation.Horizontal, scrollInputPane, scrollOutputPane ) { dividerLocation = 100 } protected val swriter = new StringWriter() protected val pwriter = new PrintWriter( swriter ) { override def print( line: String ) { super.print( line ) System.out.println("print line=``"+line+"''") System.out.println("swriter.toString=<<"+swriter.toString+">>" ) SwingUtilities.invokeLater( new Runnable() { override def run(): Unit = { // outputPane.text = outputPane.text +"\n"+ line +"\n" System.out.println("3) outputPane="+debug( outputPane) ) System.out.println("4) outputPane.peer="+debug( outputPane.peer) ) System.out.println("BEFORE outputPane.text = "+outputPane.text) outputPane.text = swriter.toString System.out.println(" AFTER outputPane.text = "+outputPane.text) outputPane.peer.revalidate outputPane.peer.repaint() } }) } } protected val cmd = new InterpreterCommand(Nil, println ) System.out.println("cmd="+cmd) protected val settings = cmd.settings System.out.println("settings="+settings) settings.usejavacp.value = true protected val interpreter = new Interpreter(settings, pwriter ) { override def reset() = { super.reset; unleash() } override def unleash() = { super.unleash; } } System.out.println("interpreter="+interpreter) protected val completion = new Completion(interpreter) System.out.println("completion="+completion) def interpret(cmd: String): Unit = { System.out.println("replInvoker.interpret cmd="+cmd ) System.out.println("interpreter="+interpreter) interpreter.interpret(cmd) } } object SwingRepl extends SimpleSwingApplication { def top = new SwingRepl().create() }
No free beer 😉
PS: You will notice that I put in a simple debug() method to dump out the object reference. It helped me track down the issue and then eureka. As they say: everything appears obvious after the invention.
PS PS: I would love to repeat the exercise for JavaFX 2.0 Beta and all I need is the multi-line TextInput and the equivalent JTextArea output components.