clarified GUI.Selector, with support for separator as pseudo-entry;
authorwenzelm
Wed, 09 Nov 2022 19:42:21 +0100
changeset 76492 e228be7cd375
parent 76491 2c37c10d6884
child 76493 2dfc8885c0ee
clarified GUI.Selector, with support for separator as pseudo-entry; clarified signature;
src/Pure/GUI/gui.scala
src/Pure/Thy/sessions.scala
src/Pure/Tools/spell_checker.scala
src/Tools/jEdit/src/document_dockable.scala
src/Tools/jEdit/src/jedit_sessions.scala
src/Tools/jEdit/src/jedit_spell_checker.scala
src/Tools/jEdit/src/monitor_dockable.scala
--- a/src/Pure/GUI/gui.scala	Wed Nov 09 14:20:52 2022 +0100
+++ b/src/Pure/GUI/gui.scala	Wed Nov 09 19:42:21 2022 +0100
@@ -13,7 +13,9 @@
 import java.awt.geom.AffineTransform
 import javax.swing.{ImageIcon, JButton, JDialog, JFrame, JLabel, JLayeredPane, JOptionPane,
   JTextField, JWindow, LookAndFeel, UIManager, SwingUtilities}
-import scala.swing.{CheckBox, ComboBox, ScrollPane, TextArea}
+
+import scala.swing.{CheckBox, ComboBox, ScrollPane, TextArea, ListView, Label, Separator,
+  Orientation}
 import scala.swing.event.{ButtonClicked, SelectionChanged}
 
 
@@ -127,9 +129,45 @@
     reactions += { case ButtonClicked(_) => clicked(selected); clicked() }
   }
 
-  class Selector[A](val entries: List[A]) extends ComboBox[A](entries) {
+
+  /* list selector */
+
+  object Selector {
+    sealed abstract class Entry[A] {
+      def get_item: Option[A] =
+        this match {
+          case Item(item, _) => Some(item)
+          case _ => None
+        }
+    }
+    case class Item[A](item: A, description: String = "") extends Entry[A] {
+      override def toString: String = proper_string(description) getOrElse item.toString
+    }
+    case class Separator[A](name: String = "") extends Entry[A] {
+      override def toString: String = "<b>" + name + "</b>"
+    }
+
+    def item(name: String): Item[String] = Item(name)
+    def separator: Separator[String] = Separator()
+  }
+
+  class Selector[A](val entries: List[Selector.Entry[A]])
+  extends ComboBox[Selector.Entry[A]](entries) {
     def changed(): Unit = {}
 
+    private val default_renderer = renderer
+    private val render_separator = new Separator
+    private val render_label = new Label
+    renderer =
+      (list: ListView[_ <: Selector.Entry[A]], selected: Boolean, focus: Boolean, entry: Selector.Entry[A], i: Int) => {
+        entry match {
+          case sep: Selector.Separator[_] =>
+            if (sep.name.isEmpty) render_separator
+            else { render_label.text = entry.toString; render_label }
+          case _ => default_renderer.componentFor(list, selected, focus, entry, i)
+        }
+      }
+
     listenTo(selection)
     reactions += { case SelectionChanged(_) => changed() }
   }
@@ -141,8 +179,9 @@
 
   class Zoom extends Selector[String](
     List("50%", "70%", "85%", "100%", "125%", "150%", "175%", "200%", "300%", "400%")
+      .map(GUI.Selector.item)
   ) {
-    def factor: Int = parse(selection.item)
+    def factor: Int = parse(selection.item.toString)
 
     private def parse(text: String): Int =
       text match {
@@ -161,7 +200,8 @@
       }
     }
 
-    makeEditable()(c => new ComboBox.BuiltInEditor(c)(text => print(parse(text)), x => x))
+    makeEditable()(c =>
+      new ComboBox.BuiltInEditor(c)(text => Selector.Item(print(parse(text))), _.toString))
     peer.getEditor.getEditorComponent match {
       case text: JTextField => text.setColumns(4)
       case _ =>
--- a/src/Pure/Thy/sessions.scala	Wed Nov 09 14:20:52 2022 +0100
+++ b/src/Pure/Thy/sessions.scala	Wed Nov 09 19:42:21 2022 +0100
@@ -507,6 +507,9 @@
 
     def dirs: List[Path] = dir :: directories
 
+    def main_group: Boolean = groups.contains("main")
+    def doc_group: Boolean = groups.contains("doc")
+
     def timeout_ignored: Boolean =
       !options.bool("timeout_build") || Time.seconds(options.real("timeout")) < Time.ms(1)
 
--- a/src/Pure/Tools/spell_checker.scala	Wed Nov 09 14:20:52 2022 +0100
+++ b/src/Pure/Tools/spell_checker.scala	Wed Nov 09 19:42:21 2022 +0100
@@ -85,12 +85,14 @@
     }
   }
 
-  def dictionaries: List[Dictionary] =
+  lazy val dictionaries: List[Dictionary] =
     for {
       path <- Path.split(Isabelle_System.getenv("JORTHO_DICTIONARIES"))
       if path.is_file
     } yield new Dictionary(path)
 
+  def get_dictionary(lang: String): Option[Dictionary] = dictionaries.find(_.lang == lang)
+
 
   /* create spell checker */
 
@@ -260,7 +262,7 @@
     if (options.bool("spell_checker")) {
       val lang = options.string("spell_checker_dictionary")
       if (current_spell_checker._1 != lang) {
-        Spell_Checker.dictionaries.find(_.lang == lang) match {
+        Spell_Checker.get_dictionary(lang) match {
           case Some(dictionary) =>
             val spell_checker =
               Exn.capture { Spell_Checker(dictionary) } match {
--- a/src/Tools/jEdit/src/document_dockable.scala	Wed Nov 09 14:20:52 2022 +0100
+++ b/src/Tools/jEdit/src/document_dockable.scala	Wed Nov 09 19:42:21 2022 +0100
@@ -187,10 +187,17 @@
 
   /* controls */
 
-  private val document_session: GUI.Selector[String] =
-    new GUI.Selector(JEdit_Sessions.sessions_structure().build_topological_order.sorted) {
+  private val document_session: GUI.Selector[String] = {
+    val sessions = JEdit_Sessions.sessions_structure()
+    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)
+    ) {
       val title = "Session"
     }
+  }
 
   private val build_button =
     new GUI.Button("<html><b>Build</b></html>") {
--- a/src/Tools/jEdit/src/jedit_sessions.scala	Wed Nov 09 14:20:52 2022 +0100
+++ b/src/Tools/jEdit/src/jedit_sessions.scala	Wed Nov 09 19:42:21 2022 +0100
@@ -69,35 +69,35 @@
 
   /* logic selector */
 
-  private sealed case class Logic_Entry(name: String = "", description: String = "") {
-    override def toString: String = proper_string(description) getOrElse name
-  }
-
   def logic_selector(options: Options_Variable, autosave: Boolean = false): JEdit_Options.Entry = {
     GUI_Thread.require {}
 
-    val default_entry = Logic_Entry(description = "default (" + logic_name(options.value) + ")")
+    val default_entry =
+      GUI.Selector.Item("", description = "default (" + logic_name(options.value) + ")")
 
     val session_entries = {
       val sessions = sessions_structure(options = options.value)
-      val (main_sessions, other_sessions) =
-        sessions.imports_topological_order.partition(name => sessions(name).groups.contains("main"))
-      (main_sessions.sorted ::: other_sessions.sorted).map(name => Logic_Entry(name = name))
+      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.map(GUI.Selector.item)
     }
 
-    new GUI.Selector[Logic_Entry](default_entry :: session_entries)
-    with JEdit_Options.Entry {
+    new GUI.Selector(default_entry :: session_entries) with JEdit_Options.Entry {
       name = jedit_logic_option
       tooltip = "Logic session name (change requires restart)"
       val title = "Logic"
       def load(): Unit = {
         val logic = options.string(jedit_logic_option)
-        entries.find(_.name == logic) match {
+        entries.find { case GUI.Selector.Item(item, _) => item == logic case _ => false } match {
           case Some(entry) => selection.item = entry
           case None =>
         }
       }
-      def save(): Unit = options.string(jedit_logic_option) = selection.item.name
+      def save(): Unit =
+        for (item <- selection.item.get_item) options.string(jedit_logic_option) = item
+
       override def changed(): Unit = if (autosave) save()
 
       load()
--- a/src/Tools/jEdit/src/jedit_spell_checker.scala	Wed Nov 09 14:20:52 2022 +0100
+++ b/src/Tools/jEdit/src/jedit_spell_checker.scala	Wed Nov 09 19:42:21 2022 +0100
@@ -85,18 +85,19 @@
     val option_name = "spell_checker_dictionary"
     val opt = PIDE.options.value.check_name(option_name)
 
-    new GUI.Selector[Spell_Checker.Dictionary](Spell_Checker.dictionaries) with JEdit_Options.Entry {
+    new GUI.Selector(Spell_Checker.dictionaries.map(GUI.Selector.Item(_))) with JEdit_Options.Entry {
       name = option_name
       tooltip = GUI.tooltip_lines(opt.print_default)
       val title = opt.title()
       def load(): Unit = {
         val lang = PIDE.options.string(option_name)
-        entries.find(_.lang == lang) match {
-          case Some(entry) => selection.item = entry
+        Spell_Checker.get_dictionary(lang) match {
+          case Some(dict) => selection.item = GUI.Selector.Item(dict)
           case None =>
         }
       }
-      def save(): Unit = PIDE.options.string(option_name) = selection.item.lang
+      def save(): Unit =
+        for (item <- selection.item.get_item) PIDE.options.string(option_name) = item.lang
 
       load()
     }
--- a/src/Tools/jEdit/src/monitor_dockable.scala	Wed Nov 09 14:20:52 2022 +0100
+++ b/src/Tools/jEdit/src/monitor_dockable.scala	Wed Nov 09 19:42:21 2022 +0100
@@ -64,10 +64,11 @@
 
   /* controls */
 
-  private val select_data = new GUI.Selector[String](ML_Statistics.all_fields.map(_._1)) {
-    tooltip = "Select visualized data collection"
-    override def changed(): Unit = { data_name = selection.item; update_chart() }
-  }
+  private val select_data =
+    new GUI.Selector(ML_Statistics.all_fields.map { case (a, _) => GUI.Selector.item(a) }) {
+      tooltip = "Select visualized data collection"
+      override def changed(): Unit = { data_name = selection.item.toString; update_chart() }
+    }
 
   private val limit_data = new TextField("200", 5) {
     tooltip = "Limit for accumulated data"