scala-swing icon indicating copy to clipboard operation
scala-swing copied to clipboard

scala.swing.ComboBox should have a method for changing the items

Open scabug opened this issue 15 years ago • 8 comments

Frequently, the items in the combobox are dynamic and not known at the time of the ComboBox's instantiation.

scabug avatar Jul 12 '09 19:07 scabug

Imported From: https://issues.scala-lang.org/browse/SI-2154?orig=1 Reporter: David Corbin (dcorbin)

scabug avatar Jul 12 '09 19:07 scabug

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

scabug avatar Nov 21 '11 15:11 scabug

@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)?

scabug avatar Nov 21 '11 15:11 scabug

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")))

scabug avatar Nov 21 '11 16:11 scabug

mathieu leclaire (mamat) said: Hi, did you plan to fix this bug in the next version ? Thanks Mathieu

scabug avatar Mar 02 '12 11:03 scabug

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.

scabug avatar Mar 06 '12 08:03 scabug

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) } }

scabug avatar Mar 15 '12 15:03 scabug

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.

Sciss avatar Apr 02 '21 12:04 Sciss