summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/swing/doc/README4
-rw-r--r--src/swing/images/banana.jpgbin0 -> 5999 bytes
-rw-r--r--src/swing/images/margarita1.jpgbin0 -> 14769 bytes
-rw-r--r--src/swing/images/margarita2.jpgbin0 -> 17309 bytes
-rw-r--r--src/swing/images/rose.jpgbin0 -> 13807 bytes
-rw-r--r--src/swing/scala/swing/AbstractButton.scala8
-rw-r--r--src/swing/scala/swing/Action.scala12
-rw-r--r--src/swing/scala/swing/Border.scala2
-rw-r--r--src/swing/scala/swing/BorderPanel.scala2
-rw-r--r--src/swing/scala/swing/BoxPanel.scala2
-rw-r--r--src/swing/scala/swing/Button.scala2
-rw-r--r--src/swing/scala/swing/CheckBox.scala2
-rw-r--r--src/swing/scala/swing/ComboBox.scala198
-rw-r--r--src/swing/scala/swing/Component.scala30
-rw-r--r--src/swing/scala/swing/Label.scala4
-rw-r--r--src/swing/scala/swing/ListView.scala76
-rw-r--r--src/swing/scala/swing/Publisher.scala130
-rw-r--r--src/swing/scala/swing/Reactions.scala53
-rw-r--r--src/swing/scala/swing/Reactor.scala2
-rw-r--r--src/swing/scala/swing/SimpleGUIApplication.scala6
-rw-r--r--src/swing/scala/swing/Slider.scala6
-rw-r--r--src/swing/scala/swing/Swing.scala4
-rw-r--r--src/swing/scala/swing/Table.scala81
-rw-r--r--src/swing/scala/swing/TextComponent.scala4
-rw-r--r--src/swing/scala/swing/TextField.scala6
-rw-r--r--src/swing/scala/swing/event/ActionEvent.scala7
-rw-r--r--src/swing/scala/swing/event/ButtonClicked.scala2
-rw-r--r--src/swing/scala/swing/event/LiveEvent.scala11
-rw-r--r--src/swing/scala/swing/test/ComboBoxes.scala79
-rw-r--r--src/swing/scala/swing/test/TableSelection.scala12
-rw-r--r--src/swing/scala/swing/test/UIDemo.scala3
31 files changed, 685 insertions, 63 deletions
diff --git a/src/swing/doc/README b/src/swing/doc/README
index 00aebe7b19..3f6d1b1c2f 100644
--- a/src/swing/doc/README
+++ b/src/swing/doc/README
@@ -30,3 +30,7 @@ The library comprises three main packages:
scala.swing.test
A set of demos.
+
+Notes:
+
+Visual appearance of combo boxes using the GTK LaF is broken on JDKs < 1.7b30. \ No newline at end of file
diff --git a/src/swing/images/banana.jpg b/src/swing/images/banana.jpg
new file mode 100644
index 0000000000..81fc4ab387
--- /dev/null
+++ b/src/swing/images/banana.jpg
Binary files differ
diff --git a/src/swing/images/margarita1.jpg b/src/swing/images/margarita1.jpg
new file mode 100644
index 0000000000..485723334a
--- /dev/null
+++ b/src/swing/images/margarita1.jpg
Binary files differ
diff --git a/src/swing/images/margarita2.jpg b/src/swing/images/margarita2.jpg
new file mode 100644
index 0000000000..c5fefb0bd5
--- /dev/null
+++ b/src/swing/images/margarita2.jpg
Binary files differ
diff --git a/src/swing/images/rose.jpg b/src/swing/images/rose.jpg
new file mode 100644
index 0000000000..5c2e75637e
--- /dev/null
+++ b/src/swing/images/rose.jpg
Binary files differ
diff --git a/src/swing/scala/swing/AbstractButton.scala b/src/swing/scala/swing/AbstractButton.scala
index d21f71aaaa..e496d0566a 100644
--- a/src/swing/scala/swing/AbstractButton.scala
+++ b/src/swing/scala/swing/AbstractButton.scala
@@ -11,7 +11,7 @@ import event._
* @see javax.swing.AbstractButton
*/
abstract class AbstractButton extends Component with Action.Trigger with Publisher {
- override lazy val peer: JAbstractButton = new JAbstractButton {}
+ override lazy val peer: JAbstractButton = new JAbstractButton with SuperMixin {}
def text: String = peer.getText
def text_=(s: String) = peer.setText(s)
@@ -38,10 +38,8 @@ abstract class AbstractButton extends Component with Action.Trigger with Publish
//1.6: def hideActionText: Boolean = peer.getHideActionText
//def hideActionText_=(b: Boolean) = peer.setHideActionText(b)
- peer.addActionListener(new java.awt.event.ActionListener {
- def actionPerformed(e: java.awt.event.ActionEvent) {
- publish(ButtonClicked(AbstractButton.this))
- }
+ peer.addActionListener(Swing.ActionListener { e =>
+ publish(ButtonClicked(AbstractButton.this))
})
def selected: Boolean = peer.isSelected
diff --git a/src/swing/scala/swing/Action.scala b/src/swing/scala/swing/Action.scala
index e259ba748a..46dc470558 100644
--- a/src/swing/scala/swing/Action.scala
+++ b/src/swing/scala/swing/Action.scala
@@ -1,10 +1,22 @@
package scala.swing
import javax.swing.{KeyStroke, Icon}
+import java.awt.event.ActionListener
object Action {
case object NoAction extends Action("") { def apply() {} }
+ object Trigger {
+ abstract trait Wrapper extends Component with Action.Trigger {
+ self: Component {
+ def peer: javax.swing.JComponent {
+ def addActionListener(a: ActionListener)
+ def removeActionListener(a: ActionListener)
+ }
+ } =>
+ }
+ }
+
/**
* Anything that triggers an action.
*/
diff --git a/src/swing/scala/swing/Border.scala b/src/swing/scala/swing/Border.scala
index 13d5d7ae1e..bdc347763e 100644
--- a/src/swing/scala/swing/Border.scala
+++ b/src/swing/scala/swing/Border.scala
@@ -11,6 +11,8 @@ import javax.swing.border._
*/
object Border {
def Empty = BorderFactory.createEmptyBorder()
+ def Empty(weight: Int) =
+ BorderFactory.createEmptyBorder(weight, weight, weight, weight)
def Empty(top: Int, left: Int, bottom: Int, right: Int) =
BorderFactory.createEmptyBorder(top, left, bottom, right)
diff --git a/src/swing/scala/swing/BorderPanel.scala b/src/swing/scala/swing/BorderPanel.scala
index ad3c4ab11c..93ee4eec71 100644
--- a/src/swing/scala/swing/BorderPanel.scala
+++ b/src/swing/scala/swing/BorderPanel.scala
@@ -25,7 +25,7 @@ object BorderPanel {
class BorderPanel extends Panel with LayoutContainer {
import BorderPanel._
def layoutManager = peer.getLayout.asInstanceOf[BorderLayout]
- override lazy val peer = new javax.swing.JPanel(new BorderLayout)
+ override lazy val peer = new javax.swing.JPanel(new BorderLayout) with SuperMixin
type Constraints = Position.Value
diff --git a/src/swing/scala/swing/BoxPanel.scala b/src/swing/scala/swing/BoxPanel.scala
index 101298bf6e..285f245167 100644
--- a/src/swing/scala/swing/BoxPanel.scala
+++ b/src/swing/scala/swing/BoxPanel.scala
@@ -8,7 +8,7 @@ package scala.swing
*/
class BoxPanel(orientation: Orientation.Value) extends Panel with SequentialContainer.Wrapper {
override lazy val peer = {
- val p = new javax.swing.JPanel
+ val p = new javax.swing.JPanel with SuperMixin
val l = new javax.swing.BoxLayout(p, orientation.id)
p.setLayout(l)
p
diff --git a/src/swing/scala/swing/Button.scala b/src/swing/scala/swing/Button.scala
index c28773a9c4..c146e66cb0 100644
--- a/src/swing/scala/swing/Button.scala
+++ b/src/swing/scala/swing/Button.scala
@@ -9,7 +9,7 @@ import event._
* @see javax.swing.JButton
*/
class Button(text0: String) extends AbstractButton with Publisher {
- override lazy val peer: JButton = new JButton(text0)
+ override lazy val peer: JButton = new JButton(text0) with SuperMixin
def this() = this("")
def this(a: Action) = {
this("")
diff --git a/src/swing/scala/swing/CheckBox.scala b/src/swing/scala/swing/CheckBox.scala
index ceea4483a1..ed266cca27 100644
--- a/src/swing/scala/swing/CheckBox.scala
+++ b/src/swing/scala/swing/CheckBox.scala
@@ -8,6 +8,6 @@ import javax.swing._
* @see javax.swing.JCheckBox
*/
class CheckBox(text: String) extends ToggleButton {
- override lazy val peer: JCheckBox = new JCheckBox(text)
+ override lazy val peer: JCheckBox = new JCheckBox(text) with SuperMixin
def this() = this("")
} \ No newline at end of file
diff --git a/src/swing/scala/swing/ComboBox.scala b/src/swing/scala/swing/ComboBox.scala
new file mode 100644
index 0000000000..6c2dd4a487
--- /dev/null
+++ b/src/swing/scala/swing/ComboBox.scala
@@ -0,0 +1,198 @@
+package scala.swing
+
+import event._
+import javax.swing.{JList, JComponent, JComboBox, JTextField, ComboBoxModel, AbstractListModel, ListCellRenderer}
+import java.awt.event.ActionListener
+
+
+object ComboBox {
+ /**
+ * An editor for a combo box. Let's you edit the currently selected item.
+ * It is highly recommended to use the BuiltInEditor class. For anything
+ * else, one cannot guarantee that it integrated nicely into the current
+ * LookAndFeel.
+ *
+ * Publishes action events.
+ */
+ trait Editor[A] extends Publisher {
+ lazy val comboBoxPeer: javax.swing.ComboBoxEditor = new javax.swing.ComboBoxEditor with Publisher {
+ def addActionListener(l: ActionListener) {
+ this match {
+ // TODO case w: Action.Trigger.Wrapper =>
+ // w.peer.addActionListener(l)
+ case _ =>
+ this.subscribe(new Reactions.Wrapper(l) ({
+ case ActionEvent(c) => l.actionPerformed(new java.awt.event.ActionEvent(c.peer, 0, ""))
+ }))
+ }
+ }
+ def removeActionListener(l: ActionListener) {
+ this match {
+ // TODO case w: Action.Trigger.Wrapper =>
+ // w.peer.removeActionListener(l)
+ case _ =>
+ this.unsubscribe(new Reactions.Wrapper(l)({ case _ => }))
+ }
+ }
+ def getEditorComponent: JComponent = Editor.this.component.peer
+ def getItem(): AnyRef = item.asInstanceOf[AnyRef]
+ def selectAll() { startEditing() }
+ def setItem(a: Any) { item = a.asInstanceOf[A] }
+ }
+ def component: Component
+ def item: A
+ def item_=(a: A)
+ def startEditing()
+ }
+
+ /**
+ * Use this editor, if you want to reuse the builtin editor supplied by the current
+ * Look and Feel. This is restricted to a text field as the editor widget. The
+ * conversion from and to a string is done by the supplied functions.
+ *
+ * It's okay if string2A throws exceptions. They are caught by an input verifier.
+ */
+ class BuiltInEditor[A](comboBox: ComboBox[A])(string2A: String => A,
+ a2String: A => String) extends ComboBox.Editor[A] {
+ protected[swing] class DelegatedEditor(editor: javax.swing.ComboBoxEditor) extends javax.swing.ComboBoxEditor {
+ var value: A = {
+ val v = comboBox.peer.getSelectedItem
+ try {
+ v match {
+ case s: String => string2A(s)
+ case _ => v.asInstanceOf[A]
+ }
+ } catch {
+ case _: Exception =>
+ throw new IllegalArgumentException("ComboBox not initialized with a proper value, was '" + v + "'.")
+ }
+ }
+ def addActionListener(l: ActionListener) {
+ editor.addActionListener(l)
+ }
+ def removeActionListener(l: ActionListener) {
+ editor.removeActionListener(l)
+ }
+
+ def getEditorComponent: JComponent = editor.getEditorComponent.asInstanceOf[JComponent]
+ def selectAll() { editor.selectAll() }
+ def getItem(): AnyRef = { verifier.verify(getEditorComponent); value.asInstanceOf[AnyRef] }
+ def setItem(a: Any) { editor.setItem(a) }
+
+ val verifier = new javax.swing.InputVerifier {
+ // TODO: should chain with potentially existing verifier in editor
+ def verify(c: JComponent) = try {
+ println(c)
+ value = string2A(c.asInstanceOf[JTextField].getText)
+ true
+ }
+ catch {
+ case e: Exception => false
+ }
+ }
+
+ def textEditor = getEditorComponent.asInstanceOf[JTextField]
+ textEditor.setInputVerifier(verifier)
+ textEditor.addActionListener(Swing.ActionListener{ a =>
+ getItem() // make sure our value is updated
+ textEditor.setText(a2String(value))
+ })
+ }
+
+
+ override lazy val comboBoxPeer: javax.swing.ComboBoxEditor = new DelegatedEditor(comboBox.peer.getEditor)
+
+ def component = Component.wrap(comboBoxPeer.getEditorComponent.asInstanceOf[JComponent])
+ def item: A = { comboBoxPeer.asInstanceOf[DelegatedEditor].value }
+ def item_=(a: A) { comboBoxPeer.setItem(a2String(a)) }
+ def startEditing() { comboBoxPeer.selectAll() }
+ }
+
+ implicit def stringEditor(c: ComboBox[String]): Editor[String] = new BuiltInEditor(c)(s => s, s => s)
+ implicit def intEditor(c: ComboBox[Int]): Editor[Int] = new BuiltInEditor(c)(s => s.toInt, s => s.toString)
+ implicit def floatEditor(c: ComboBox[Float]): Editor[Float] = new BuiltInEditor(c)(s => s.toFloat, s => s.toString)
+ implicit def doubleEditor(c: ComboBox[Double]): Editor[Double] = new BuiltInEditor(c)(s => s.toDouble, s => s.toString)
+
+ def newConstantModel[A](items: Seq[A]): ComboBoxModel = {
+ new AbstractListModel with ComboBoxModel {
+ private var selected = items(0)
+ def getSelectedItem: AnyRef = selected.asInstanceOf[AnyRef]
+ def setSelectedItem(a: Any) { selected = a.asInstanceOf[A] }
+ def getElementAt(n: Int) = items(n).asInstanceOf[AnyRef]
+ def getSize = items.size
+ }
+ }
+
+ def newMutableModel[A, Self](items: Seq[A] with scala.collection.mutable.Publisher[scala.collection.mutable.Message[A], Self]): ComboBoxModel = {
+ new AbstractListModel with ComboBoxModel {
+ private var selected = items(0)
+ def getSelectedItem: AnyRef = selected.asInstanceOf[AnyRef]
+ def setSelectedItem(a: Any) { selected = a.asInstanceOf[A] }
+ def getElementAt(n: Int) = items(n).asInstanceOf[AnyRef]
+ def getSize = items.size
+ }
+ }
+
+ /*def newConstantModel[A](items: Seq[A]): ComboBoxModel = items match {
+ case items: Seq[A] with scala.collection.mutable.Publisher[scala.collection.mutable.Message[A], Self] => newMutableModel
+ case _ => newConstantModel(items)
+ }*/
+}
+
+/**
+ * Has built-in default editor and renderer that cannot be exposed.
+ * They are set by the look and feel (LaF). Unfortunately, this design in
+ * inherently broken, since custom editors will almost always look
+ * differently. The border of the built-in text field editor, e.g., is drawn
+ * by the LaF. In a custom text field editor we have no way to mirror that.
+ *
+ * This combo box has to be initialized with a valid selected value.
+ * Otherwise it will fail.
+ *
+ * @see javax.swing.JComboBox
+ */
+class ComboBox[A](items: Seq[A]) extends Component with Publisher {
+ override lazy val peer: JComboBox = new JComboBox(ComboBox.newConstantModel(items)) with SuperMixin
+
+ object selection extends Publisher {
+ def index: Int = peer.getSelectedIndex
+ def item: A = peer.getSelectedItem.asInstanceOf[A]
+
+ println("created")
+ peer.addActionListener(Swing.ActionListener { e =>
+ println("action")
+ publish(event.SelectionChanged(ComboBox.this))
+ })
+ }
+
+ /**
+ * Sets the renderer for this combo box's items. Index -1 is
+ * passed to the renderer for the selected item (not in the popup menu).
+ *
+ * The underlying combo box renders all items in a ListView (both, in
+ * the pulldown menu as well as in the box itself), hence the
+ * ListView.Renderer.
+ *
+ * Note that the UI peer of a combo box usually changes the colors
+ * of the component to its own defaults _after_ the renderer has configured it.
+ * That's Swing's principle of most suprise.
+ */
+ def renderer: ListView.Renderer[A] = ListView.Renderer.wrap(peer.getRenderer)
+ def renderer_=(r: ListView.Renderer[A]) { peer.setRenderer(r.peer) }
+
+ /* XXX: currently not safe to expose:
+ def editor: ComboBox.Editor[A] =
+ def editor_=(r: ComboBox.Editor[A]) { peer.setEditor(r.comboBoxPeer) }
+ */
+ def editable: Boolean = peer.isEditable
+
+ def makeEditable()(implicit editor: ComboBox[A] => ComboBox.Editor[A]) {
+ peer.setEditable(true)
+ peer.setEditor(editor(this).comboBoxPeer)
+ }
+
+ def prototypeDisplayValue: Option[A] = Swing.toOption(peer.getPrototypeDisplayValue)
+ def prototypeDisplayValue_=(v: Option[A]) {
+ peer.setPrototypeDisplayValue(Swing.toNull(v.map(_.asInstanceOf[AnyRef])))
+ }
+} \ No newline at end of file
diff --git a/src/swing/scala/swing/Component.scala b/src/swing/scala/swing/Component.scala
index 2039a09d36..3a715e91aa 100644
--- a/src/swing/scala/swing/Component.scala
+++ b/src/swing/scala/swing/Component.scala
@@ -12,9 +12,17 @@ object Component {
/**
* Returns the wrapper for a given peer.
+ * Fails if there is no wrapper for the given component.
*/
protected[swing] def wrapperFor[C<:Component](c: javax.swing.JComponent): C =
c.getClientProperty(ClientKey).asInstanceOf[C]
+
+ /**
+ * Wraps a given Java Swing Component into a new wrapper.
+ */
+ def wrap[A](c: JComponent) = new Component {
+ override lazy val peer = c
+ }
}
/**
@@ -23,9 +31,20 @@ object Component {
* @see javax.swing.JComponent
*/
abstract class Component extends UIElement with Publisher {
- override lazy val peer: javax.swing.JComponent = new javax.swing.JComponent{}
+ override lazy val peer: javax.swing.JComponent = new javax.swing.JComponent with SuperMixin {}
+ var initP: JComponent = null
peer.putClientProperty(Component.ClientKey, this)
+ trait SuperMixin extends JComponent {
+ override def paintComponent(g: java.awt.Graphics) {
+ Component.this.paintComponent(g)
+ }
+ def __super__paintComponent(g: java.awt.Graphics) {
+ super.paintComponent(g)
+ }
+ }
+
+
/**
* Used by certain layout managers, e.g., BoxLayout or OverlayLayout to
* align components relative to each other.
@@ -182,5 +201,14 @@ abstract class Component extends UIElement with Publisher {
def revalidate() { peer.revalidate() }
+ def requestFocus() { peer.requestFocus() }
+
+ protected def paintComponent(g: java.awt.Graphics) {
+ peer match {
+ case peer: SuperMixin => peer.__super__paintComponent(g)
+ case _ => // it's a wrapper created on the fly
+ }
+ }
+
override def toString = "scala.swing wrapper " + peer.toString
}
diff --git a/src/swing/scala/swing/Label.scala b/src/swing/scala/swing/Label.scala
index 20692628d8..bcb421e344 100644
--- a/src/swing/scala/swing/Label.scala
+++ b/src/swing/scala/swing/Label.scala
@@ -10,8 +10,10 @@ import javax.swing._
class Label(text0: String) extends Component {
override lazy val peer: JLabel = new JLabel(text0)
def this() = this("")
- def text: String = peer.getText()
+ def text: String = peer.getText
def text_=(s: String) = peer.setText(s)
+ def icon: Icon = peer.getIcon
+ def icon_=(i: Icon) = peer.setIcon(i)
/**
* The alignment of the label's contents relative to its bounding box.
*/
diff --git a/src/swing/scala/swing/ListView.scala b/src/swing/scala/swing/ListView.scala
index c0a8b2fc4c..25af541802 100644
--- a/src/swing/scala/swing/ListView.scala
+++ b/src/swing/scala/swing/ListView.scala
@@ -3,6 +3,7 @@ package scala.swing
import javax.swing._
import javax.swing.event._
import event._
+import java.awt.Color
object ListView {
object IntervalMode extends Enumeration {
@@ -10,6 +11,68 @@ object ListView {
val SingleInterval = Value(ListSelectionModel.SINGLE_INTERVAL_SELECTION)
val MultiInterval = Value(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
}
+
+ def wrap[A](c: JList) = new ListView[A] {
+ override lazy val peer = c
+ }
+
+ object Renderer {
+ def wrap[A](r: ListCellRenderer): Renderer[A] = new Wrapped[A](r)
+
+ class Wrapped[A](override val peer: ListCellRenderer) extends Renderer[A] {
+ def componentFor(list: ListView[_<:A], isSelected: Boolean, hasFocus: Boolean, a: A, index: Int) = {
+ Component.wrap(peer.getListCellRendererComponent(list.peer, a, index, isSelected, hasFocus).asInstanceOf[JComponent])
+ }
+ }
+ }
+
+ abstract class Renderer[-A] {
+ def peer: ListCellRenderer = new ListCellRenderer {
+ def getListCellRendererComponent(list: JList, a: Any, index: Int, isSelected: Boolean, hasFocus: Boolean) = {
+ componentFor(ListView.wrap[A](list), isSelected, hasFocus, a.asInstanceOf[A], index).peer
+ }
+ }
+ def componentFor(list: ListView[_<:A], isSelected: Boolean, hasFocus: Boolean, a: A, index: Int): Component
+ }
+
+ /**
+ * A default renderer that maintains a single component for item rendering
+ * and preconfigures it to sensible defaults. It is polymorphic on the
+ * components type so clients can easily use component specific attributes
+ * during configuration.
+ */
+ abstract class DefaultRenderer[-A, C<:Component](protected val component: C) extends Renderer[A] {
+ // The renderer component is responsible for painting selection
+ // backgrounds. Hence, make sure it is opaque to let it draw
+ // the background.
+ component.opaque = true
+
+ /**
+ * Standard preconfiguration that is commonly done for any component.
+ */
+ def preConfigure(list: ListView[_<:A], isSelected: Boolean, hasFocus: Boolean, a: A, index: Int) {
+ if (isSelected) {
+ component.background = list.selectionBackground
+ component.foreground = list.selectionForeground
+ } else {
+ component.background = list.background
+ component.foreground = list.foreground
+ }
+ }
+ /**
+ * Configuration that is specific to the component and this renderer.
+ */
+ def configure(list: ListView[_<:A], isSelected: Boolean, hasFocus: Boolean, a: A, index: Int)
+
+ /**
+ * Configures the component before returning it.
+ */
+ def componentFor(list: ListView[_<:A], isSelected: Boolean, hasFocus: Boolean, a: A, index: Int): Component = {
+ preConfigure(list, isSelected, hasFocus, a, index)
+ configure(list, isSelected, hasFocus, a, index)
+ component
+ }
+ }
}
/**
@@ -23,11 +86,11 @@ class ListView[A] extends Component {
import ListView._
override lazy val peer: JList = new JList
- def this(elems: Seq[A]) = {
+ def this(items: Seq[A]) = {
this()
peer.setModel(new AbstractListModel {
- def getElementAt(n: Int) = elems(n).asInstanceOf[AnyRef]
- def getSize = elems.size
+ def getElementAt(n: Int) = items(n).asInstanceOf[AnyRef]
+ def getSize = items.size
})
}
@@ -48,7 +111,7 @@ class ListView[A] extends Component {
def anchorIndex: Int = peer.getSelectionModel.getAnchorSelectionIndex
}
- object elements extends SeqProxy[A] {
+ object items extends SeqProxy[A] {
def self = peer.getSelectedValues.projection.map(_.asInstanceOf[A])
def leadIndex: Int = peer.getSelectionModel.getLeadSelectionIndex
def anchorIndex: Int = peer.getSelectionModel.getAnchorSelectionIndex
@@ -70,6 +133,11 @@ class ListView[A] extends Component {
def fixedCellHeight = peer.getFixedCellHeight
def fixedCellHeight_=(x: Int) = peer.setFixedCellHeight(x)
+ def selectionForeground: Color = peer.getSelectionForeground
+ def selectionForeground_=(c: Color) = peer.setSelectionForeground(c)
+ def selectionBackground: Color = peer.getSelectionBackground
+ def selectionBackground_=(c: Color) = peer.setSelectionBackground(c)
+
peer.getModel.addListDataListener(new ListDataListener {
def contentsChanged(e: ListDataEvent) { publish(ListChanged(ListView.this)) }
def intervalRemoved(e: ListDataEvent) { publish(ListElementsRemoved(ListView.this, e.getIndex0 to e.getIndex1)) }
diff --git a/src/swing/scala/swing/Publisher.scala b/src/swing/scala/swing/Publisher.scala
index ad1b3b5da0..bcefc6c42e 100644
--- a/src/swing/scala/swing/Publisher.scala
+++ b/src/swing/scala/swing/Publisher.scala
@@ -1,17 +1,137 @@
package scala.swing
-import scala.collection.mutable.HashSet
+import scala.collection.mutable._
import event.Event
/**
* Notifies subscribed observers when a event is published.
*/
trait Publisher extends Reactor {
- protected var listeners = new HashSet[Reactions]
+ import Reactions._
+ // TODO: optionally weak references
+ protected var listeners = new RefSet[Reaction] {
+ import scala.ref._
+ val underlying = new HashSet[Reference[Reaction]]
+ protected def Ref(a: Reaction) = a match {
+ case a: StronglyReferenced => new StrongReference[Reaction](a) with super.Ref[Reaction] {
+ type _$1 = Reaction // FIXME: what's going on here?
+ }
+ case _ => new WeakReference[Reaction](a, referenceQueue) with super.Ref[Reaction] {
+ type _$1 = Reaction // FIXME: what's going on here?
+ }
+ }
+ }
- def subscribe(listener: Reactions) { listeners += listener }
- def unsubscribe(listener: Reactions) { listeners -= listener }
- def publish(e: Event) { for (val l <- listeners) l.send(e) }
+ def subscribe(listener: Reaction) { listeners += listener }
+ def unsubscribe(listener: Reaction) { listeners -= listener }
+ def publish(e: Event) { for (val l <- listeners) l(e) }
listenTo(this)
}
+
+import scala.ref._
+
+private[swing] trait SingleRefCollection[+A <: AnyRef] extends Collection[A] { self =>
+
+ trait Ref[+A<:AnyRef] extends Reference[A] {
+ override def hashCode() = {
+ val v = get
+ if (v == None) 0 else v.get.hashCode
+ }
+ override def equals(that: Any) = that match {
+ case that: ReferenceWrapper[_] =>
+ val v1 = this.get
+ val v2 = that.get
+ v1 == v2
+ case _ => false
+ }
+ }
+
+ //type Ref <: Reference[A] // TODO: could use higher kinded types, but currently crashes
+ protected[this] def Ref(a: A): Ref[A]
+ protected[this] val referenceQueue = new ReferenceQueue[A]
+
+ protected val underlying: Collection[Reference[A]]
+
+ def purgeReferences() {
+ var ref = referenceQueue.poll
+ while(ref != None) {
+ removeReference(ref.get.asInstanceOf[Reference[A]])
+ ref = referenceQueue.poll
+ }
+ }
+
+ protected[this] def removeReference(ref: Reference[A])
+
+ def elements = new Iterator[A] {
+ private val elems = self.underlying.elements
+ private var hd: A = _
+ private var ahead: Boolean = false
+ private def skip: Unit =
+ while (!ahead && elems.hasNext) {
+ // make sure we have a reference to the next element,
+ // otherwise it might be garbage collected
+ val next = elems.next.get
+ ahead = next != None
+ if (ahead) hd = next.get
+ }
+ def hasNext: Boolean = { skip; ahead }
+ def next(): A =
+ if (hasNext) { ahead = false; hd }
+ else throw new NoSuchElementException("next on empty iterator")
+ }
+}
+
+private[swing] class StrongReference[+T <: AnyRef](value: T) extends Reference[T] {
+ private[this] var ref: Option[T] = Some(value)
+ @deprecated def isValid: Boolean = ref != None
+ def apply(): T = ref.get
+ def get : Option[T] = ref
+ override def toString = get.map(_.toString).getOrElse("<deleted>")
+ def clear() { ref = None }
+ def enqueue(): Boolean = false
+ def isEnqueued(): Boolean = false
+ }
+
+ abstract class RefBuffer[A <: AnyRef] extends Buffer[A] with SingleRefCollection[A] { self =>
+ protected val underlying: Buffer[Reference[A]]
+
+ def +=(el: A) { purgeReferences(); underlying += Ref(el) }
+ def +:(el: A) = { purgeReferences(); Ref(el) +: underlying; this }
+ def remove(el: A) { underlying -= Ref(el); purgeReferences(); }
+ def remove(n: Int) = { val el = apply(n); remove(el); el }
+ def insertAll(n: Int, iter: Iterable[A]) {
+ purgeReferences()
+ underlying.insertAll(n, iter.projection.map(Ref(_)))
+ }
+ def update(n: Int, el: A) { purgeReferences(); underlying(n) = Ref(el) }
+ def readOnly : Seq[A] = new Seq[A] {
+ def length = self.length
+ def elements = self.elements
+ def apply(n: Int) = self(n)
+ }
+ def apply(n: Int) = {
+ purgeReferences()
+ var el = underlying(n).get
+ while (el == None) {
+ purgeReferences(); el = underlying(n).get
+ }
+ el.get
+ }
+
+ def length = { purgeReferences(); underlying.length }
+ def clear() { underlying.clear(); purgeReferences() }
+
+ protected[this] def removeReference(ref: Reference[A]) { underlying -= ref }
+}
+
+private[swing] abstract class RefSet[A <: AnyRef] extends Set[A] with SingleRefCollection[A] { self =>
+ protected val underlying: Set[Reference[A]]
+
+ def -=(el: A) { underlying -= Ref(el); purgeReferences() }
+ def +=(el: A) { purgeReferences(); underlying += Ref(el) }
+ def contains(el: A) = { purgeReferences(); underlying.contains(Ref(el)) }
+ def size = { purgeReferences(); underlying.size }
+
+ protected[this] def removeReference(ref: Reference[A]) { underlying -= ref }
+}
diff --git a/src/swing/scala/swing/Reactions.scala b/src/swing/scala/swing/Reactions.scala
index cdb6d198a9..fce1fe3956 100644
--- a/src/swing/scala/swing/Reactions.scala
+++ b/src/swing/scala/swing/Reactions.scala
@@ -1,41 +1,44 @@
package scala.swing
import event.Event
+import scala.collection.mutable.{Buffer, ListBuffer}
+
+object Reactions {
+ import scala.ref._
+
+ class Impl extends Reactions {
+ private val parts: Buffer[Reaction] = new ListBuffer[Reaction]
+ def isDefinedAt(e: Event) = parts.exists(_ isDefinedAt e)
+ def += (r: Reaction) = { parts += r }
+ def -= (r: Reaction) { parts -= r }
+ def apply(e: Event) {
+ for (p <- parts) if (p isDefinedAt e) p(e)
+ }
+ }
+
+ type Reaction = PartialFunction[Event, Unit]
-class Reactions {
/**
- * Convenience type alias.
+ * A Reaction implementing this trait is strongly referenced in the reaction list
*/
- type Reaction = PartialFunction[Event, unit]
+ trait StronglyReferenced // TODO: implement in Publisher
+
+ class Wrapper(listener: Any)(r: Reaction) extends Reaction with StronglyReferenced with Proxy {
+ def self = listener
+ def isDefinedAt(e: Event) = r.isDefinedAt(e)
+ def apply(e: Event) { r(e) }
+ }
+}
- private var parts: List[Reaction] = List()
+abstract class Reactions extends Reactions.Reaction {
/**
* Add a reaction.
*/
- def += (r: Reaction) = { parts = r :: parts }
+ def += (r: Reactions.Reaction)
/**
* Remove the given reaction.
*/
- def -= (r: Reaction) = {
- def withoutR(xs: List[Reaction]): List[Reaction] =
- if (xs.isEmpty) xs
- else if (xs.head == r) xs.tail
- else xs.head :: withoutR(xs.tail)
- parts = withoutR(parts)
- }
-
- /**
- * Pass the given event to registered reactions.
- */
- def send(e: Event) = {
- def sendTo(ps: List[Reaction]): Unit = ps match {
- case Nil =>
- case p :: ps =>
- if (p isDefinedAt e) p(e)
- /*else*/ sendTo(ps) // no overwrite semantics
- }
- sendTo(parts)
- }
+ def -= (r: Reactions.Reaction)
}
diff --git a/src/swing/scala/swing/Reactor.scala b/src/swing/scala/swing/Reactor.scala
index 919ae15209..002002d3f9 100644
--- a/src/swing/scala/swing/Reactor.scala
+++ b/src/swing/scala/swing/Reactor.scala
@@ -1,7 +1,7 @@
package scala.swing
trait Reactor {
- val reactions = new Reactions
+ val reactions = new Reactions.Impl
/**
* Listen to the given publisher as long as <code>deafTo</code> isn't called for
* them.
diff --git a/src/swing/scala/swing/SimpleGUIApplication.scala b/src/swing/scala/swing/SimpleGUIApplication.scala
index b341a16e0e..436ba1e10b 100644
--- a/src/swing/scala/swing/SimpleGUIApplication.scala
+++ b/src/swing/scala/swing/SimpleGUIApplication.scala
@@ -10,4 +10,10 @@ abstract class SimpleGUIApplication extends GUIApplication {
new Runnable { def run() { init(); top.pack(); top.visible = true } }
}
}
+
+ def resourceFromClassloader(path: String): java.net.URL =
+ this.getClass.getResource(path)
+
+ def resourceFromUserDirectory(path: String): java.io.File =
+ new java.io.File(System.getProperty("user.dir"), path)
}
diff --git a/src/swing/scala/swing/Slider.scala b/src/swing/scala/swing/Slider.scala
index fe18965ea3..e33d079c13 100644
--- a/src/swing/scala/swing/Slider.scala
+++ b/src/swing/scala/swing/Slider.scala
@@ -33,9 +33,9 @@ class Slider extends Component with Orientable with Publisher {
def majorTickSpacing: Int = peer.getMajorTickSpacing
def majorTickSpacing_=(v: Int) { peer.setMajorTickSpacing(v) }
- def labels: collection.Map[Int, Label] =
- new collection.jcl.MapWrapper[Int, Label] { def underlying = peer.getLabelTable.asInstanceOf[java.util.Hashtable[Int, Label]] }
- def labels_=(l: collection.Map[Int, Label]) {
+ def labels: scala.collection.Map[Int, Label] =
+ new scala.collection.jcl.MapWrapper[Int, Label] { def underlying = peer.getLabelTable.asInstanceOf[java.util.Hashtable[Int, Label]] }
+ def labels_=(l: scala.collection.Map[Int, Label]) {
val table = new java.util.Hashtable[Any, Any]
for ((k,v) <- l) table.put(k, v)
peer.setLabelTable(table)
diff --git a/src/swing/scala/swing/Swing.scala b/src/swing/scala/swing/Swing.scala
index e34ab1052b..80d22d7a10 100644
--- a/src/swing/scala/swing/Swing.scala
+++ b/src/swing/scala/swing/Swing.scala
@@ -1,6 +1,7 @@
package scala.swing
import java.awt.Dimension
+import java.awt.event._
import javax.swing._
import javax.swing.event._
@@ -20,6 +21,9 @@ object Swing {
def ChangeListener(f: ChangeEvent => Unit) = new ChangeListener {
def stateChanged(e: ChangeEvent) { f(e) }
}
+ def ActionListener(f: ActionEvent => Unit) = new ActionListener {
+ def actionPerformed(e: ActionEvent) { f(e) }
+ }
def Box(min: Dimension, pref: Dimension, max: Dimension) = new Component {
override lazy val peer = new javax.swing.Box.Filler(min, pref, max)
diff --git a/src/swing/scala/swing/Table.scala b/src/swing/scala/swing/Table.scala
index e4c957e2a5..22012b0b45 100644
--- a/src/swing/scala/swing/Table.scala
+++ b/src/swing/scala/swing/Table.scala
@@ -25,19 +25,75 @@ object Table {
object ElementMode extends Enumeration {
val Row, Column, Cell, None = Value
}
+
+ abstract class Renderer[-A] {
+ def peer: TableCellRenderer = new TableCellRenderer {
+ def getTableCellRendererComponent(table: JTable, value: AnyRef, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int) = {
+ componentFor(table match {
+ case t: JTableMixin => t.tableWrapper
+ case _ => assert(false); null
+ }, isSelected, hasFocus, value.asInstanceOf[A], row, column).peer
+ }
+ }
+ def componentFor(table: Table, isSelected: Boolean, hasFocus: Boolean, a: A, row: Int, column: Int): Component
+ }
+
+ abstract class DefaultRenderer[-A, C<:Component](val component: C) extends Renderer[A] {
+ // The renderer component is responsible for painting selection
+ // backgrounds. Hence, make sure it is opaque to let it draw
+ // the background.
+ component.opaque = true
+
+ /**
+ * Standard preconfiguration that is commonly done for any component.
+ */
+ def preConfigure(table: Table, isSelected: Boolean, hasFocus: Boolean, a: A, row: Int, column: Int) {
+ if (isSelected) {
+ component.background = table.selectionBackground
+ component.foreground = table.selectionForeground
+ } else {
+ component.background = table.background
+ component.foreground = table.foreground
+ }
+ }
+ /**
+ * Configuration that is specific to the component and this renderer.
+ */
+ def configure(table: Table, isSelected: Boolean, hasFocus: Boolean, a: A, row: Int, column: Int)
+
+ /**
+ * Configures the component before returning it.
+ */
+ def componentFor(table: Table, isSelected: Boolean, hasFocus: Boolean, a: A, row: Int, column: Int): Component = {
+ preConfigure(table, isSelected, hasFocus, a, row, column)
+ configure(table, isSelected, hasFocus, a, row, column)
+ component
+ }
+ }
+
+ class LabelRenderer[A](convert: A => (Icon, String)) extends DefaultRenderer[A, Label](new Label) {
+ def configure(table: Table, isSelected: Boolean, hasFocus: Boolean, a: A, row: Int, column: Int) {
+ val (icon, text) = convert(a)
+ component.icon = icon
+ component.text = text
+ }
+ }
+
+ private[swing] trait JTableMixin { def tableWrapper: Table }
}
/**
* @see javax.swing.JTable
*/
class Table extends Component with Scrollable with Publisher {
- override lazy val peer: JTable = new JTable {
+ override lazy val peer: JTable = new JTable with Table.JTableMixin {
+ def tableWrapper = Table.this
override def getCellRenderer(r: Int, c: Int) = new TableCellRenderer {
def getTableCellRendererComponent(table: JTable, value: AnyRef, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int) =
Table.this.renderer(isSelected, hasFocus, row, column).peer
}
override def getCellEditor(r: Int, c: Int) = editor(r, c)
- override def getValueAt(r: Int, c: Int) = Table.this.apply(r,c)
+ override def getValueAt(r: Int, c: Int) = Table.this.apply(r,c).asInstanceOf[AnyRef]
}
import Table._
@@ -188,10 +244,25 @@ class Table extends Component with Scrollable with Publisher {
Table.this.peer.getDefaultEditor(classOf[Object])
}
- def apply(row: Int, column: Int) = model.getValueAt(row, column)
- def update(row: Int, column: Int, value: Any) = model.setValueAt(value, row, column)
+ /**
+ * Get the current value of the given cell.
+ */
+ def apply(row: Int, column: Int): Any = model.getValueAt(row, column)
+
+ /**
+ * Change the value of the given cell.
+ */
+ def update(row: Int, column: Int, value: Any) { model.setValueAt(value, row, column) }
+
+ /**
+ * Visually update the given cell.
+ */
+ def updateCell(row: Int, column: Int) = update(row, column, apply(row, column))
- def markUpdated(row: Int, column: Int) = update(row, column, apply(row, column))
+ def selectionForeground: Color = peer.getSelectionForeground
+ def selectionForeground_=(c: Color) = peer.setSelectionForeground(c)
+ def selectionBackground: Color = peer.getSelectionBackground
+ def selectionBackground_=(c: Color) = peer.setSelectionBackground(c)
protected val modelListener = new TableModelListener {
def tableChanged(e: TableModelEvent) = publish(
diff --git a/src/swing/scala/swing/TextComponent.scala b/src/swing/scala/swing/TextComponent.scala
index b1e53b98c3..e24940680e 100644
--- a/src/swing/scala/swing/TextComponent.scala
+++ b/src/swing/scala/swing/TextComponent.scala
@@ -22,7 +22,7 @@ object TextComponent {
* @see javax.swing.JTextComponent
*/
class TextComponent extends Component with Publisher {
- override lazy val peer: JTextComponent = new JTextComponent {}
+ override lazy val peer: JTextComponent = new JTextComponent with SuperMixin {}
def text: String = peer.getText
def text_=(t: String) = peer.setText(t)
@@ -53,6 +53,8 @@ class TextComponent extends Component with Publisher {
def copy() { peer.copy() }
def selected: String = peer.getSelectedText
+ def selectAll() { peer.selectAll() }
+
peer.getDocument.addDocumentListener(new DocumentListener {
def changedUpdate(e:DocumentEvent) { publish(ValueChanged(TextComponent.this, true)) }
def insertUpdate(e:DocumentEvent) { publish(ValueChanged(TextComponent.this, true)) }
diff --git a/src/swing/scala/swing/TextField.scala b/src/swing/scala/swing/TextField.scala
index bd23457e04..3844c8c515 100644
--- a/src/swing/scala/swing/TextField.scala
+++ b/src/swing/scala/swing/TextField.scala
@@ -10,7 +10,7 @@ import event._
* @see javax.swing.JTextField
*/
class TextField(text0: String, columns0: Int) extends TextComponent with TextComponent.HasColumns {
- override lazy val peer: JTextField = new JTextField(text0, columns0)
+ override lazy val peer: JTextField = new JTextField(text0, columns0) with SuperMixin
def this(text: String) = this(text, 0)
def this(columns: Int) = this("", columns)
def this() = this("")
@@ -18,7 +18,7 @@ class TextField(text0: String, columns0: Int) extends TextComponent with TextCom
def columns: Int = peer.getColumns
def columns_=(n: Int) = peer.setColumns(n)
- peer.addActionListener(new ActionListener {
- def actionPerformed(e: ActionEvent) { publish(ValueChanged(TextField.this, false)) }
+ peer.addActionListener(Swing.ActionListener { e =>
+ publish(ValueChanged(TextField.this, false))
})
}
diff --git a/src/swing/scala/swing/event/ActionEvent.scala b/src/swing/scala/swing/event/ActionEvent.scala
new file mode 100644
index 0000000000..e1020fe82c
--- /dev/null
+++ b/src/swing/scala/swing/event/ActionEvent.scala
@@ -0,0 +1,7 @@
+package scala.swing.event
+
+object ActionEvent {
+ def unapply(e: ActionEvent): Option[Component] = Some(e.source)
+}
+
+trait ActionEvent extends ComponentEvent
diff --git a/src/swing/scala/swing/event/ButtonClicked.scala b/src/swing/scala/swing/event/ButtonClicked.scala
index c0029f27fe..ce3fcf3c18 100644
--- a/src/swing/scala/swing/event/ButtonClicked.scala
+++ b/src/swing/scala/swing/event/ButtonClicked.scala
@@ -1,4 +1,4 @@
package scala.swing.event
-case class ButtonClicked(override val source: AbstractButton) extends ComponentEvent(source)
+case class ButtonClicked(override val source: AbstractButton) extends ComponentEvent(source) with ActionEvent
diff --git a/src/swing/scala/swing/event/LiveEvent.scala b/src/swing/scala/swing/event/LiveEvent.scala
index a68a616f90..868a2d0164 100644
--- a/src/swing/scala/swing/event/LiveEvent.scala
+++ b/src/swing/scala/swing/event/LiveEvent.scala
@@ -1,5 +1,14 @@
package scala.swing.event
-trait LiveEvent {
+object LiveEvent {
+ def unapply(e: LiveEvent): Option[(Component, Boolean)] = Some(e.source, e.live)
+}
+
+/**
+ * An event that indicates some editing operation that can be explicitly
+ * finished by some final action. Example: entering text in a text field
+ * (not a text area) is finalized not before the user hits enter.
+ */
+trait LiveEvent extends ComponentEvent {
def live: Boolean
}
diff --git a/src/swing/scala/swing/test/ComboBoxes.scala b/src/swing/scala/swing/test/ComboBoxes.scala
new file mode 100644
index 0000000000..46441dbeef
--- /dev/null
+++ b/src/swing/scala/swing/test/ComboBoxes.scala
@@ -0,0 +1,79 @@
+package scala.swing.test
+
+import swing._
+import event._
+import java.util.Date
+import java.awt.Color
+import java.text.SimpleDateFormat
+import javax.swing.{Icon, ImageIcon}
+
+/**
+ * Demonstrates how to use combo boxes and custom item renderers.
+ *
+ * TODO: clean up layout
+ */
+object ComboBoxes extends SimpleGUIApplication {
+ import ComboBox._
+ val ui = new FlowPanel {
+ contents += new ComboBox(List(1,2,3,4))
+
+ val patterns = List("dd MMMMM yyyy",
+ "dd.MM.yy",
+ "MM/dd/yy",
+ "yyyy.MM.dd G 'at' hh:mm:ss z",
+ "EEE, MMM d, ''yy",
+ "h:mm a",
+ "H:mm:ss:SSS",
+ "K:mm a,z",
+ "yyyy.MMMMM.dd GGG hh:mm aaa")
+ val dateBox = new ComboBox(patterns) { makeEditable() }
+ contents += dateBox
+ val field = new TextField(20) { editable = false }
+ contents += field
+
+ reactions += {
+ case SelectionChanged(`dateBox`) => reformat()
+ }
+ listenTo(dateBox.selection)
+
+ def reformat() {
+ try {
+ val today = new Date
+ val formatter = new SimpleDateFormat(dateBox.selection.item)
+ val dateString = formatter.format(today)
+ field.foreground = Color.black
+ field.text = dateString
+ } catch {
+ case e: IllegalArgumentException =>
+ field.foreground = Color.red
+ field.text = "Error: " + e.getMessage
+ }
+ }
+
+ val icons = List(new ImageIcon(resourceFromUserDirectory("swing/images/margarita1.jpg").toURL),
+ new ImageIcon(resourceFromUserDirectory("swing/images/margarita2.jpg").toURL),
+ new ImageIcon(resourceFromUserDirectory("swing/images/rose.jpg").toURL),
+ new ImageIcon(resourceFromUserDirectory("swing/images/banana.jpg").toURL))
+
+ val iconBox = new ComboBox(icons) {
+ renderer = new ListView.DefaultRenderer[Icon, Label](new Label) {
+ def configure(list: ListView[_<:Icon], isSelected: Boolean, hasFocus: Boolean, icon: Icon, index: Int) {
+ component.icon = icon
+ component.xAlignment = Alignment.Center
+ if(isSelected) {
+ component.border = Border.Line(list.selectionBackground, 3)
+ } else {
+ component.border = Border.Empty(3)
+ }
+ }
+ }
+ }
+ contents += iconBox
+ }
+
+ def top = new MainFrame {
+ title = "ComboBoxes Demo"
+ contents = ui
+ }
+}
+
diff --git a/src/swing/scala/swing/test/TableSelection.scala b/src/swing/scala/swing/test/TableSelection.scala
index f9b4a8c20f..7cb7c45755 100644
--- a/src/swing/scala/swing/test/TableSelection.scala
+++ b/src/swing/scala/swing/test/TableSelection.scala
@@ -12,9 +12,15 @@ object TableSelection extends SimpleGUIApplication {
List("Philip", "Milne", "Pool", 5, false).toArray)
val ui = new BoxPanel(Orientation.Vertical) {
- val table = new Table(model, Array("First Name", "Last Name", "Sport", "# of Years", "Vegetarian"))
- listenTo()
- table.preferredViewportSize = new Dimension(500, 70)
+ val table = new Table(model, Array("First Name", "Last Name", "Sport", "# of Years", "Vegetarian")) {
+ preferredViewportSize = new Dimension(500, 70)
+ val l = new Table.LabelRenderer[String](a => (null,a))
+ override def renderer(isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component =
+ TableSelection.model(row)(column) match {
+ case s: String => l.componentFor(this, isSelected, hasFocus, s, row, column)
+ case _ => super.renderer(isSelected, hasFocus, row, column)
+ }
+ }
//1.6:table.fillsViewportHeight = true
listenTo(table.selection)
diff --git a/src/swing/scala/swing/test/UIDemo.scala b/src/swing/scala/swing/test/UIDemo.scala
index 830fbff28a..a55913d47d 100644
--- a/src/swing/scala/swing/test/UIDemo.scala
+++ b/src/swing/scala/swing/test/UIDemo.scala
@@ -70,6 +70,7 @@ object UIDemo extends SimpleGUIApplication {
pages += new Page("Converter", CelsiusConverter2.ui)
pages += new Page("Tables", TableSelection.ui)
pages += new Page("Dialogs", Dialogs.ui)
+ pages += new Page("Combo Boxes", ComboBoxes.ui)
val password = new FlowPanel {
contents += new Label("Enter your secret password here ")
@@ -105,5 +106,7 @@ object UIDemo extends SimpleGUIApplication {
}
}
}
+ //val keys = javax.swing.UIManager.getDefaults().keys
+ //while(keys.hasMoreElements) println(keys.nextElement)
}