more robust selection: avoid duplicates via "batch" number;
authorwenzelm
Wed, 09 Nov 2022 21:14:20 +0100
changeset 76494 9686049ce988
parent 76493 2dfc8885c0ee
child 76500 1cebb0ca6d86
more robust selection: avoid duplicates via "batch" number; evade attempts to select separator via keyboard or mouse (non-keyboard), assuming there are no consecutive separators;
src/Pure/GUI/gui.scala
src/Tools/jEdit/src/document_dockable.scala
src/Tools/jEdit/src/jedit_sessions.scala
--- a/src/Pure/GUI/gui.scala	Wed Nov 09 20:30:52 2022 +0100
+++ b/src/Pure/GUI/gui.scala	Wed Nov 09 21:14:20 2022 +0100
@@ -9,10 +9,11 @@
 import java.util.{Map => JMap}
 import java.awt.{Component, Container, Font, Image, Insets, KeyboardFocusManager, Window, Point,
   Rectangle, Dimension, GraphicsEnvironment, MouseInfo, Toolkit}
+import java.awt.event.{KeyAdapter, KeyEvent, ItemListener, ItemEvent}
 import java.awt.font.{FontRenderContext, LineMetrics, TextAttribute, TransformAttribute}
 import java.awt.geom.AffineTransform
 import javax.swing.{ImageIcon, JButton, JDialog, JFrame, JLabel, JLayeredPane, JOptionPane,
-  JTextField, JWindow, LookAndFeel, UIManager, SwingUtilities}
+  JTextField, JWindow, JComboBox, LookAndFeel, UIManager, SwingUtilities}
 
 import scala.swing.{CheckBox, ComboBox, ScrollPane, TextArea, ListView, Label, Separator,
   Orientation}
@@ -136,25 +137,54 @@
     sealed abstract class Entry[A] {
       def get_item: Option[A] =
         this match {
-          case Item(item, _) => Some(item)
+          case item: Item[_] => Some(item.item)
           case _ => None
         }
     }
-    case class Item[A](item: A, description: String = "") extends Entry[A] {
+    case class Item[A](item: A, description: String = "", batch: Int = 0) extends Entry[A] {
       override def toString: String = proper_string(description) getOrElse item.toString
     }
-    case class Separator[A](name: String = "") extends Entry[A] {
+    case class Separator[A](name: String = "", batch: Int = 0) extends Entry[A] {
       override def toString: String = "<b>" + name + "</b>"
     }
 
-    def item(name: String): Item[String] = Item(name)
-    def separator: Separator[String] = Separator()
+    def item(name: String, batch: Int = 0): Item[String] = Item(name, batch = batch)
+    def separator(batch: Int = 0): Separator[String] = Separator(batch = batch)
   }
 
   class Selector[A](val entries: List[Selector.Entry[A]])
   extends ComboBox[Selector.Entry[A]](entries) {
     def changed(): Unit = {}
 
+    override lazy val peer: JComboBox[Selector.Entry[A]] =
+      new JComboBox[Selector.Entry[A]](ComboBox.newConstantModel(entries)) with SuperMixin {
+        private var key_released = false
+        private var sep_selected = false
+
+        addKeyListener(new KeyAdapter {
+          override def keyPressed(e: KeyEvent): Unit = { key_released = false }
+          override def keyReleased(e: KeyEvent): Unit = { key_released = true }
+        })
+
+        override def setSelectedIndex(i: Int): Unit = {
+          getItemAt(i) match {
+            case _: Selector.Separator[_] =>
+              if (key_released) { sep_selected = true }
+              else {
+                val k = getSelectedIndex()
+                val j = if (i > k) i + 1 else i - 1
+                if (0 <= j && j < dataModel.getSize()) super.setSelectedIndex(j)
+              }
+            case _ => super.setSelectedIndex(i)
+          }
+        }
+
+        override def setPopupVisible(visible: Boolean): Unit = {
+          if (sep_selected) { sep_selected = false}
+          else super.setPopupVisible(visible)
+        }
+      }
+
     private val default_renderer = renderer
     private val render_separator = new Separator
     private val render_label = new Label
@@ -179,7 +209,7 @@
 
   class Zoom extends Selector[String](
     List("50%", "70%", "85%", "100%", "125%", "150%", "175%", "200%", "300%", "400%")
-      .map(GUI.Selector.item)
+      .map(GUI.Selector.item(_))
   ) {
     def factor: Int = parse(selection.item.toString)
 
--- a/src/Tools/jEdit/src/document_dockable.scala	Wed Nov 09 20:30:52 2022 +0100
+++ b/src/Tools/jEdit/src/document_dockable.scala	Wed Nov 09 21:14:20 2022 +0100
@@ -192,8 +192,8 @@
     val all_sessions = sessions.build_topological_order.sorted
     val doc_sessions = all_sessions.filter(name => sessions(name).doc_group)
     new GUI.Selector(
-      doc_sessions.map(GUI.Selector.item) ::: List(GUI.Selector.separator) :::
-      all_sessions.map(GUI.Selector.item)
+      doc_sessions.map(GUI.Selector.item(_)) ::: List(GUI.Selector.separator()) :::
+      all_sessions.map(GUI.Selector.item(_, batch = 1))
     ) {
       val title = "Session"
     }
--- a/src/Tools/jEdit/src/jedit_sessions.scala	Wed Nov 09 20:30:52 2022 +0100
+++ b/src/Tools/jEdit/src/jedit_sessions.scala	Wed Nov 09 21:14:20 2022 +0100
@@ -80,8 +80,8 @@
       val all_sessions = sessions.imports_topological_order
       val main_sessions = all_sessions.filter(name => sessions(name).main_group)
 
-      main_sessions.map(GUI.Selector.item) ::: List(GUI.Selector.separator) :::
-      all_sessions.sorted.map(GUI.Selector.item)
+      main_sessions.map(GUI.Selector.item(_)) ::: List(GUI.Selector.separator()) :::
+      all_sessions.sorted.map(GUI.Selector.item(_, batch = 1))
     }
 
     new GUI.Selector(default_entry :: session_entries) with JEdit_Options.Entry {
@@ -90,7 +90,10 @@
       val title = "Logic"
       def load(): Unit = {
         val logic = options.string(jedit_logic_option)
-        entries.find { case GUI.Selector.Item(item, _) => item == logic case _ => false } match {
+        entries.find {
+          case item: GUI.Selector.Item[_] => item.item == logic
+          case _ => false
+        } match {
           case Some(entry) => selection.item = entry
           case None =>
         }