| author | wenzelm | 
| Wed, 17 May 2017 23:05:30 +0200 | |
| changeset 65860 | ce6be2e40d47 | 
| parent 65370 | 1324268c2f6a | 
| child 67835 | c8e4ee2b5482 | 
| permissions | -rw-r--r-- | 
| 53783 
f5e9d182f645
clarified location of GUI modules (which depend on Swing of JFX);
 wenzelm parents: 
53778diff
changeset | 1 | /* Title: Pure/GUI/gui.scala | 
| 51619 | 2 | Author: Makarius | 
| 3 | ||
| 53712 | 4 | Basic GUI tools (for AWT/Swing). | 
| 51619 | 5 | */ | 
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 54962 | 9 | import java.lang.{ClassLoader, ClassNotFoundException, NoSuchMethodException}
 | 
| 57908 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 10 | import java.io.{FileInputStream, BufferedInputStream}
 | 
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 11 | import java.awt.{GraphicsEnvironment, Image, Component, Container, Toolkit, Window, Font,
 | 
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 12 | KeyboardFocusManager} | 
| 53785 | 13 | import java.awt.font.{TextAttribute, TransformAttribute, FontRenderContext, LineMetrics}
 | 
| 14 | import java.awt.geom.AffineTransform | |
| 57879 
91e188508bc9
proper layered_pane for JDialog, e.g. relevant for floating dockables in jEdit, for completion popup in text field;
 wenzelm parents: 
57639diff
changeset | 15 | import javax.swing.{ImageIcon, JOptionPane, UIManager, JLayeredPane, JFrame, JWindow, JDialog,
 | 
| 56874 | 16 | JButton, JTextField} | 
| 51619 | 17 | |
| 18 | import scala.swing.{ComboBox, TextArea, ScrollPane}
 | |
| 19 | import scala.swing.event.SelectionChanged | |
| 20 | ||
| 21 | ||
| 22 | object GUI | |
| 23 | {
 | |
| 24 | /* Swing look-and-feel */ | |
| 25 | ||
| 59201 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 26 | def find_laf(name: String): Option[String] = | 
| 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 27 | UIManager.getInstalledLookAndFeels(). | 
| 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 28 | find(c => c.getName == name || c.getClassName == name). | 
| 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 29 | map(_.getClassName) | 
| 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 30 | |
| 51619 | 31 | def get_laf(): String = | 
| 59201 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 32 |     find_laf(System.getProperty("isabelle.laf")) getOrElse {
 | 
| 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 33 | if (Platform.is_windows || Platform.is_macos) | 
| 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 34 | UIManager.getSystemLookAndFeelClassName() | 
| 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 35 | else | 
| 61529 
82fc5a6231a2
back to traditional Metal as default, and thus evade current problems with Nimbus scrollbar slider;
 wenzelm parents: 
61523diff
changeset | 36 | UIManager.getCrossPlatformLookAndFeelClassName() | 
| 59201 
702e0971d617
added system property isabelle.laf, notably for initial system dialog;
 wenzelm parents: 
59183diff
changeset | 37 | } | 
| 51619 | 38 | |
| 39 | def init_laf(): Unit = UIManager.setLookAndFeel(get_laf()) | |
| 40 | ||
| 53848 
8d7029eb0c31
disable standard behaviour of Mac OS X text field (i.e. select-all after focus gain) in order to make completion work more smoothly;
 wenzelm parents: 
53786diff
changeset | 41 | def is_macos_laf(): Boolean = | 
| 53849 | 42 | Platform.is_macos && | 
| 53848 
8d7029eb0c31
disable standard behaviour of Mac OS X text field (i.e. select-all after focus gain) in order to make completion work more smoothly;
 wenzelm parents: 
53786diff
changeset | 43 | UIManager.getSystemLookAndFeelClassName() == UIManager.getLookAndFeel.getClass.getName | 
| 
8d7029eb0c31
disable standard behaviour of Mac OS X text field (i.e. select-all after focus gain) in order to make completion work more smoothly;
 wenzelm parents: 
53786diff
changeset | 44 | |
| 60998 | 45 | def is_windows_laf(): Boolean = | 
| 46 | Platform.is_windows && | |
| 47 | UIManager.getSystemLookAndFeelClassName() == UIManager.getLookAndFeel.getClass.getName | |
| 48 | ||
| 51619 | 49 | |
| 56752 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 50 | /* plain focus traversal, notably for text fields */ | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 51 | |
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 52 | def plain_focus_traversal(component: Component) | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 53 |   {
 | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 54 | val dummy_button = new JButton | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 55 | def apply(id: Int): Unit = | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 56 | component.setFocusTraversalKeys(id, dummy_button.getFocusTraversalKeys(id)) | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 57 | apply(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS) | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 58 | apply(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS) | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 59 | } | 
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 60 | |
| 
72b4205f4de9
uniform focus traversal via TAB / Shift-TAB for all fields, in contrast to Java defaults, but in accordance to occasional jEdit practice;
 wenzelm parents: 
56622diff
changeset | 61 | |
| 54962 | 62 | /* X11 window manager */ | 
| 63 | ||
| 64 | def window_manager(): Option[String] = | |
| 65 |   {
 | |
| 54965 | 66 | if (Platform.is_windows || Platform.is_macos) None | 
| 67 | else | |
| 68 |       try {
 | |
| 59080 
611914621edb
added Untyped.method convenience (for *this* class only);
 wenzelm parents: 
57908diff
changeset | 69 | val wm = | 
| 
611914621edb
added Untyped.method convenience (for *this* class only);
 wenzelm parents: 
57908diff
changeset | 70 |           Untyped.method(Class.forName("sun.awt.X11.XWM", true, ClassLoader.getSystemClassLoader),
 | 
| 
611914621edb
added Untyped.method convenience (for *this* class only);
 wenzelm parents: 
57908diff
changeset | 71 | "getWM").invoke(null) | 
| 
611914621edb
added Untyped.method convenience (for *this* class only);
 wenzelm parents: 
57908diff
changeset | 72 | if (wm == null) None | 
| 
611914621edb
added Untyped.method convenience (for *this* class only);
 wenzelm parents: 
57908diff
changeset | 73 | else Some(wm.toString) | 
| 54962 | 74 | } | 
| 54965 | 75 |       catch {
 | 
| 76 | case _: ClassNotFoundException => None | |
| 77 | case _: NoSuchMethodException => None | |
| 78 | } | |
| 54962 | 79 | } | 
| 80 | ||
| 81 | ||
| 51619 | 82 | /* simple dialogs */ | 
| 83 | ||
| 59671 
9715eb8e9408
more precise position information in Isabelle/Scala, with YXML markup as in Isabelle/ML;
 wenzelm parents: 
59286diff
changeset | 84 | def scrollable_text(raw_txt: String, width: Int = 60, height: Int = 20, editable: Boolean = false) | 
| 53714 | 85 | : ScrollPane = | 
| 51619 | 86 |   {
 | 
| 59671 
9715eb8e9408
more precise position information in Isabelle/Scala, with YXML markup as in Isabelle/ML;
 wenzelm parents: 
59286diff
changeset | 87 | val txt = Output.clean_yxml(raw_txt) | 
| 51619 | 88 | val text = new TextArea(txt) | 
| 89 | if (width > 0) text.columns = width | |
| 53714 | 90 | if (height > 0 && split_lines(txt).length > height) text.rows = height | 
| 51619 | 91 | text.editable = editable | 
| 92 | new ScrollPane(text) | |
| 93 | } | |
| 94 | ||
| 95 | private def simple_dialog(kind: Int, default_title: String, | |
| 65370 | 96 | parent: Component, title: String, message: Iterable[Any]) | 
| 51619 | 97 |   {
 | 
| 57612 
990ffb84489b
clarified module name: facilitate alternative GUI frameworks;
 wenzelm parents: 
57044diff
changeset | 98 |     GUI_Thread.now {
 | 
| 65370 | 99 | val java_message = | 
| 100 |         message.iterator.map({ case x: scala.swing.Component => x.peer case x => x }).
 | |
| 101 | toArray.asInstanceOf[Array[AnyRef]] | |
| 102 | JOptionPane.showMessageDialog(parent, java_message, | |
| 51619 | 103 | if (title == null) default_title else title, kind) | 
| 104 | } | |
| 105 | } | |
| 106 | ||
| 57639 | 107 | def dialog(parent: Component, title: String, message: Any*): Unit = | 
| 51619 | 108 | simple_dialog(JOptionPane.PLAIN_MESSAGE, null, parent, title, message) | 
| 109 | ||
| 57639 | 110 | def warning_dialog(parent: Component, title: String, message: Any*): Unit = | 
| 51619 | 111 | simple_dialog(JOptionPane.WARNING_MESSAGE, "Warning", parent, title, message) | 
| 112 | ||
| 57639 | 113 | def error_dialog(parent: Component, title: String, message: Any*): Unit = | 
| 51619 | 114 | simple_dialog(JOptionPane.ERROR_MESSAGE, "Error", parent, title, message) | 
| 115 | ||
| 116 | def confirm_dialog(parent: Component, title: String, option_type: Int, message: Any*): Int = | |
| 57612 
990ffb84489b
clarified module name: facilitate alternative GUI frameworks;
 wenzelm parents: 
57044diff
changeset | 117 |     GUI_Thread.now {
 | 
| 51619 | 118 |       val java_message = message map { case x: scala.swing.Component => x.peer case x => x }
 | 
| 119 | JOptionPane.showConfirmDialog(parent, | |
| 120 | java_message.toArray.asInstanceOf[Array[AnyRef]], title, | |
| 121 | option_type, JOptionPane.QUESTION_MESSAGE) | |
| 122 | } | |
| 123 | ||
| 124 | ||
| 125 | /* zoom box */ | |
| 126 | ||
| 57044 | 127 | private val Zoom_Factor = "([0-9]+)%?".r | 
| 128 | ||
| 129 | abstract class Zoom_Box extends ComboBox[String]( | |
| 51619 | 130 |     List("50%", "70%", "85%", "100%", "125%", "150%", "175%", "200%", "300%", "400%"))
 | 
| 131 |   {
 | |
| 57044 | 132 | def changed: Unit | 
| 133 | def factor: Int = parse(selection.item) | |
| 134 | ||
| 135 | private def parse(text: String): Int = | |
| 51619 | 136 |       text match {
 | 
| 57044 | 137 | case Zoom_Factor(s) => | 
| 51619 | 138 | val i = Integer.parseInt(s) | 
| 56874 | 139 | if (10 <= i && i < 1000) i else 100 | 
| 51619 | 140 | case _ => 100 | 
| 141 | } | |
| 142 | ||
| 57044 | 143 | private def print(i: Int): String = i.toString + "%" | 
| 51619 | 144 | |
| 145 |     def set_item(i: Int) {
 | |
| 146 |       peer.getEditor match {
 | |
| 147 | case null => | |
| 148 | case editor => editor.setItem(print(i)) | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | makeEditable()(c => new ComboBox.BuiltInEditor(c)(text => print(parse(text)), x => x)) | |
| 56874 | 153 |     peer.getEditor.getEditorComponent match {
 | 
| 56888 | 154 | case text: JTextField => text.setColumns(4) | 
| 56874 | 155 | case _ => | 
| 156 | } | |
| 157 | ||
| 57044 | 158 | selection.index = 3 | 
| 159 | ||
| 51619 | 160 | listenTo(selection) | 
| 57044 | 161 |     reactions += { case SelectionChanged(_) => changed }
 | 
| 51619 | 162 | } | 
| 163 | ||
| 164 | ||
| 53786 | 165 | /* tooltip with multi-line support */ | 
| 166 | ||
| 56622 
891d1b8b64fb
clarified tooltip_lines: HTML.encode already takes care of newline (but not space);
 wenzelm parents: 
54965diff
changeset | 167 | def tooltip_lines(text: String): String = | 
| 
891d1b8b64fb
clarified tooltip_lines: HTML.encode already takes care of newline (but not space);
 wenzelm parents: 
54965diff
changeset | 168 | if (text == null || text == "") null | 
| 62113 | 169 | else "<html>" + HTML.output(text) + "</html>" | 
| 53786 | 170 | |
| 171 | ||
| 51619 | 172 | /* icon */ | 
| 173 | ||
| 174 | def isabelle_icon(): ImageIcon = | |
| 54676 
6b2ca4850b71
uniform use of transparent icons, as for main "apps";
 wenzelm parents: 
54659diff
changeset | 175 |     new ImageIcon(getClass.getClassLoader.getResource("isabelle/isabelle_transparent-32.gif"))
 | 
| 51619 | 176 | |
| 54709 | 177 | def isabelle_icons(): List[ImageIcon] = | 
| 178 |     for (icon <- List("isabelle/isabelle_transparent-32.gif", "isabelle/isabelle_transparent.gif"))
 | |
| 179 | yield new ImageIcon(getClass.getClassLoader.getResource(icon)) | |
| 180 | ||
| 51619 | 181 | def isabelle_image(): Image = isabelle_icon().getImage | 
| 53712 | 182 | |
| 183 | ||
| 184 | /* component hierachy */ | |
| 185 | ||
| 186 | def get_parent(component: Component): Option[Container] = | |
| 187 |     component.getParent match {
 | |
| 188 | case null => None | |
| 189 | case parent => Some(parent) | |
| 190 | } | |
| 191 | ||
| 192 |   def ancestors(component: Component): Iterator[Container] = new Iterator[Container] {
 | |
| 193 | private var next_elem = get_parent(component) | |
| 194 | def hasNext(): Boolean = next_elem.isDefined | |
| 195 | def next(): Container = | |
| 196 |       next_elem match {
 | |
| 197 | case Some(parent) => | |
| 198 | next_elem = get_parent(parent) | |
| 199 | parent | |
| 200 | case None => Iterator.empty.next() | |
| 201 | } | |
| 202 | } | |
| 203 | ||
| 204 | def parent_window(component: Component): Option[Window] = | |
| 205 |     ancestors(component).collectFirst({ case x: Window => x })
 | |
| 53778 
29eaacff4078
proper layered pane at root of parent component, not global view (e.g. relevant for tooltips for detached info windows);
 wenzelm parents: 
53714diff
changeset | 206 | |
| 
29eaacff4078
proper layered pane at root of parent component, not global view (e.g. relevant for tooltips for detached info windows);
 wenzelm parents: 
53714diff
changeset | 207 | def layered_pane(component: Component): Option[JLayeredPane] = | 
| 
29eaacff4078
proper layered pane at root of parent component, not global view (e.g. relevant for tooltips for detached info windows);
 wenzelm parents: 
53714diff
changeset | 208 |     parent_window(component) match {
 | 
| 57879 
91e188508bc9
proper layered_pane for JDialog, e.g. relevant for floating dockables in jEdit, for completion popup in text field;
 wenzelm parents: 
57639diff
changeset | 209 | case Some(w: JWindow) => Some(w.getLayeredPane) | 
| 
91e188508bc9
proper layered_pane for JDialog, e.g. relevant for floating dockables in jEdit, for completion popup in text field;
 wenzelm parents: 
57639diff
changeset | 210 | case Some(w: JFrame) => Some(w.getLayeredPane) | 
| 
91e188508bc9
proper layered_pane for JDialog, e.g. relevant for floating dockables in jEdit, for completion popup in text field;
 wenzelm parents: 
57639diff
changeset | 211 | case Some(w: JDialog) => Some(w.getLayeredPane) | 
| 53778 
29eaacff4078
proper layered pane at root of parent component, not global view (e.g. relevant for tooltips for detached info windows);
 wenzelm parents: 
53714diff
changeset | 212 | case _ => None | 
| 
29eaacff4078
proper layered pane at root of parent component, not global view (e.g. relevant for tooltips for detached info windows);
 wenzelm parents: 
53714diff
changeset | 213 | } | 
| 53785 | 214 | |
| 63874 | 215 | def traverse_components(component: Component, apply: Component => Unit) | 
| 216 |   {
 | |
| 217 | def traverse(comp: Component) | |
| 218 |     {
 | |
| 219 | apply(comp) | |
| 220 |       comp match {
 | |
| 221 | case cont: Container => | |
| 222 | for (i <- 0 until cont.getComponentCount) | |
| 223 | traverse(cont.getComponent(i)) | |
| 224 | case _ => | |
| 225 | } | |
| 226 | } | |
| 227 | traverse(component) | |
| 228 | } | |
| 229 | ||
| 53785 | 230 | |
| 231 | /* font operations */ | |
| 232 | ||
| 61742 
fd3b214b0979
clarified font: GUI defaults might change dynamically;
 wenzelm parents: 
61529diff
changeset | 233 | def copy_font(font: Font): Font = | 
| 
fd3b214b0979
clarified font: GUI defaults might change dynamically;
 wenzelm parents: 
61529diff
changeset | 234 | if (font == null) null | 
| 
fd3b214b0979
clarified font: GUI defaults might change dynamically;
 wenzelm parents: 
61529diff
changeset | 235 | else new Font(font.getFamily, font.getStyle, font.getSize) | 
| 
fd3b214b0979
clarified font: GUI defaults might change dynamically;
 wenzelm parents: 
61529diff
changeset | 236 | |
| 59230 | 237 | def line_metrics(font: Font): LineMetrics = | 
| 53785 | 238 |     font.getLineMetrics("", new FontRenderContext(null, false, false))
 | 
| 239 | ||
| 59286 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59230diff
changeset | 240 | def imitate_font(font: Font, family: String, scale: Double = 1.0): Font = | 
| 53785 | 241 |   {
 | 
| 242 | val font1 = new Font(family, font.getStyle, font.getSize) | |
| 59286 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59230diff
changeset | 243 | val rel_size = line_metrics(font).getHeight.toDouble / line_metrics(font1).getHeight | 
| 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59230diff
changeset | 244 | new Font(family, font.getStyle, (scale * rel_size * font.getSize).toInt) | 
| 59183 
ec83638b6bfb
imitate font more carefully: err on smaller size;
 wenzelm parents: 
59080diff
changeset | 245 | } | 
| 
ec83638b6bfb
imitate font more carefully: err on smaller size;
 wenzelm parents: 
59080diff
changeset | 246 | |
| 59286 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59230diff
changeset | 247 | def imitate_font_css(font: Font, family: String, scale: Double = 1.0): String = | 
| 59183 
ec83638b6bfb
imitate font more carefully: err on smaller size;
 wenzelm parents: 
59080diff
changeset | 248 |   {
 | 
| 
ec83638b6bfb
imitate font more carefully: err on smaller size;
 wenzelm parents: 
59080diff
changeset | 249 | val font1 = new Font(family, font.getStyle, font.getSize) | 
| 59230 | 250 | val rel_size = line_metrics(font).getHeight.toDouble / line_metrics(font1).getHeight | 
| 59183 
ec83638b6bfb
imitate font more carefully: err on smaller size;
 wenzelm parents: 
59080diff
changeset | 251 | "font-family: " + family + "; font-size: " + (scale * rel_size * 100).toInt + "%;" | 
| 53785 | 252 | } | 
| 253 | ||
| 254 | def transform_font(font: Font, transform: AffineTransform): Font = | |
| 255 |   {
 | |
| 256 | import scala.collection.JavaConversions._ | |
| 257 | font.deriveFont(Map(TextAttribute.TRANSFORM -> new TransformAttribute(transform))) | |
| 258 | } | |
| 57908 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 259 | |
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 260 | def font(family: String = "IsabelleText", size: Int = 1, bold: Boolean = false): Font = | 
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 261 | new Font(family, if (bold) Font.BOLD else Font.PLAIN, size) | 
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 262 | |
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 263 | def install_fonts() | 
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 264 |   {
 | 
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 265 | val ge = GraphicsEnvironment.getLocalGraphicsEnvironment() | 
| 65083 | 266 | for (font <- Isabelle_System.fonts()) | 
| 57908 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 267 | ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, font.file)) | 
| 
1937603dbdf2
separate Java FX modules -- no need to include jfxrt.jar by default;
 wenzelm parents: 
57879diff
changeset | 268 | } | 
| 51619 | 269 | } | 
| 270 |