diff options
45 files changed, 460 insertions, 75 deletions
diff --git a/src/swing/scala/swing/Alignment.scala b/src/swing/scala/swing/Alignment.scala index 848f7449c9..6ef638ff95 100644 --- a/src/swing/scala/swing/Alignment.scala +++ b/src/swing/scala/swing/Alignment.scala @@ -2,6 +2,10 @@ package scala.swing import javax.swing.SwingConstants._ +/** + * Horizontal and vertical alignments. We sacrifice a bit of type-safety + * for simplicity here. + */ object Alignment extends Enumeration { val Left = Value(LEFT) val Right = Value(RIGHT) diff --git a/src/swing/scala/swing/Applet.scala b/src/swing/scala/swing/Applet.scala index 99c3c35c03..7eb1d37120 100644 --- a/src/swing/scala/swing/Applet.scala +++ b/src/swing/scala/swing/Applet.scala @@ -5,8 +5,9 @@ import javax.swing.JApplet /** * Clients should implement the ui field. See the SimpleApplet demo for an example. * - * Note: Applet extends JApplet to satisfy Java's applet loading mechanism. - * The usual component wrapping scheme doesn't work here. + * Note: <code>Applet</code> extends <code>javax.swing.JApplet</code> to satisfy + * Java's applet loading mechanism. The usual component wrapping scheme doesn't + * work here. * * @see javax.swing.JApplet */ diff --git a/src/swing/scala/swing/Border.scala b/src/swing/scala/swing/Border.scala index fef3481eeb..13d5d7ae1e 100644 --- a/src/swing/scala/swing/Border.scala +++ b/src/swing/scala/swing/Border.scala @@ -5,6 +5,8 @@ import javax.swing.{Icon, BorderFactory} import javax.swing.border._ /** + * Helper object for creating <code>java.awt.Border</code>s more conveniently. + * * @see javax.swing.BorderFactory */ object Border { diff --git a/src/swing/scala/swing/BoxPanel.scala b/src/swing/scala/swing/BoxPanel.scala index d3de33030b..101298bf6e 100644 --- a/src/swing/scala/swing/BoxPanel.scala +++ b/src/swing/scala/swing/BoxPanel.scala @@ -1,6 +1,9 @@ package scala.swing /** + * A panel that lays out its contents one after the other, + * either horizontally or vertically. + * * @see javax.swing.BoxLayout */ class BoxPanel(orientation: Orientation.Value) extends Panel with SequentialContainer.Wrapper { diff --git a/src/swing/scala/swing/Button.scala b/src/swing/scala/swing/Button.scala index acdb212486..11d500c41b 100644 --- a/src/swing/scala/swing/Button.scala +++ b/src/swing/scala/swing/Button.scala @@ -4,6 +4,9 @@ import javax.swing.{AbstractButton => JAbstractButton,Icon} import event._ /** + * Base class of all button-like widgets, such as push buttons, + * check boxes, and radio buttons. + * * @see javax.swing.AbstractButton */ abstract class Button extends Component with Action.Trigger with Publisher { diff --git a/src/swing/scala/swing/ButtonGroup.scala b/src/swing/scala/swing/ButtonGroup.scala index 66fa8fe874..bdbf322059 100644 --- a/src/swing/scala/swing/ButtonGroup.scala +++ b/src/swing/scala/swing/ButtonGroup.scala @@ -5,6 +5,9 @@ import event._ import scala.collection._ /** + * A button mutex. At most one of its associated buttons is selected + * at a time. + * * @see javax.swing.ButtonGroup */ class ButtonGroup(initialButtons: Button*) { diff --git a/src/swing/scala/swing/CheckBox.scala b/src/swing/scala/swing/CheckBox.scala index 20df7cecb9..ceea4483a1 100644 --- a/src/swing/scala/swing/CheckBox.scala +++ b/src/swing/scala/swing/CheckBox.scala @@ -3,6 +3,8 @@ package scala.swing import javax.swing._ /** + * Two state button that can be either be checked or not. + * * @see javax.swing.JCheckBox */ class CheckBox(text: String) extends ToggleButton { diff --git a/src/swing/scala/swing/Component.scala b/src/swing/scala/swing/Component.scala index 9e4aa71a4f..7b37a0e500 100644 --- a/src/swing/scala/swing/Component.scala +++ b/src/swing/scala/swing/Component.scala @@ -8,11 +8,18 @@ import javax.swing.JComponent import javax.swing.border.Border object Component { - val ClientKey = "scala.swingWrapper" - def wrapperFor[C<:Component](c: javax.swing.JComponent): C = c.getClientProperty(ClientKey).asInstanceOf[C] + private val ClientKey = "scala.swingWrapper" + + /** + * Returns the wrapper for a given peer. + */ + protected[swing] def wrapperFor[C<:Component](c: javax.swing.JComponent): C = + c.getClientProperty(ClientKey).asInstanceOf[C] } /** + * Base class for all UI elements that can be displayed in a window. + * * @see javax.swing.JComponent */ abstract class Component extends UIElement with Publisher { @@ -94,7 +101,14 @@ abstract class Component extends UIElement with Publisher { } }) + /** + * Contains publishers for various mouse events. They are subdivided for + * efficiency reasons. + */ object Mouse { + /** + * Publishes clicks, presses and releases. + */ val clicks: Publisher = new Publisher { peer.addMouseListener(new MouseListener { def mouseEntered(e: java.awt.event.MouseEvent) { } @@ -113,6 +127,9 @@ abstract class Component extends UIElement with Publisher { } }) } + /** + * Publishes enters, exits, moves, and drags. + */ val moves: Publisher = new Publisher { peer.addMouseListener(new MouseListener { def mouseEntered(e: java.awt.event.MouseEvent) { @@ -138,6 +155,9 @@ abstract class Component extends UIElement with Publisher { } }) } + /** + * Publishes mouse wheel moves. + */ val wheel: Publisher = new Publisher { peer.addMouseWheelListener(new MouseWheelListener { def mouseWheelMoved(e: java.awt.event.MouseWheelEvent) { diff --git a/src/swing/scala/swing/ComponentList.scala b/src/swing/scala/swing/ComponentList.scala deleted file mode 100644 index 1bcecddf0d..0000000000 --- a/src/swing/scala/swing/ComponentList.scala +++ /dev/null @@ -1,18 +0,0 @@ -package scala.swing - -import javax.swing._ -import event._ - -class ComponentList extends Component with SequentialContainer.Wrapper { - override lazy val peer: JList = new JList - def this(elems: Seq[Object]) = { - this() - peer.setListData(elems.toArray) - } - - def fixedCellWidth = peer.getFixedCellWidth - def fixedCellWidth_=(x: Int) = peer.setFixedCellWidth(x) - - def fixedCellHeight = peer.getFixedCellHeight - def fixedCellHeight_=(x: Int) = peer.setFixedCellHeight(x) -}
\ No newline at end of file diff --git a/src/swing/scala/swing/Container.scala b/src/swing/scala/swing/Container.scala index 20ef0c0fed..0c421f6f4a 100644 --- a/src/swing/scala/swing/Container.scala +++ b/src/swing/scala/swing/Container.scala @@ -35,6 +35,10 @@ object Container { } } +/** + * A UI element that can contain <code>Component</code>s, such as windows, + * panels, and menus. + */ trait Container extends UIElement { def contents: Seq[Component] }
\ No newline at end of file diff --git a/src/swing/scala/swing/FileChooser.scala b/src/swing/scala/swing/FileChooser.scala new file mode 100644 index 0000000000..92bf3c54e1 --- /dev/null +++ b/src/swing/scala/swing/FileChooser.scala @@ -0,0 +1,74 @@ +package scala.swing + +import java.io.File +import javax.swing._ +import javax.swing.filechooser._ + +object FileChooser { + /** + * The result of a file dialog. The precise meaning of the <code>Approve</code> + * result depends on the specific dialog kind. + */ + object Result extends Enumeration { + val Cancel = Value(JFileChooser.CANCEL_OPTION) + val Approve = Value(JFileChooser.APPROVE_OPTION) + val Error = Value(JFileChooser.ERROR_OPTION) + } + + /** + * The kind of elements a user can select in a file dialog. + */ + object SelectionMode extends Enumeration { + val FilesOnly = Value(JFileChooser.FILES_ONLY) + val DirectoriesOnly = Value(JFileChooser.DIRECTORIES_ONLY) + val FilesAndDirectories = Value(JFileChooser.FILES_AND_DIRECTORIES) + } +} + +/** + * Used to open file dialogs. + * + * @see javax.swing.JFileChooser + */ +class FileChooser(dir: File) { + import FileChooser._ + lazy val peer: JFileChooser = new JFileChooser(dir) + + def this() = this(null) + + def showOpenDialog(over: Component): Result.Value = Result(peer.showOpenDialog(over.peer)) + def showSaveDialog(over: Component): Result.Value = Result(peer.showSaveDialog(over.peer)) + def showDialog(over: Component, approveText: String): Result.Value = Result(peer.showDialog(over.peer, approveText)) + + def controlButtonsAreShown: Boolean = peer.getControlButtonsAreShown + def controlButtonsAreShown_=(b: Boolean) { peer.setControlButtonsAreShown(b) } + + def title: String = peer.getDialogTitle + def title_=(t: String) { peer.setDialogTitle(t) } + + def accessory: Component = Component.wrapperFor(peer.getAccessory) + def accessory_=(c: Component) { peer.setAccessory(c.peer) } + + def fileHidingEnabled: Boolean = peer.isFileHidingEnabled + def fileHidingEnabled_=(b: Boolean) { peer.setFileHidingEnabled(b) } + def fileSelectionMode: SelectionMode.Value = SelectionMode(peer.getFileSelectionMode) + def fileSelectionMode_=(s: SelectionMode.Value) { peer.setFileSelectionMode(s.id) } + def fileFilter: FileFilter = peer.getFileFilter + def fileFilter_=(f: FileFilter) { peer.setFileFilter(f) } + + def selectedFile: File = peer.getSelectedFile + def selectedFile_=(file: File) { peer.setSelectedFile(file) } + def selectedFiles: Seq[File] = peer.getSelectedFiles + def selectedFiles_=(files: File*) { peer.setSelectedFiles(files.toArray) } + + def multiSelectionEnabled: Boolean = peer.isMultiSelectionEnabled + def multiSelectionEnable_=(b: Boolean) { peer.setMultiSelectionEnabled(b) } + + def iconFor(f: File) = peer.getIcon(f) + def descriptionFor(f: File) = peer.getDescription(f) + def nameFor(f: File) = peer.getName(f) + def typeDescriptionFor(f: File) = peer.getTypeDescription(f) + def traversable(f: File) = peer.isTraversable(f) + + def acceptAllFileFilter = peer.getAcceptAllFileFilter +} diff --git a/src/swing/scala/swing/FlowPanel.scala b/src/swing/scala/swing/FlowPanel.scala index 05d5ecc0c7..3c3c7d1f31 100644 --- a/src/swing/scala/swing/FlowPanel.scala +++ b/src/swing/scala/swing/FlowPanel.scala @@ -13,6 +13,9 @@ object FlowPanel { } /** + * A panel that lays out its contents horizontally, one after the other. + * If they don't fit, this panel will try to insert line breaks. + * * @see java.awt.FlowLayout */ class FlowPanel(alignment: FlowPanel.Alignment.Value) extends Panel with SequentialContainer.Wrapper { diff --git a/src/swing/scala/swing/Frame.scala b/src/swing/scala/swing/Frame.scala index 65596ca943..b899cc82f6 100644 --- a/src/swing/scala/swing/Frame.scala +++ b/src/swing/scala/swing/Frame.scala @@ -4,6 +4,8 @@ import javax.swing._ import event._ /** + * A decorated window. + * * @see javax.swing.JFrame */ class Frame extends UIElement with RootPanel with Publisher { diff --git a/src/swing/scala/swing/GridBagPanel.scala b/src/swing/scala/swing/GridBagPanel.scala index 8b12cbee60..8e42de28a1 100644 --- a/src/swing/scala/swing/GridBagPanel.scala +++ b/src/swing/scala/swing/GridBagPanel.scala @@ -33,6 +33,9 @@ object GridBagPanel { } /** + * A panel that lays out its children in a grid. Layout details can be + * given for each cell of the grid. + * * @see java.awt.GridBagLayout */ class GridBagPanel extends Panel with LayoutContainer { @@ -41,6 +44,13 @@ class GridBagPanel extends Panel with LayoutContainer { private def layoutManager = peer.getLayout.asInstanceOf[java.awt.GridBagLayout] + implicit def pair2Constraints(p: (Int, Int)): Constraints = { + val c = new Constraints + c.gridx = p._1 + c.gridy = p._2 + c + } + class Constraints(val peer: GridBagConstraints) extends Proxy { def self = peer def this(gridx: Int, gridy: Int, @@ -58,6 +68,11 @@ class GridBagPanel extends Panel with LayoutContainer { def gridx_=(x: Int) { peer.gridx = x } def gridy: Int = peer.gridy def gridy_=(y: Int) { peer.gridy = y } + def grid: (Int, Int) = (gridx, gridy) + def grid_=(c: (Int, Int)) = { + gridx = c._1 + gridy = c._2 + } def gridwidth: Int = peer.gridwidth def gridwidth_=(w: Int) { peer.gridwidth = w } @@ -79,6 +94,9 @@ class GridBagPanel extends Panel with LayoutContainer { def ipady_=(y: Int) { peer.ipady = y } } - def constraintsFor(comp: Component) = + protected def constraintsFor(comp: Component) = new Constraints(layoutManager.getConstraints(comp.peer)) + + protected def areValid(c: Constraints): (Boolean, String) = (true, "") + protected def add(c: Component, l: Constraints) { peer.add(c.peer, l.peer) } } diff --git a/src/swing/scala/swing/GridPanel.scala b/src/swing/scala/swing/GridPanel.scala index c3ef15c843..0d92b8117f 100644 --- a/src/swing/scala/swing/GridPanel.scala +++ b/src/swing/scala/swing/GridPanel.scala @@ -5,11 +5,26 @@ object GridPanel { } /** + * A panel that lays out its contents in a uniform grid. * @see java.awt.GridLayout */ class GridPanel(rows0: Int, cols0: Int) extends Panel with SequentialContainer.Wrapper { override lazy val peer = new javax.swing.JPanel(new java.awt.GridLayout(rows0, cols0)) + /*type Constraints = (Int, Int) + + protected def constraintsFor(comp: Component) = { + assert(peer.getComponentOrientation.isHorizontal) + val idx = contents.indexOf(comp) + val (r, c) = (((idx-1)/columns)+1, ((idx-1)%columns)+1) + if (peer.getComponentOrientation.isLeftToRight) (r, c) + else (r, columns-c+1) + } + + protected def add(c: Component, l: Constraints) { peer.add(c.peer, (l._1-1)*columns+l._2) } + protected def areValid(c: Constraints): (Boolean, String) = + ((c._1 > 0 && c._2 > 0), "Grid coordinates (row,col) must be >= 1 but where " + c)*/ + private def layoutManager = peer.getLayout.asInstanceOf[java.awt.GridLayout] def rows: Int = layoutManager.getRows diff --git a/src/swing/scala/swing/Icon.scala b/src/swing/scala/swing/Icon.scala index 2a794df29f..a36a55a45f 100644 --- a/src/swing/scala/swing/Icon.scala +++ b/src/swing/scala/swing/Icon.scala @@ -2,7 +2,14 @@ package scala.swing import javax.swing._ +/** + * Helper object for icons. + */ object Icon { + /** + * The empty icon. Use this icon instead of <code>null</code> to indicate + * that you don't want an icon. + */ case object Empty extends Icon { def getIconHeight: Int = 0 def getIconWidth: Int = 0 diff --git a/src/swing/scala/swing/Label.scala b/src/swing/scala/swing/Label.scala index 04ef62ab15..20692628d8 100644 --- a/src/swing/scala/swing/Label.scala +++ b/src/swing/scala/swing/Label.scala @@ -3,6 +3,8 @@ package scala.swing import javax.swing._ /** + * A label component. + * * @see javax.swing.JLabel */ class Label(text0: String) extends Component { diff --git a/src/swing/scala/swing/LayoutContainer.scala b/src/swing/scala/swing/LayoutContainer.scala index f7982b27ef..960c87e426 100644 --- a/src/swing/scala/swing/LayoutContainer.scala +++ b/src/swing/scala/swing/LayoutContainer.scala @@ -8,11 +8,36 @@ import scala.collection.mutable.Map * See GridBagPanel for an example. */ trait LayoutContainer extends Container.Wrapper { - type Constraints <: { def peer: AnyRef } + /** + * The specific type of constraints. + */ + type Constraints <: AnyRef + + /** + * Used to obtain the constraints from a Swing layout manager. + */ protected def constraintsFor(c: Component): Constraints + /** + * Checks whether the given constraints are valid. Additionally returns + * an error string that is only fetched if the constraints aren't valid. + */ + protected def areValid(c: Constraints): (Boolean, String) + /** + * Adds the component to the layout manager and the peer. + */ + protected def add(comp: Component, c: Constraints) + + /** + * A map of components to the associated layout constraints. + * Any element in this map is automatically added to this panel. + */ def layout: Map[Component, Constraints] = new Map[Component, Constraints] { def -=(c: Component) { _contents -= c } - def update(c: Component, l: Constraints) { peer.add(c.peer, l.peer) } + def update(c: Component, l: Constraints) { + val (v, msg) = areValid(l) + if (!v) throw new IllegalArgumentException(msg) + add(c, l) + } def get(c: Component) = Swing.toOption(constraintsFor(c)) def size = peer.getComponentCount def elements: Iterator[(Component, Constraints)] = diff --git a/src/swing/scala/swing/ListView.scala b/src/swing/scala/swing/ListView.scala new file mode 100644 index 0000000000..c0a8b2fc4c --- /dev/null +++ b/src/swing/scala/swing/ListView.scala @@ -0,0 +1,78 @@ +package scala.swing + +import javax.swing._ +import javax.swing.event._ +import event._ + +object ListView { + object IntervalMode extends Enumeration { + val Single = Value(ListSelectionModel.SINGLE_SELECTION) + val SingleInterval = Value(ListSelectionModel.SINGLE_INTERVAL_SELECTION) + val MultiInterval = Value(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + } +} + +/** + * A component that displays its elements in a list. Named + * <code>ListView</code> to avoid a clash with the frequently used + * <code>scala.List</code> + * + * @see javax.swing.JList + */ +class ListView[A] extends Component { + import ListView._ + override lazy val peer: JList = new JList + + def this(elems: Seq[A]) = { + this() + peer.setModel(new AbstractListModel { + def getElementAt(n: Int) = elems(n).asInstanceOf[AnyRef] + def getSize = elems.size + }) + } + + object selection extends Publisher { + protected abstract class Indices[A](a: =>Seq[A]) extends scala.collection.mutable.Set[A] { + def -=(n: A) + def +=(n: A) + def contains(n: A) = a.contains(n) + def size = a.length + def elements = a.elements + } + + object indices extends Indices(peer.getSelectedIndices) { + def -=(n: Int) { peer.removeSelectionInterval(n,n) } + def +=(n: Int) { peer.addSelectionInterval(n,n) } + + def leadIndex: Int = peer.getSelectionModel.getLeadSelectionIndex + def anchorIndex: Int = peer.getSelectionModel.getAnchorSelectionIndex + } + + object elements extends SeqProxy[A] { + def self = peer.getSelectedValues.projection.map(_.asInstanceOf[A]) + def leadIndex: Int = peer.getSelectionModel.getLeadSelectionIndex + def anchorIndex: Int = peer.getSelectionModel.getAnchorSelectionIndex + } + + def intervalMode: IntervalMode.Value = IntervalMode(peer.getSelectionModel.getSelectionMode) + def intervalMode_=(m: IntervalMode.Value) { peer.getSelectionModel.setSelectionMode(m.id) } + + peer.getSelectionModel.addListSelectionListener(new ListSelectionListener { + def valueChanged(e: javax.swing.event.ListSelectionEvent) { + publish(ElementSelected(ListView.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting)) + } + }) + } + + def fixedCellWidth = peer.getFixedCellWidth + def fixedCellWidth_=(x: Int) = peer.setFixedCellWidth(x) + + def fixedCellHeight = peer.getFixedCellHeight + def fixedCellHeight_=(x: Int) = peer.setFixedCellHeight(x) + + 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)) } + def intervalAdded(e: ListDataEvent) { publish(ListElementsAdded(ListView.this, e.getIndex0 to e.getIndex1)) } + }) +}
\ No newline at end of file diff --git a/src/swing/scala/swing/MainFrame.scala b/src/swing/scala/swing/MainFrame.scala index 882226dd18..018866ba2b 100644 --- a/src/swing/scala/swing/MainFrame.scala +++ b/src/swing/scala/swing/MainFrame.scala @@ -2,6 +2,10 @@ package scala.swing import event._ +/** + * A frame that can be used for main application windows. Quits the + * application on close. + */ class MainFrame extends Frame { reactions += { case WindowClosing(_) => System.exit(1) diff --git a/src/swing/scala/swing/Orientable.scala b/src/swing/scala/swing/Orientable.scala index a4fc0126d0..c350b7a002 100644 --- a/src/swing/scala/swing/Orientable.scala +++ b/src/swing/scala/swing/Orientable.scala @@ -1,5 +1,8 @@ package scala.swing +/** + * An <code>Oriented</code> whose orientation can be changed. + */ trait Orientable extends Oriented { def orientation_=(o: Orientation.Value) { peer.setOrientation(o.id) } } diff --git a/src/swing/scala/swing/Oriented.scala b/src/swing/scala/swing/Oriented.scala index 7efa61d615..b9caff5da2 100644 --- a/src/swing/scala/swing/Oriented.scala +++ b/src/swing/scala/swing/Oriented.scala @@ -1,5 +1,8 @@ package scala.swing +/** + * Something that can have an orientation. + */ trait Oriented { def peer: javax.swing.JComponent { def getOrientation(): Int diff --git a/src/swing/scala/swing/Panel.scala b/src/swing/scala/swing/Panel.scala index d93f0b065b..ac5a8de047 100644 --- a/src/swing/scala/swing/Panel.scala +++ b/src/swing/scala/swing/Panel.scala @@ -1,6 +1,8 @@ package scala.swing /** + * A component that can contain other components, such as layout panels. + * * @see javax.swing.JPanel */ abstract class Panel extends Component with Container.Wrapper { diff --git a/src/swing/scala/swing/Publisher.scala b/src/swing/scala/swing/Publisher.scala index 28462055cc..ad1b3b5da0 100644 --- a/src/swing/scala/swing/Publisher.scala +++ b/src/swing/scala/swing/Publisher.scala @@ -3,6 +3,9 @@ package scala.swing import scala.collection.mutable.HashSet import event.Event +/** + * Notifies subscribed observers when a event is published. + */ trait Publisher extends Reactor { protected var listeners = new HashSet[Reactions] diff --git a/src/swing/scala/swing/PushButton.scala b/src/swing/scala/swing/PushButton.scala index 245f443119..0e4d6c5f8c 100644 --- a/src/swing/scala/swing/PushButton.scala +++ b/src/swing/scala/swing/PushButton.scala @@ -4,6 +4,8 @@ import javax.swing._ import event._ /** + * A button that can be pushed, usually to perfrom some action. + * * @see javax.swing.JButton */ class PushButton(text0: String) extends Button with Publisher { diff --git a/src/swing/scala/swing/RadioButton.scala b/src/swing/scala/swing/RadioButton.scala index c863375f38..307051a1b0 100644 --- a/src/swing/scala/swing/RadioButton.scala +++ b/src/swing/scala/swing/RadioButton.scala @@ -3,6 +3,10 @@ package scala.swing import javax.swing._ /** + * A two state button that is usually used in a <code>ButtonGroup</code> + * together with other <code>RadioButton</code>s, in order to indicate + * that at most one of them can be selected. + * * @see javax.swing.JRadioButton */ class RadioButton(text0: String) extends ToggleButton { diff --git a/src/swing/scala/swing/Reactions.scala b/src/swing/scala/swing/Reactions.scala index 7b94650976..cdb6d198a9 100644 --- a/src/swing/scala/swing/Reactions.scala +++ b/src/swing/scala/swing/Reactions.scala @@ -3,12 +3,21 @@ package scala.swing import event.Event class Reactions { + /** + * Convenience type alias. + */ type Reaction = PartialFunction[Event, unit] private var parts: List[Reaction] = List() + /** + * Add a reaction. + */ def += (r: Reaction) = { parts = r :: parts } + /** + * Remove the given reaction. + */ def -= (r: Reaction) = { def withoutR(xs: List[Reaction]): List[Reaction] = if (xs.isEmpty) xs @@ -17,12 +26,15 @@ class Reactions { 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) + /*else*/ sendTo(ps) // no overwrite semantics } sendTo(parts) } diff --git a/src/swing/scala/swing/Reactor.scala b/src/swing/scala/swing/Reactor.scala index 30606665ce..919ae15209 100644 --- a/src/swing/scala/swing/Reactor.scala +++ b/src/swing/scala/swing/Reactor.scala @@ -1,7 +1,14 @@ -package scala.swing; +package scala.swing trait Reactor { val reactions = new Reactions + /** + * Listen to the given publisher as long as <code>deafTo</code> isn't called for + * them. + */ def listenTo(ps: Publisher*) = for (val p <- ps) p.subscribe(reactions) + /** + * Installed reaction won't receive events from the given publisher anylonger. + */ def deafTo(ps: Publisher*) = for (val p <- ps) p.unsubscribe(reactions) } diff --git a/src/swing/scala/swing/RootPanel.scala b/src/swing/scala/swing/RootPanel.scala index 95fa5ef140..584007a60f 100644 --- a/src/swing/scala/swing/RootPanel.scala +++ b/src/swing/scala/swing/RootPanel.scala @@ -1,20 +1,20 @@ package scala.swing /** + * The root of a component hierarchy. Contains at most one component. + * * @see javax.swing.RootPaneContainer */ trait RootPanel extends Container { def peer: java.awt.Component with javax.swing.RootPaneContainer - //contents = new Component { opaque = false } + + /** + * At most one component. + */ def contents: Seq[Component] = { Swing.toOption(peer.getContentPane.getComponent(0)).map { c => Component.wrapperFor(c.asInstanceOf[javax.swing.JComponent]) }.toList - /*if (peer.getContentPane.getComponentCount == 0) Nil - else { - val c = peer.getContentPane.getComponent(0) - ListComponent.wrapperFor(c.asInstanceOf[javax.swing.JComponent]) - }*/ } def contents_=(c: Component) { if (peer.getContentPane.getComponentCount > 0) { diff --git a/src/swing/scala/swing/ScrollPane.scala b/src/swing/scala/swing/ScrollPane.scala index 833d9dacc1..a451745631 100644 --- a/src/swing/scala/swing/ScrollPane.scala +++ b/src/swing/scala/swing/ScrollPane.scala @@ -3,6 +3,9 @@ package scala.swing import javax.swing.JScrollPane /** + * Can have at most a single child that is put inside a canvas that can be + * scrolled. + * * @see javax.swing.JScrollPane */ class ScrollPane extends Component with Container { @@ -18,6 +21,7 @@ class ScrollPane extends Component with Container { def rowHeaderView: Option[Component] = Swing.toOption(peer.getRowHeader.getView).map(Component.wrapperFor(_)) def rowHeaderView_=(c: Component) = peer.setRowHeaderView(c.peer) def rowHeaderView_=(c: Option[Component]) = peer.setRowHeaderView(Swing.toNull(c.map(_.peer))) + def viewportView: Option[Component] = Swing.toOption(peer.getViewport.getView).map(Component.wrapperFor(_)) def viewportView_=(c: Component) = peer.setViewportView(c.peer) def viewportView_=(c: Option[Component]) = peer.setViewportView(Swing.toNull(c.map(_.peer))) diff --git a/src/swing/scala/swing/Scrollable.scala b/src/swing/scala/swing/Scrollable.scala index 73be856459..3a2acb8426 100644 --- a/src/swing/scala/swing/Scrollable.scala +++ b/src/swing/scala/swing/Scrollable.scala @@ -3,6 +3,9 @@ package scala.swing import java.awt.Rectangle /** + * A component that specially suitable for being placed inside a + * <code>ScrollPane</code>. + * * @see javax.swing.Scrollable */ trait Scrollable extends Component { diff --git a/src/swing/scala/swing/Separator.scala b/src/swing/scala/swing/Separator.scala index 0b7d3e0840..94e467a3b0 100644 --- a/src/swing/scala/swing/Separator.scala +++ b/src/swing/scala/swing/Separator.scala @@ -3,6 +3,8 @@ package scala.swing import javax.swing._ /** + * A bar that can be used a separator, most commonly in menus. + * * @see javax.swing.JSeparator */ class Separator(o: Orientation.Value) extends Component with Oriented { diff --git a/src/swing/scala/swing/SequentialContainer.scala b/src/swing/scala/swing/SequentialContainer.scala index 333c0b1969..0d1270cb72 100644 --- a/src/swing/scala/swing/SequentialContainer.scala +++ b/src/swing/scala/swing/SequentialContainer.scala @@ -9,6 +9,9 @@ object SequentialContainer { } } +/** + * A container that contains children in a certain order. + */ trait SequentialContainer extends Container { override def contents: Buffer[Component] def contents_=(c: Component*) diff --git a/src/swing/scala/swing/Swing.scala b/src/swing/scala/swing/Swing.scala index 2e48e26904..e34ab1052b 100644 --- a/src/swing/scala/swing/Swing.scala +++ b/src/swing/scala/swing/Swing.scala @@ -4,6 +4,9 @@ import java.awt.Dimension import javax.swing._ import javax.swing.event._ +/** + * Helpers for this package. + */ object Swing { protected[swing] def ifNull[A](o: Object, a: A): A = if(o eq null) a else o.asInstanceOf[A] protected[swing] def toOption[A](o: Object): Option[A] = if(o eq null) None else Some(o.asInstanceOf[A]) diff --git a/src/swing/scala/swing/Table.scala b/src/swing/scala/swing/Table.scala index 28695844f6..659659fa0a 100644 --- a/src/swing/scala/swing/Table.scala +++ b/src/swing/scala/swing/Table.scala @@ -78,16 +78,16 @@ class Table extends Component with Scrollable with Publisher { //1.6: def fillsViewportHeight: Boolean = peer.getFillsViewportHeight //def fillsViewportHeight_=(b: Boolean) = peer.setFillsViewportHeight(b) - // TODO: could be a sorted set - protected abstract class SelectionSet[A](a: =>Seq[A]) extends Set[A] { - def -=(n: A) - def +=(n: A) - def contains(n: A) = a.contains(n) - def size = a.length - def elements = a.elements - } + object selection extends Publisher { + // TODO: could be a sorted set + protected abstract class SelectionSet[A](a: =>Seq[A]) extends scala.collection.mutable.Set[A] { + def -=(n: A) + def +=(n: A) + def contains(n: A) = a.contains(n) + def size = a.length + def elements = a.elements + } - class Selection extends Publisher { object rows extends SelectionSet(peer.getSelectedRows) { def -=(n: Int) { peer.removeRowSelectionInterval(n,n) } def +=(n: Int) { peer.addRowSelectionInterval(n,n) } @@ -140,18 +140,16 @@ class Table extends Component with Scrollable with Publisher { peer.getColumnModel.getSelectionModel.addListSelectionListener(new ListSelectionListener { def valueChanged(e: javax.swing.event.ListSelectionEvent) { - publish(ColumnsSelected(Table.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting)) + publish(TableColumnsSelected(Table.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting)) } }) peer.getSelectionModel.addListSelectionListener(new ListSelectionListener { def valueChanged(e: javax.swing.event.ListSelectionEvent) { - publish(RowsSelected(Table.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting)) + publish(TableRowsSelected(Table.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting)) } }) } - val selection: Selection = new Selection - private val initialRenderer = peer.getDefaultRenderer(classOf[AnyRef]) /** @@ -175,11 +173,18 @@ class Table extends Component with Scrollable with Publisher { def markUpdated(row: Int, column: Int) = update(row, column, apply(row, column)) model.addTableModelListener(new TableModelListener { - def tableChanged(event: TableModelEvent) = publish( - if (event.getType == TableModelEvent.UPDATE) - TableChanged(Table.this, event.getFirstRow, event.getLastRow, event.getColumn) - else - TableResized(Table.this) + def tableChanged(e: TableModelEvent) = publish( + e.getType match { + case TableModelEvent.UPDATE => + if (e.getLastRow == Math.MAX_INT) + TableChanged(Table.this) + else + TableUpdated(Table.this, e.getFirstRow to e.getLastRow, e.getColumn) + case TableModelEvent.INSERT => + TableRowsAdded(Table.this, e.getFirstRow to e.getLastRow) + case TableModelEvent.DELETE => + TableRowsRemoved(Table.this, e.getFirstRow to e.getLastRow) + } ) }) } diff --git a/src/swing/scala/swing/TextArea.scala b/src/swing/scala/swing/TextArea.scala index 9039e428c6..12abe1b5ea 100644 --- a/src/swing/scala/swing/TextArea.scala +++ b/src/swing/scala/swing/TextArea.scala @@ -5,6 +5,8 @@ import java.awt.event._ import event._ /** + * A text component that allows multiline text input and display. + * * @see javax.swing.JTextArea */ class TextArea(text0: String, rows0: Int, columns0: int) extends TextComponent with TextComponent.HasColumns with TextComponent.HasRows { diff --git a/src/swing/scala/swing/TextComponent.scala b/src/swing/scala/swing/TextComponent.scala index 687076a3a7..b1e53b98c3 100644 --- a/src/swing/scala/swing/TextComponent.scala +++ b/src/swing/scala/swing/TextComponent.scala @@ -17,6 +17,8 @@ object TextComponent { } /** + * A component that allows some kind of text input and display. + * * @see javax.swing.JTextComponent */ class TextComponent extends Component with Publisher { diff --git a/src/swing/scala/swing/TextField.scala b/src/swing/scala/swing/TextField.scala index f5a7342be1..bd23457e04 100644 --- a/src/swing/scala/swing/TextField.scala +++ b/src/swing/scala/swing/TextField.scala @@ -5,6 +5,8 @@ import java.awt.event._ import event._ /** + * A text component that allows single line text input and display. + * * @see javax.swing.JTextField */ class TextField(text0: String, columns0: Int) extends TextComponent with TextComponent.HasColumns { diff --git a/src/swing/scala/swing/ToggleButton.scala b/src/swing/scala/swing/ToggleButton.scala index 27e86ad3ef..4132ecae2c 100644 --- a/src/swing/scala/swing/ToggleButton.scala +++ b/src/swing/scala/swing/ToggleButton.scala @@ -4,6 +4,8 @@ import javax.swing._ import event._ /** + * A two state button with a push button like UI. Usually used in tool bars. + * * @see javax.swing.JToggleButton */ class ToggleButton(text0: String) extends Button { diff --git a/src/swing/scala/swing/UIElement.scala b/src/swing/scala/swing/UIElement.scala index 9cb7bdb37a..6bfd579360 100644 --- a/src/swing/scala/swing/UIElement.scala +++ b/src/swing/scala/swing/UIElement.scala @@ -3,7 +3,7 @@ package scala.swing import java.awt.{Color, Cursor, Font} /** - * The root of all user interface elements. + * The base trait of all user interface elements. */ trait UIElement { def peer: java.awt.Component diff --git a/src/swing/scala/swing/event/ListEvent.scala b/src/swing/scala/swing/event/ListEvent.scala new file mode 100644 index 0000000000..aa1f6d9878 --- /dev/null +++ b/src/swing/scala/swing/event/ListEvent.scala @@ -0,0 +1,14 @@ +package scala.swing.event + +abstract class ListEvent[A](override val source: ListView[A]) extends ComponentEvent(source) + +case class ElementSelected[A](override val source: ListView[A], range: Range, live: Boolean) + extends ListEvent(source) with LiveEvent with ListSelectionEvent + +abstract class ListChange[A](override val source: ListView[A]) extends ListEvent(source) + +case class ListChanged[A](override val source: ListView[A]) extends ListChange(source) +case class ListElementsAdded[A](override val source: ListView[A], range: Range) + extends ListChange(source) +case class ListElementsRemoved[A](override val source: ListView[A], range: Range) + extends ListChange(source)
\ No newline at end of file diff --git a/src/swing/scala/swing/event/TableEvent.scala b/src/swing/scala/swing/event/TableEvent.scala index bf917dd239..4d87b353fd 100644 --- a/src/swing/scala/swing/event/TableEvent.scala +++ b/src/swing/scala/swing/event/TableEvent.scala @@ -1,12 +1,18 @@ package scala.swing.event -class TableEvent(override val source: Table) extends ComponentEvent(source) +abstract class TableEvent(override val source: Table) extends ComponentEvent(source) -case class TableChanged(override val source: Table, firstRow: Int, lastRow: Int, column: Int) - extends TableEvent(source) -case class TableResized(override val source: Table) extends TableEvent(source) +abstract class TableChange(override val source: Table) extends TableEvent(source) -case class ColumnsSelected(override val source: Table, range: Range, live: Boolean) +case class TableChanged(override val source: Table) extends TableChange(source) +case class TableUpdated(override val source: Table, range: Range, column: Int) + extends TableChange(source) + +case class TableResized(override val source: Table) extends TableChange(source) +case class TableRowsAdded(override val source: Table, range: Range) extends TableResized(source) +case class TableRowsRemoved(override val source: Table, range: Range) extends TableResized(source) + +case class TableColumnsSelected(override val source: Table, range: Range, live: Boolean) extends TableEvent(source) with LiveEvent with ListSelectionEvent -case class RowsSelected(override val source: Table, range: Range, live: Boolean) +case class TableRowsSelected(override val source: Table, range: Range, live: Boolean) extends TableEvent(source) with LiveEvent with ListSelectionEvent
\ No newline at end of file diff --git a/src/swing/scala/swing/test/Dialogs.scala b/src/swing/scala/swing/test/Dialogs.scala new file mode 100644 index 0000000000..c8cf4698ae --- /dev/null +++ b/src/swing/scala/swing/test/Dialogs.scala @@ -0,0 +1,45 @@ +package scala.swing.test + +import swing._ +import swing.event._ + +object Dialogs extends SimpleGUIApplication { + val ui = new GridBagPanel { grid => + import GridBagPanel._ + val buttonText = new TextField("Click Me") + + val c = new Constraints + c.fill = Fill.Horizontal + c.grid = (1,1) + + val chooser = new FileChooser + layout(new PushButton(Action("Open") { + chooser.showOpenDialog(grid) + })) = c + + c.grid = (1,2) + layout(new PushButton(Action("Save") { + chooser.showSaveDialog(grid) + })) = c + + c.grid = (1,3) + layout(new PushButton(Action("Custom") { + chooser.showDialog(grid, buttonText.text) + })) = c + + c.grid = (2,3) + layout(new Label(" with Text ")) = c + + c.grid = (3,3) + c.ipadx = 50 + layout(buttonText) = c + + border = Border.Empty(5, 5, 5, 5) + } + + def top = new MainFrame { + title = "Dialog Demo" + contents = ui + } +} + diff --git a/src/swing/scala/swing/test/TableSelection.scala b/src/swing/scala/swing/test/TableSelection.scala index cd0d4f40ab..f9b4a8c20f 100644 --- a/src/swing/scala/swing/test/TableSelection.scala +++ b/src/swing/scala/swing/test/TableSelection.scala @@ -11,12 +11,6 @@ object TableSelection extends SimpleGUIApplication { List("Sharon", "Zakhour", "Speed reading", 5, false).toArray, List("Philip", "Milne", "Pool", 5, false).toArray) - /*Array[Array[AnyRef]](Array[AnyRef]("Mary", "Campione", "Snowboarding", new java.lang.Integer(5), new java.lang.Boolean(false)), - Array[AnyRef]("Alison", "Huml", "Rowing", new java.lang.Integer(5), new java.lang.Boolean(false)), - Array[AnyRef]("Kathy", "Walrath", "Knitting", new java.lang.Integer(5), new java.lang.Boolean(false)), - Array[AnyRef]("Sharon", "Zakhour", "Speed reading", new java.lang.Integer(5), new java.lang.Boolean(false)), - Array[AnyRef]("Philip", "Milne", "Pool", new java.lang.Integer(5), new java.lang.Boolean(false)))*/ - val ui = new BoxPanel(Orientation.Vertical) { val table = new Table(model, Array("First Name", "Last Name", "Sport", "# of Years", "Vegetarian")) listenTo() @@ -93,10 +87,10 @@ object TableSelection extends SimpleGUIApplication { case ButtonClicked(`cellSelection`) => if (cellSelection.selected) table.selection.elementMode = Table.ElementMode.Cell - case RowsSelected(_, range, false) => + case TableRowsSelected(_, range, false) => output.append("Rows selected, changes: " + range + "\n") outputSelection() - case ColumnsSelected(_, range, false) => + case TableColumnsSelected(_, range, false) => output.append("Columns selected, changes " + range + "\n") outputSelection() } diff --git a/src/swing/scala/swing/test/UIDemo.scala b/src/swing/scala/swing/test/UIDemo.scala index 00681bd8da..d08208300a 100644 --- a/src/swing/scala/swing/test/UIDemo.scala +++ b/src/swing/scala/swing/test/UIDemo.scala @@ -30,6 +30,8 @@ object UIDemo extends SimpleGUIApplication { menuBar.contents += menu menuBar.contents += new Menu("Empty Menu") + var reactLive = false + contents = new BoxPanel(Orientation.Vertical) { val tabs = new TabbedPane { import TabbedPane._ @@ -45,15 +47,21 @@ object UIDemo extends SimpleGUIApplication { } contents += new BoxPanel(Orientation.Vertical) { border = Compound(Titled(Etched, "Check Boxes"), Empty(5,5,5,10)) - val a = new CheckBox("Paint Labels") - val b = new CheckBox("Paint Ticks") - val c = new CheckBox("Snap To Ticks") - contents.append(a,b,c) - listenTo(a,b,c) + val paintLabels = new CheckBox("Paint Labels") + val paintTicks = new CheckBox("Paint Ticks") + val snapTicks = new CheckBox("Snap To Ticks") + val live = new CheckBox("Live") + contents.append(paintLabels, paintTicks, snapTicks, live) + listenTo(paintLabels, paintTicks, snapTicks, live) reactions += { - case ButtonClicked(`a`) => slider.paintLabels = a.selected - case ButtonClicked(`b`) => slider.paintTicks = b.selected - case ButtonClicked(`c`) => slider.snapToTicks = c.selected + case ButtonClicked(`paintLabels`) => + slider.paintLabels = paintLabels.selected + case ButtonClicked(`paintTicks`) => + slider.paintTicks = paintTicks.selected + case ButtonClicked(`snapTicks`) => + slider.snapToTicks = snapTicks.selected + case ButtonClicked(`live`) => + reactLive = live.selected } } } @@ -61,6 +69,7 @@ object UIDemo extends SimpleGUIApplication { pages += new Page("GridBag", GridBagDemo.ui) pages += new Page("Converter", CelsiusConverter2.ui) pages += new Page("Tables", TableSelection.ui) + pages += new Page("Dialogs", Dialogs.ui) } contents += tabs @@ -75,7 +84,8 @@ object UIDemo extends SimpleGUIApplication { listenTo(slider) listenTo(tabs.selection) reactions += { - case ValueChanged(`slider`, false) => tabs.selection.index = slider.value + case ValueChanged(`slider`, live) => + if(!live || live == reactLive) tabs.selection.index = slider.value case SelectionChanged(`tabs`) => slider.value = tabs.selection.index } } |