Bernard Sufrin
Emeritus: Department of Computer Science
Oxford University
(Revised April 3rd 2025)
(Version 0.9.0)
Glyph is a (prototype) user-interface library for Scala. Its graphical features are geometrically compositional: rendered using a small subset of the facilities of Google's Skia 2-D graphics library, as delivered (in the JVM) by the Skija library.
Our motivation for developing the library was our frustration, over many years, with complex UI frameworks that impose a uniform style, and that make it hard to develop novel modes of interaction. There is nothing wrong with uniform styling: it's the cost of straying outside the styling envelope that we want to diminish.
The belief that has guided this work is that a rich set of interactive interface components can be composed using a small collection of combining forms, together with a suitable collection of elementary visual components -- some reactive. So instead of (just) providing a uniformly-styled “high level” toolkit, we have provided enough elements and combining forms for an innovative UI designer to experiment with, and a collection of implementations of conventional components (for example buttons, checkboxes, text input components, tabbed-notebooks) to serve as models. Its component-styling framework straightforwardly supports the separation of component functionality from component appearance; and although we have not yet done so, we think it would be straightforward for a developer to generalize it into a system of application “skins”.
Glyph has been tested on OS/X
(x86 and Mx processors), Linux
(x86),
and Windows.
A draft introduction to Glyph appears in Glyph-Document/Glyph.pdf
--
which provides a few explained examples of
GUIs (Glyph User Interfaces). Prospective
users may also want to read the source code of the largest
example demonstrationBook
-- which embodies many
useful GUIdioms in its several files.
The repository has all the Skija/Skia/JWM jar files needed to support building and using Glyph. I maintain the code using IntelliJ, and the repository has all the appropriate configuration files for that IDE.
The sbt build.sbt file is also set up with the appropriate Skija/JWM dependencies, and can be used to build the package as a whole, as well as to run individual test applications. The following are available:
[info] * org.sufrin.glyph.GridUtils
[info] * org.sufrin.glyph.glyphXML.AbstractSyntax
[info] * org.sufrin.glyph.osBridge.SwingMain
[info] * org.sufrin.glyph.tests.CalculatorExample
[info] * org.sufrin.glyph.tests.DocumentationDiagrams
[info] * org.sufrin.glyph.tests.Example1
[info] * org.sufrin.glyph.tests.Example2
[info] * org.sufrin.glyph.tests.Example3
[info] * org.sufrin.glyph.tests.Example3a
[info] * org.sufrin.glyph.tests.Example3b
[info] * org.sufrin.glyph.tests.Example3c
[info] * org.sufrin.glyph.tests.Example3d
[info] * org.sufrin.glyph.tests.Example3e
[info] * org.sufrin.glyph.tests.Example3f
[info] * org.sufrin.glyph.tests.Example4
[info] * org.sufrin.glyph.tests.HandleQuit
[info] * org.sufrin.glyph.tests.LargeTest
[info] * org.sufrin.glyph.tests.ResizeAndSplitTest
[info] * org.sufrin.glyph.tests.ResizeableWindowTest
[info] * org.sufrin.glyph.tests.GlyphBook.GlyphBook
[info] * org.sufrin.glyph.tests.glyphXMLTest
You can run any of them with (eg)
sbt "runMain org.sufrin.glyph.tests.Example4"
or from inside sbt in response to the sbt prompt:
sbt:Glyph> runMain org.sufrin.glyph.tests.Example4
But if you don't want to have any truck with an IDE, and if you cannot be bothered to learn anything about sbt (it really is a bother), then you can use the shell script:
Script/compile
to compile the library (to GETTINGSTARTED/Glyph.jar), and the script:
Script/run <test-name> <arguments>
to run any of the tests. If no is given, then
demonstrationBook.Pages
is run.
A moderately experienced Scala programmer should be able to adapt these scripts.
Here's a little example taken from the documentation.
package org.sufrin.glyph
package tests
import NaturalSize.{Col, Row}
import styled.overlaydialogues.Dialogue.OK
import styled._
class Example4Interface(sheet: StyleSheet) {
implicit val style: StyleSheet = sheet
import glyphXML.Language._
val help: Glyph =
<div width="45em" align="justify">
<p>
This app solves the equation <i>c = a + b</i> if at least two of <i>a, b, c</i>
are well-formed numbers: possibly floating-point.
</p>
<p>
Typing <tt>↩</tt> (<i>ie. the enter key</i>) in any of the text fields, causes the
equation to be re-solved.
</p>
</div>
val a, b, c = textField()
val fields = List(a, b, c)
val GUI: Glyph = Col(align=Center)(
help,
Row(align=Mid)(
c.framed(),
Label(" = "),
a.framed(),
Label(" + "),
b.framed()
)
) enlarged 25
def textField(): TextField = TextField(
size = 8,
case s: String if s.toDoubleOption.isDefined => calculemus()
case s: String =>
OK( <p align="justify" width="20em">The text {s"'$s'"} doesn't look much like a num_ber. Enter it again please.</p> ).InFront(help).start()
}
)
def calculemus(): Unit =
(
c.text.toDoubleOption,
a.text.toDoubleOption,
b.text.toDoubleOption
) match {
case (None, Some(av), Some(bv)) => c.text = text(av + bv)
case (Some(cv), Some(av), None) => b.text = text(cv - av)
case (Some(cv), None, Some(bv)) => a.text = text(cv - bv)
case (Some(cv), Some(av), Some(bv)) =>
if (cv == av + bv) {} else c.text = text(av + bv)
case _ =>
}
def text(d: Double): String = f"$d%.5g"
def clear(): Unit = {
for { field <- fields } field.text = ""
}
}
object Example4 extends Application {
val sheet: StyleSheet = StyleSheet()
val GUI: Glyph = new Example4Interface(sheet).GUI
override def title: String = "Example4"
}