scala-swing
scala-swing copied to clipboard
scala.swing.ComboBox should have a method for changing the items
Frequently, the items in the combobox are dynamic and not known at the time of the ComboBox's instantiation.
Imported From: https://issues.scala-lang.org/browse/SI-2154?orig=1 Reporter: David Corbin (dcorbin)
mathieu leclaire (mamat) said (edited on Nov 21, 2011 3:32:31 PM UTC): For now, the only solution is to use the peer method setModel. With scala 2.9.1 and JDK7 as classpath, the following error occurs when compiling : error: something is wrong (wrong class file?): class JComboBox with type parameters [E] gets applied to arguments [], phase = namer
@soc said: @Mathieu: Those two things are most likely two completely separate issues.
Could you provide the code which triggered the compiler error (and probably file a new issue for it)?
mathieu leclaire (mamat) said: In a scala console importing the scala-swing jar: scala> val a = new ComboBox(List("one","two")) scala> a.peer.setModel(ComboBox.newConstantModel(List("three","four")))
gives the following error: error: something is wrong (wrong class file?): class JComboBox with type parameters [E] gets applied to arguments [], phase = namer a.peer.setModel(ComboBox.newConstantModel(List("three","four")))
mathieu leclaire (mamat) said: Hi, did you plan to fix this bug in the next version ? Thanks Mathieu
mathieu leclaire (mamat) said: The bug comes from the fact that since Java 7 a ComboBoxModel (returned by ComboBox.newConstantModel) takes a parameter (http://docs.oracle.com/javase/7/docs/api/javax/swing/ComboBoxModel.html) whereas it did not in Java 6.
mathieu leclaire (mamat) said (edited on Mar 15, 2012 3:59:25 PM UTC): Hi, I wrote a patch for the ComboBox class taking into account the java parametrized classes. It enables to run the small piece of code that I tested :
package scala.swing */
import event._ import javax.swing.{JList, JComponent, JComboBox, JTextField, ComboBoxModel, AbstractListModel, ListCellRenderer} import java.awt.event.ActionListener
object MyComboBox { /**
- 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 integrates nicely with 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: MyComboBox[A])(string2A: String => A, a2String: A => String) extends MyComboBox.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 { 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: MyComboBox[String]): Editor[String] = new BuiltInEditor(c)(s => s, s => s) implicit def intEditor(c: MyComboBox[Int]): Editor[Int] = new BuiltInEditor(c)(s => s.toInt, s => s.toString) implicit def floatEditor(c: MyComboBox[Float]): Editor[Float] = new BuiltInEditor(c)(s => s.toFloat, s => s.toString) implicit def doubleEditor(c: MyComboBox[Double]): Editor[Double] = new BuiltInEditor(c)(s => s.toDouble, s => s.toString)
def newConstantModel[A](items: Seq[A]): ComboBoxModel[A] = { new AbstractListModel[A] with ComboBoxModel[A] { private var selected: A = if (items.isEmpty) null.asInstanceOf[A] else items(0) def getSelectedItem: AnyRef = selected.asInstanceOf[AnyRef] def setSelectedItem(a: Any) { if ((selected != null && selected != a) || selected == null && a != null) { selected = a.asInstanceOf[A] fireContentsChanged(this, -1, -1) } } def getElementAt(n: Int) = items(n).asInstanceOf[A] 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) }*/ }
/**
- Let's the user make a selection from a list of predefined items. Visually,
- this is implemented as a button-like component with a pull-down menu.
- @see javax.swing.JComboBox */ class MyComboBox[A](items: Seq[A]) extends Component with Publisher { override lazy val peer: JComboBox[A] = new JComboBox(MyComboBox.newConstantModel(items)) with SuperMixin
object selection extends Publisher { def index: Int = peer.getSelectedIndex def index_=(n: Int) { peer.setSelectedIndex(n) } def item: A = peer.getSelectedItem.asInstanceOf[A] def item_=(a: A) { peer.setSelectedItem(a) }
peer.addActionListener(Swing.ActionListener { e =>
publish(event.SelectionChanged(MyComboBox.this))
})
}
/**
- Sets the renderer for this combo box's items. Index -1 is
- passed to the renderer for the selected item (not in the pull-down menu).
- The underlying combo box renders all items in a
ListView
- (both, in the pull-down 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 been
- configured. That's Swing's principle of most suprise. */ def renderer = peer.getRenderer //ListView.Renderer.wrapA def renderer_=(r: ListView.Renderer[A]) { peer.setRenderer(r.peer.asInstanceOf[javax.swing.ListCellRenderer[A]]) }
/* 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
/**
- Makes this combo box editable. In order to do so, this combo needs an
- editor which is supplied by the implicit argument. For default
- editors, see ComboBox companion object. */ def makeEditable()(implicit editor: MyComboBox[A] => MyComboBox.Editor[A]) { peer.setEditable(true) peer.setEditor(editor(this).comboBoxPeer) }
def prototypeDisplayValue: Option[A] = OptionA def prototypeDisplayValue_=(v: Option[A]) { peer.setPrototypeDisplayValue(v.get) } }
I should check in https://github.com/Sciss/SwingPlus/blob/main/src/main/scala/de/sciss/swingplus/ComboBox.scala - since minimum Java version supported is now 8, this would no longer need work around for generics.