Scala Swing: how to get child component form Borderpanel - scala

How do I get the child component for a particular position on a Scala Swing BorderPanel? I can place components OK. In this particular case I want to check the component at BorderPanel.Position.Centre. Also am I right that there can only be one child component or a null at each position: North, East etc?

Well, the layout is a Map from component to position, so the following works (not sure if there's an easier way):
import scala.swing.SimpleSwingApplication
import scala.swing.MainFrame
import scala.swing.BorderPanel
import scala.swing.Label
object BorderLayoutTest extends SimpleSwingApplication {
def top = new MainFrame {
contents = new BorderPanel {
val label = new Label("hi")
import BorderPanel.Position._
layout(label) = Center
println("North: " + layout.find(_._2 == North)) //None
println("Center: "+ layout.find(_._2 == Center)) //Some( ... )
}
}
}

Related

Scala How to change window size

I would like to change the size of my window application. I am using scene builder for this project. In the preview, the size of my fxml is the size that I want. However, when I try to run it with my windows it will show the application in a slightly small size. My question is how can I make the window application size the same as the size of the preview. In another word, I want to make my window application to the exact size that I want. Below attach my code.
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.Scene
import scalafx.Includes._
import scalafxml.core.{NoDependencyResolver, FXMLView, FXMLLoader}
import javafx.{scene => jfxs}
import scalafx.scene.image.Image
object MainApp extends JFXApp {
// transform path of RootLayout.fxml to URI for resource location.
val rootResource = getClass.getResource("view/RootLayout.fxml")
// initialize the loader object.
val loader = new FXMLLoader(rootResource, NoDependencyResolver)
// Load root layout from fxml file.
loader.load();
// retrieve the root component BorderPane from the FXML
val roots = loader.getRoot[jfxs.layout.BorderPane]
// initialize stage
stage = new PrimaryStage {
title = "TalkShow"
scene = new Scene {
root = roots
}
}
// actions for display person overview window
def showTalkOverview() = {
val resource = getClass.getResource("view/TalkShow.fxml")
val loader = new FXMLLoader(resource, NoDependencyResolver)
loader.load();
val roots = loader.getRoot[jfxs.layout.AnchorPane]
this.roots.setCenter(roots)
}
def showWelcome() = {
val resource = getClass.getResource("view/Welcome.fxml")
val loader = new FXMLLoader(resource, NoDependencyResolver)
loader.load();
val roots = loader.getRoot[jfxs.layout.AnchorPane]
this.roots.setCenter(roots)
}
// call to display PersonOverview when app start
showWelcome()
}

Scala Swing skips repaint of the Frame

I am currently working on an implementation of the game Othello in Scala and so far it's working pretty nicely. When implementing the GUI though (using Scala Swing) I've stumbled across an issue that I can't seem to fix.
When playing against the computer opponent the Frame seems to be repainted only when the bot is done making its move.
The game is also playable through the terminal and doing so updates the Frame properly every time regardless of the player configuration (Player vs Player or Player vs Computer). Also playing player vs player using the GUI exclusively presents no issues at all.
It might be an oversight on my behalf, but so far I am unable to find a solution and would greatly appreciate any help.
So far I have tried various combinations of revalidating and repainting the individual panels, adding and removing listeners, changing my implementation of the reactor pattern to the one provided by Scala Swing, adding Thread.sleep to see if there could be a scheduling conflict of sorts.
import java.awt.Color
import othello.controller.Controller
import javax.swing.ImageIcon
import javax.swing.border.LineBorder
import scala.swing.event.MouseClicked
import scala.swing.{BorderPanel, BoxPanel, Dimension, FlowPanel, GridPanel, Label, Orientation}
class TablePanel(controller: Controller) extends FlowPanel {
val sides = 32
val sidesColor: Color = Color.lightGray
val squareSize = 52
def tableSize: Int = controller.board.size
def edgeLength: Int = tableSize * squareSize
def rows: BoxPanel = new BoxPanel(Orientation.Vertical) {
background = sidesColor
preferredSize = new Dimension(sides, edgeLength)
contents += new Label {
preferredSize = new Dimension(sides, sides)
}
contents += new GridPanel(tableSize, 1) {
background = sidesColor
for { i <- 1 to rows } contents += new Label(s"$i")
}
}
def columns: GridPanel = new GridPanel(1, tableSize) {
background = sidesColor
preferredSize = new Dimension(edgeLength, sides)
for { i <- 0 until columns } contents += new Label(s"${(i + 65).toChar}")
}
def table: GridPanel = new GridPanel(tableSize, tableSize) {
background = new Color(10, 90, 10)
for {
col <- 0 until columns
row <- 0 until rows
} contents += square(col, row)
}
def square(row: Int, col: Int): Label = new Label {
border = new LineBorder(new Color(30, 30, 30, 140), 1)
preferredSize = new Dimension(squareSize, squareSize)
icon = controller.board.valueOf(col, row) match {
case -1 => new ImageIcon("resources/big_dot.png")
case 0 => new ImageIcon("resources/empty.png")
case 1 => new ImageIcon("resources/black_shadow.png")
case 2 => new ImageIcon("resources/white_shadow.png")
}
listenTo(mouse.clicks)
reactions += {
case _: MouseClicked =>
if (controller.options.contains((col, row))) controller.set(col, row)
else if (controller.board.gameOver) controller.newGame()
else controller.highlight()
}
}
def redraw(): Unit = {
contents.clear
contents += new BorderPanel {
add(rows, BorderPanel.Position.West)
add(new BoxPanel(Orientation.Vertical) {
contents += columns
contents += table
}, BorderPanel.Position.East)
}
repaint
}
}
import scala.swing._
import othello.controller._
import othello.util.Observer
import scala.swing.event.Key
class SwingGui(controller: Controller) extends Frame with Observer {
controller.add(this)
lazy val tablePanel = new TablePanel(controller)
lazy val mainFrame: MainFrame = new MainFrame {
title = "Othello"
menuBar = menus
contents = tablePanel
centerOnScreen
// peer.setAlwaysOnTop(true)
resizable = false
visible = true
}
def menus: MenuBar = new MenuBar {
contents += new Menu("File") {
mnemonic = Key.F
contents += new MenuItem(Action("New Game") {
controller.newGame()
})
contents += new MenuItem(Action("Quit") {
controller.exit()
})
}
contents += new Menu("Edit") {
mnemonic = Key.E
contents += new MenuItem(Action("Undo") {
controller.undo()
})
contents += new MenuItem(Action("Redo") {
controller.redo()
})
}
contents += new Menu("Options") {
mnemonic = Key.O
contents += new MenuItem(Action("Highlight possible moves") {
controller.highlight()
})
contents += new MenuItem(Action("Reduce board size") {
controller.resizeBoard("-")
})
contents += new MenuItem(Action("Increase board size") {
controller.resizeBoard("+")
})
contents += new MenuItem(Action("Reset board size") {
controller.resizeBoard(".")
})
contents += new Menu("Game mode") {
contents += new MenuItem(Action("Player vs. Computer") {
controller.setupPlayers("1")
})
contents += new MenuItem(Action("Player vs. Player") {
controller.setupPlayers("2")
})
}
}
}
def update: Boolean = {
tablePanel.redraw()
mainFrame.pack
mainFrame.centerOnScreen
mainFrame.repaint
true
}
}
The expected behavior is a repainted Frame on every turn. The actual result is the Frame only being repainted after the opponent made a move. It only happens when playing player vs bot exclusively through clicking the UI.
I don't think the problem is in the code you have shown, but I would bet that you are blocking the "event dispatch thread" (the UI thread) with your AI computation for the computer player.
In a Swing app, there is a special thread called the "event dispatch thread" that is responsible for handling messages from the O/S including dealing with repaint messages. All UI event handlers will be invoked on this thread. If you use that thread to do any computations which take a long time (such as a computer move in a game like this), any UI updates will be blocked until the thread becomes free.
This tutorial has more info: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html
You need to move the AI onto a background thread and release the event dispatch thread to deal with repaints. This might be tricky to implement if you are not familiar with multi-threaded programs! Good luck.
As Rich suggested, the solution was concurrency. Making the search algorithm a Future-type solved the issue for now. It might not be the cleanest implementation as it is the first time I'm using Future, but this is what it looks like now:
def selectAndSet(): Future[_] = Future(if (!board.gameOver && player.isBot) {
new MoveSelector(this).select() match {
case Success(square) => set(square)
case _ => omitPlayer()
}
selectAndSet()
})(ExecutionContext.global)

ScrollPane automatically scrolls down, but not all the way down in Scala Swing

I'm having an issue with a project I'm working on. The original code is too big to poste here but I wrote a short version which has the same issue.
Basically, I wrote some kind of chatbox that adds Labels containing the replies to a BoxPanel, one after another. Ideally the user wouldn't need to scroll manually, the scrolling would happen automatically and show the very last reply of the chat.
The issue is that when using scrollBar.peer.setValue(scrollBar.peer.getMaximum),
the chat doesn't automatically scroll all the way down, it never shows the very last line of the chat, even after I revalidate() the chat before scrolling down.
this is what the code looks like:
import scala.swing._
import scala.swing.event._
class testUI extends MainFrame {
title = "testScroll"
preferredSize = new Dimension(400, 400)
val chat = new BoxPanel(Orientation.Vertical)
val inputField = new TextField {
listenTo(mouse.clicks, this.keys)
reactions += {
case KeyPressed(_, Key.Enter, _, _) => if (this.text != "") { printToChat(this.text) }
}
}
val scrollPanel = new ScrollPane(chat)
var scrollBar = scrollPanel.verticalScrollBar
val window = new BorderPanel {
add(inputField, BorderPanel.Position.North)
add(scrollPanel, BorderPanel.Position.Center)
}
contents = window
def printToChat(s: String) {
chat.contents += new Label(s) {
horizontalAlignment = Alignment.Right
border = Swing.EmptyBorder(20, 20, 0, 0) //(top, left, bottom, right)
}
chat.revalidate()
scrollToBottom()
inputField.text = ""
}
def scrollToBottom() {
scrollBar.peer.setValue(scrollBar.peer.getMaximum)
}
}
object testUI {
def main(args: Array[String]) {
val ui = new testUI
ui.visible = true
}
}
Thank you very much in advance :)
EDIT: I solved my issue thanks to camickr by wrapping my previous line of code with Swing.onEDT:
Swing.onEDT(scrollBar.peer.setValue(scrollBar.peer.getMaximum))
instead of just:
scrollBar.peer.setValue(scrollBar.peer.getMaximum)

vaadin + scala : automatic push makes me waiting

I would like to perform a simple change of the text of a label, using vaadin 7 & scala.
here is my UI:
package com.example.scaladinchat
import com.github.nscala_time.time.Imports._
import vaadin.scala.UI
import vaadin.scala.VerticalLayout
import vaadin.scala.Label
import vaadin.scala.server.ScaladinRequest
import vaadin.scala.Button
import metier.Objets.Rdv
import models.{ oracle => myOracle }
import vaadin.scala.PushMode
class N02Parameters extends UI with myOracle { app =>
pushConfiguration.pushMode = PushMode.Automatic
val l = Label("zigouigoui")
def changeLabel{
access{
l.value="roploplo"
}
}
override def init(request: ScaladinRequest) {
content = new VerticalLayout {
add(l)
val b = Button("Click me!", { e =>
changeLabel
})
add(b)
}
}
}
but when I call the page, there is at the top-right of my window an waiting animation, and I wait but nothing happens.
can you tell me what's going wrong?
thanks
As I can not yet comment, I ask as an answer:
You can get the page running using only a label right? Cause I had a problem referring to scala and vaadin in connection with apache tomcat, which looked as you discribed.

How do I recognize mouse clicks in Scala?

I'm writing a small GUI program. Everything works except that I want to recognize mouse double-clicks. However, I can't recognize mouse clicks (as such) at all, though I can click buttons and select code from a list.
The following code is adapted from Ingo Maier's "The scala.swing package":
import scala.swing._
import scala.swing.event._
object MouseTest extends SimpleGUIApplication {
def top = new MainFrame {
listenTo(this.mouse) // value mouse is not a member of scala.swing.MainFrame
reactions += {
case e: MouseClicked =>
println("Mouse clicked at " + e.point)
}
}
}
I've tried multiple variations: mouse vs. Mouse, SimpleSwingApplication, importing MouseEvent from java.awt.event, etc. The error message is clear enough--no value mouse in MainFrame--so, where is it then? Help!
Maybe that way?
object App extends SimpleSwingApplication {
lazy val ui = new Panel {
listenTo(mouse.clicks)
reactions += {
case e: MouseClicked =>
println("Mouse clicked at " + e.point)
}
}
def top = new MainFrame {
contents = ui
}
}
BTW, SimpleGUIApplication is deprecated
The MouseClicked event has an attribute clicks, which should be 2 if it was a double-click. Have a look at java.awt.event.MouseEvent for the original source if you're curious.