Skip to content

Issue/84 #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 62 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,79 +5,115 @@
[<img src="https://img.shields.io/maven-central/v/org.scala-lang.modules/scala-swing_2.12.svg?label=latest%20release%20for%202.12"/>](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.scala-lang.modules%20a%3Ascala-swing_2.12)
[![Stories in Ready](https://badge.waffle.io/scala/scala-swing.svg?label=ready&title=Ready)](http://waffle.io/scala/scala-swing)

This is now community maintained by @benhutchison & @Sciss. If you're interested in helping then contact them or @adriaanm.
This is now community maintained by @benhutchison & @Sciss. If you are interested in helping then contact them or @adriaanm.

This is a UI library that will wrap most of Java Swing for Scala in a straightforward manner.
The widget class hierarchy loosely resembles that of Java Swing. The main differences are:

- In Java Swing all components are containers per default. This doesn't make much sense for
- In Java Swing all components are containers per default. This does not make much sense for
a number of components, like TextField, CheckBox, RadioButton, and so on. Our guess is that
this architecture was chosen because Java lacks multiple inheritance.
In scala-swing, components that can have child components extend the Container trait.
- Layout managers and panels are coupled. There is no way to exchange the layout manager
- Layout managers and panels are coupled. There is no way to exchange the layout manager
of a panel. As a result, the layout constraints for widgets can be typed.
(Note that you gain more type-safety and don't loose much flexibility here. Besides
being not a common operation, exchanging the layout manager of a panel in Java
Swing almost always leads to exchanging the layout constraints for every of the panel's
child component. In the end, it is not more work to move all children to a newly created
panel.)

The event system. TODO.

- Widget hierarchies are built by adding children to their parent container's `contents`
collection. The typical usage style is to create anonymous subclasses of the widgets to
customize their properties, and nest children and event reactions.
- The scala-swing event system follows a different approach than the underlying Java system.
Instead of add event listeners with a particular interface (such as `java.awt.ActionListener`),
a `Reactor` instances announces the interest in receiving events by calling `listenTo` for
a `Publisher`. Publishers are also reactors and listen to themselves per default as a convenience.
A reactor contains an object `reactions` which serves as a convenient place to register observers
by adding partial functions that pattern match for any event that the observer is interested in.
This is shown in the examples section below.
- For more details see [SIP-8](docs/SIP-8.md)

The library comprises two main packages:

- `scala.swing`: All widget classes and traits.
- `scala.swing.event`: The event hierarchy.


## Examples

A number of examples can be found in the `examples` project.
A good place to start is `[12] scala.swing.examples.UIDemo` (_index number may be different for you_). This pulls in the all the other examples into a tabbed window.
A good place to start is `[16] scala.swing.examples.UIDemo`.
This pulls in the all the other examples into a tabbed window.

```
$ sbt examples/run

Multiple main classes detected, select one to run:

[1] scala.swing.examples.ButtonApp
[2] scala.swing.examples.Dialogs
[3] scala.swing.examples.ComboBoxes
[4] scala.swing.examples.CelsiusConverter2
[5] scala.swing.examples.ListViewDemo
[6] scala.swing.examples.HelloWorld
[7] scala.swing.examples.LabelTest
[8] scala.swing.examples.PopupDemo
[9] scala.swing.examples.ColorChooserDemo
[10] scala.swing.examples.LinePainting
[11] scala.swing.examples.GridBagDemo
[12] scala.swing.examples.UIDemo
[13] scala.swing.examples.TableSelection
[14] scala.swing.examples.CelsiusConverter
[15] scala.swing.examples.SwingApp
[16] scala.swing.examples.CountButton
[2] scala.swing.examples.CelsiusConverter
[3] scala.swing.examples.CelsiusConverter2
[4] scala.swing.examples.ColorChooserDemo
[5] scala.swing.examples.ComboBoxes
[6] scala.swing.examples.CountButton
[7] scala.swing.examples.Dialogs
[8] scala.swing.examples.GridBagDemo
[9] scala.swing.examples.HelloWorld
[10] scala.swing.examples.LabelTest
[11] scala.swing.examples.LinePainting
[12] scala.swing.examples.ListViewDemo
[13] scala.swing.examples.PopupDemo
[14] scala.swing.examples.SwingApp
[15] scala.swing.examples.TableSelection
[16] scala.swing.examples.UIDemo

Enter number:
```

### Frame with a Button

The following example shows how to plug components and containers together and react to a
mouse click on a button:

```scala
import scala.swing._

new Frame {
title = "Hello world"

contents = new FlowPanel {
contents += new Label("Launch rainbows:")
contents += new Button("Click me") {
reactions += {
case event.ButtonClicked(_) =>
println("All the colours!")
}
}
}

pack()
centerOnScreen()
open()
}
```

## Versions

- The `1.0.x` branch is compiled with JDK 6 and released for Scala 2.10, 2.11. The 1.0.x releases can be used with both Scala versions on JDK 6 or newer.
- The `2.0.x` branch is compiled with JDK 8 and released for Scala 2.11 and 2.12.
- When using Scala 2.11, you can use the Scala swing 2.0.x releases on JDK 6 or newer.
- Scala 2.12 requires you to use JDK 8 (that has nothing to do with scala-swing).
- Version `2.1.0` adds support for Scala 2.13, while dropping Scala 2.10.

The reason to have two versions is to allow for binary incompatible changes. Also, some java-swing classes were generified in JDK 7 (see [SI-3634](https://issues.scala-lang.org/browse/SI-3634)) and require the scala-swing soruces to be adjusted.
The reason to have different major versions is to allow for binary incompatible changes. Also, some java-swing classes were
generified in JDK 7 (see [SI-3634](https://issues.scala-lang.org/browse/SI-3634)) and require the scala-swing sources to be adjusted.


## API documentation (Scaladoc)

The API documentation for scala-swing can be found at [http://www.scala-lang.org/documentation/api.html](http://www.scala-lang.org/documentation/api.html).
The API documentation for scala-swing can be found
at [http://www.scala-lang.org/documentation/api.html](http://www.scala-lang.org/documentation/api.html).


## Current Work

Current changes are being made on the `2.0.x` branch.
Current changes are being made on the `work` branch.
20 changes: 11 additions & 9 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,32 @@ scalaModuleSettings

name := "scala-swing"

version := "2.0.4-SNAPSHOT"
version := "2.1.0-SNAPSHOT"

scalacOptions ++= Seq("-deprecation", "-feature")
scalacOptions in ThisBuild ++= Seq("-deprecation", "-feature")

// Map[JvmMajorVersion, List[(ScalaVersion, UseForPublishing)]]
scalaVersionsByJvm in ThisBuild := Map(
8 -> List("2.11.12", "2.12.6", "2.13.0-M3").map(_ -> true),
9 -> List("2.11.12", "2.12.6", "2.13.0-M3").map(_ -> false),
10 -> List("2.11.12", "2.12.6", "2.13.0-M3").map(_ -> false),
11 -> List("2.11.12", "2.12.6", "2.13.0-M3").map(_ -> false)
8 -> List("2.11.12", "2.12.8", "2.13.0-M5").map(_ -> true),
9 -> List("2.11.12", "2.12.8", "2.13.0-M5").map(_ -> false),
10 -> List("2.11.12", "2.12.8", "2.13.0-M5").map(_ -> false),
11 -> List("2.11.12", "2.12.8", "2.13.0-M5").map(_ -> false)
)

scalaVersion in ThisBuild := "2.13.0-M5" // for testing

OsgiKeys.exportPackage := Seq(s"scala.swing.*;version=${version.value}")

mimaPreviousVersion := Some("2.0.0")
mimaPreviousVersion := None // Some("2.0.0")

// set the prompt (for this build) to include the project id.
shellPrompt in ThisBuild := { state => Project.extract(state).currentRef.project + "> " }

lazy val swing = project.in(file("."))
.settings(
libraryDependencies += {
val v = if (scalaVersion.value == "2.13.0-M3") "3.0.5-M1" else "3.0.5"
"org.scalatest" %% "scalatest" % v % "test"
val v = if (scalaVersion.value == "2.13.0-M5") "3.0.6-SNAP5" else "3.0.5"
"org.scalatest" %% "scalatest" % v % Test
}
)

Expand Down
3 changes: 1 addition & 2 deletions examples/src/main/scala/scala/swing/examples/ButtonApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import scala.swing._
import scala.swing.event._

object ButtonApp extends SimpleSwingApplication {
def top = new MainFrame {
def top: Frame = new MainFrame {
title = "My Frame"
contents = new GridPanel(2, 2) {
hGap = 3
Expand All @@ -27,4 +27,3 @@ object ButtonApp extends SimpleSwingApplication {
size = new Dimension(300, 80)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import scala.swing.event._
* A GUI app to convert celsius to centigrade
*/
object CelsiusConverter extends SimpleSwingApplication {
def top = new MainFrame {
def top: Frame = new MainFrame {
title = "Convert Celsius to Fahrenheit"
val tempCelsius = new TextField
val celsiusLabel = new Label {
Expand All @@ -42,7 +42,7 @@ object CelsiusConverter extends SimpleSwingApplication {
}
}
contents = new GridPanel(2, 2) {
contents.append(tempCelsius, celsiusLabel, convertButton, fahrenheitLabel)
contents ++= Seq(tempCelsius, celsiusLabel, convertButton, fahrenheitLabel)
border = Swing.EmptyBorder(10, 10, 10, 10)
}
//defaultButton = Some(convertButton)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import scala.swing._
import scala.swing.event._

object CelsiusConverter2 extends SimpleSwingApplication {
def newField = new TextField {
def newField: TextField = new TextField {
text = "0"
columns = 5
horizontalAlignment = Alignment.Right
}

val celsius = newField
val fahrenheit = newField
val celsius = newField
val fahrenheit = newField

listenTo(fahrenheit, celsius)
reactions += {
Expand All @@ -38,7 +38,7 @@ object CelsiusConverter2 extends SimpleSwingApplication {
border = Swing.EmptyBorder(15, 10, 10, 10)
}

def top = new MainFrame {
def top: Frame = new MainFrame {
title = "Convert Celsius / Fahrenheit"
contents = ui
}
Expand Down
23 changes: 12 additions & 11 deletions examples/src/main/scala/scala/swing/examples/ColorChooserDemo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
package scala.swing.examples

import java.awt.{Color, Font}

import scala.swing.BorderPanel._
import scala.swing.Swing._
import scala.swing._
import scala.swing.event._
import scala.swing.Swing._
import scala.swing.BorderPanel._

/**
* Demo for ColorChooser.
Expand All @@ -21,23 +22,23 @@ import scala.swing.BorderPanel._
* @author andy@hicks.net
*/
object ColorChooserDemo extends SimpleSwingApplication {
def top = new MainFrame {
def top: Frame = new MainFrame {
title = "ColorChooser Demo"
size = new Dimension(400, 400)

contents = ui
}

val banner = new Label("Welcome to Scala Swing") {
val banner: Label = new Label("Welcome to Scala Swing") {
horizontalAlignment = Alignment.Center
foreground = Color.yellow
background = Color.blue
opaque = true
font = new Font("SansSerif", Font.BOLD, 24)
}

def ui = new BorderPanel {
val colorChooser = new ColorChooser {
def ui: BorderPanel = new BorderPanel {
val colorChooser: ColorChooser = new ColorChooser {
reactions += {
case ColorChanged(_, c) =>
banner.foreground = c
Expand All @@ -46,13 +47,13 @@ object ColorChooserDemo extends SimpleSwingApplication {

colorChooser.border = TitledBorder(EtchedBorder, "Choose Text Color")

val bannerArea = new BorderPanel {
val bannerArea: BorderPanel = new BorderPanel {
layout(banner) = Position.Center
border = TitledBorder(EtchedBorder, "Banner")
}

// Display a color selection dialog when button pressed
val selectColor = new Button("Choose Background Color") {
val selectColor: Button = new Button("Choose Background Color") {
reactions += {
case ButtonClicked(_) =>
ColorChooser.showDialog(this, "Test", Color.red) match {
Expand All @@ -62,8 +63,8 @@ object ColorChooserDemo extends SimpleSwingApplication {
}
}

layout(bannerArea) = Position.North
layout(colorChooser) = Position.Center
layout(selectColor) = Position.South
layout(bannerArea) = Position.North
layout(colorChooser) = Position.Center
layout(selectColor) = Position.South
}
}
13 changes: 7 additions & 6 deletions examples/src/main/scala/scala/swing/examples/ComboBoxes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

package scala.swing.examples

import scala.swing._
import scala.swing.event._
import java.util.Date
import java.awt.Color
import java.text.SimpleDateFormat
import java.util.Date

import javax.swing.{Icon, ImageIcon}

import scala.swing._
import scala.swing.event._

/**
* Demonstrates how to use combo boxes and custom item renderers.
*
Expand All @@ -24,7 +26,7 @@ object ComboBoxes extends SimpleSwingApplication {

import ComboBox._

lazy val ui = new FlowPanel {
lazy val ui: FlowPanel = new FlowPanel {
contents += new ComboBox(List(1, 2, 3, 4))

val patterns = List(
Expand Down Expand Up @@ -100,10 +102,9 @@ object ComboBoxes extends SimpleSwingApplication {
contents += iconBox
}

def top = new MainFrame {
def top: Frame = new MainFrame {
title = "ComboBoxes Demo"
contents = ui
}

}

10 changes: 5 additions & 5 deletions examples/src/main/scala/scala/swing/examples/CountButton.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import scala.swing._
import scala.swing.event._

object CountButton extends SimpleSwingApplication {
def top = new MainFrame {
def top: Frame = new MainFrame {
title = "My Frame"
contents = new GridPanel(2, 2) {
hGap = 3
Expand All @@ -27,11 +27,11 @@ object CountButton extends SimpleSwingApplication {
contents += label

listenTo(button)
var nclicks = 0
var nClicks = 0
reactions += {
case ButtonClicked(b) =>
nclicks += 1
label.text = "Number of button clicks: " + nclicks
case ButtonClicked(_) =>
nClicks += 1
label.text = "Number of button clicks: " + nClicks
}
}
}
Expand Down
Loading