scalismo.ui.view.dialog.AboutDialog.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2016 University of Basel, Graphics and Vision Research Group
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package scalismo.ui.view.dialog
import java.awt.event.{MouseAdapter, MouseEvent}
import java.awt.{Color, Cursor, Font}
import java.net.URI
import javax.swing._
import scalismo.ui.resources.icons.BundledIcon
import scalismo.ui.resources.thirdparty.ThirdPartyResource
import scalismo.ui.view.ScalismoFrame
import scalismo.ui.view.dialog.AboutDialog._
import scalismo.ui.view.dialog.AboutDialog.scaled._
import scalismo.ui.view.util.{LinkLabel, MultiLineLabel, ScalableUI}
import scala.swing.GridBagPanel.{Anchor, Fill}
import scala.swing.Swing.EmptyIcon
import scala.swing.TabbedPane.Page
import scala.swing.{Action, _}
import scala.util.Try
object AboutDialog {
/**
* This is essentially a workaround for IntelliJ Idea not liking
* auto-generated sources. (I.e. Idea continues to bitch about
* not finding scalismo.ui.BuildInfo, thus making it impossible
* to compile or start the program from the UI).
*
* Using runtime reflection avoids this.
*/
object BuildInfo {
import scala.reflect.runtime.universe
private lazy val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader)
private lazy val objectName = "scalismo.ui.BuildInfo$"
def proxy(fieldName: String): String =
Try {
val moduleSymbol = runtimeMirror.moduleSymbol(Class.forName(objectName))
val targetMethod = moduleSymbol.typeSignature.members
.filter(x => x.isMethod && x.name.toString == fieldName)
.head
.asMethod
runtimeMirror
.reflect(runtimeMirror.reflectModule(moduleSymbol).instance)
.reflectMethod(targetMethod)()
.toString
}.getOrElse("???")
// proxied fields
def version: String = proxy("version")
def scalaVersion: String = proxy("scalaVersion")
def sbtVersion: String = proxy("sbtVersion")
def buildTime: String = proxy("buildTime")
}
object scaled {
import ScalableUI.implicits.scalableInt
/* scaled versions of pixel sizes.
* Defined separately (instead of just using a base, and a multiplier / divisor)
* because they're mostly small values, and the scale factor is generally also a small value
* (between 1 and 2) -- so (for example) 3.scaled * 2 is not necessarily equal to (3 * 2).scaled .
*
* Defined as methods in subpackage to avoid code duplication (in other words: all components should use one of
* the values defined here, not a literal value)
*/
def s_3: Int = 3.scaled
def s_5: Int = 5.scaled
def s_10: Int = 10.scaled
def s_15: Int = 15.scaled
def s_20: Int = 20.scaled
def s_128: Int = 128.scaled
}
class BoldLabel(text: String, icon: Icon = EmptyIcon, alignment: Alignment.Value = Alignment.Center)
extends Label(text, icon, alignment) {
font = font.deriveFont(font.getStyle | Font.BOLD)
}
class LogoPanel extends BorderPanel {
private val logo = BundledIcon.Logo
private val dim = s_128
private val scaledIcon = ScalableUI.resizeIcon(logo, dim, dim)
private val image = new LinkLabel("",
new URI("https://github.com/unibas-gravis/scalismo"),
scaledIcon,
Alignment.Left,
preventLinkStyle = true,
preventTooltip = true) {
peer.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR))
}
private val h = s_15
image.border = BorderFactory.createEmptyBorder(h * 2, h, h * 2, h)
layout(image) = BorderPanel.Position.North
}
class KeyValuePanel(pairs: List[(String, String)]) extends GridBagPanel {
private var x = 0
private var y = 0
val columns = 2
private def constraints(text: String): Constraints = new Constraints {
this.gridx = x
this.gridy = y
this.anchor = Anchor.NorthWest
this.ipadx = s_20
this.ipady = s_3
if (text == null) {
// special "Fill" entry: fill vertical space
this.weighty = 1
}
// update state for next add()
x += 1
if (x == columns) {
x = 0
y += 1
}
}
pairs.foreach {
case (key, value) =>
add(new BoldLabel(key), constraints(key))
add(new Label(value), constraints(value))
}
}
class ThirdPartyPanel(frame: ScalismoFrame) extends BorderPanel {
val description =
"The scalismo library, and the user interface, use a number of third-party open source resources. These dependencies are listed below."
private val descriptionBorder = BorderFactory.createEmptyBorder(s_10, 0, s_10, 0)
private val thanksBorder = BorderFactory.createEmptyBorder(s_10, 0, 0, 0)
private val listBorder = BorderFactory.createEmptyBorder(s_3, s_10, s_3, s_10)
private val scrollBorder = BorderFactory.createEmptyBorder(0, 0, 0, s_10)
private val north = new MultiLineLabel(description) {
border = descriptionBorder
}
val center: BorderPanel = new BorderPanel {
private val list = new ThirdPartyListPanel(frame) {
border = listBorder
}
private val scroll = new ScrollPane(list) {
horizontalScrollBarPolicy = ScrollPane.BarPolicy.AsNeeded
verticalScrollBarPolicy = ScrollPane.BarPolicy.AsNeeded
}
scroll.border = BorderFactory.createCompoundBorder(scrollBorder, scroll.border)
layout(scroll) = BorderPanel.Position.Center
}
layout(north) = BorderPanel.Position.North
layout(center) = BorderPanel.Position.Center
// very swiss, but it doesn't harm, and I bet it will be appreciated.
layout(new BoldLabel("Thank you!", alignment = Alignment.Left) {
border = thanksBorder
}) = BorderPanel.Position.South
}
class ThirdPartyListPanel(frame: ScalismoFrame) extends GridBagPanel {
private var x = 0
private var y = 0
val columns = List("Name", "Author(s)", "License")
private val lastColumnIndex = columns.length - 1
private val lastRowIndex = ThirdPartyResource.All.length + 1 // accounts for header and footer
private def constraints(): Constraints = new Constraints {
this.gridx = x
this.gridy = y
this.anchor = Anchor.NorthWest
this.ipadx = s_20
this.ipady = s_3
this.weightx = x match {
case c if c == lastColumnIndex => 1
case _ => 0
}
this.weighty = y match {
case r if r == lastRowIndex => 1
case _ => 0
}
this.fill = (x, y) match {
case (h, v) if h == lastColumnIndex && v == lastRowIndex => Fill.Both
case (h, _) if h == lastColumnIndex => Fill.Horizontal
case (_, v) if v == lastRowIndex => Fill.Vertical
case (_, _) => Fill.None
}
// update state for next add()
x += 1
if (x == columns.length) {
x = 0
y += 1
}
}
def add(label: Label): Unit = {
label.horizontalAlignment = Alignment.Left
add(label, constraints())
}
// table headers
columns.foreach(c => add(new BoldLabel(c)))
ThirdPartyResource.All.foreach { tp =>
val name = tp.homepage match {
case Some(url) => new LinkLabel(tp.name, new URI(url))
case None => new Label(tp.name)
}
val author = new Label(tp.authors)
val license = tp.licenseText match {
case Some(licenseText) =>
new Label(tp.licenseName) {
tooltip = "Show License"
foreground = Color.BLUE.darker()
peer.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR))
peer.addMouseListener(new MouseAdapter {
override def mouseClicked(e: MouseEvent): Unit = {
popupLicense(frame, tp.name, licenseText)
}
})
}
case None => new Label(tp.licenseName)
}
add(name)
add(author)
add(license)
}
// table footer (dummy row to fill space on resize)
(0 to lastColumnIndex).foreach { _ =>
add(new Label(""))
}
}
def popupLicense(frame: ScalismoFrame, productName: String, licenseText: String): Unit = {
val dialog = new Dialog(owner = frame) {
modal = true
title = s"$productName - License"
}
val main = new BorderPanel
val button = new BorderPanel {
layout(new Button(new Action("OK") {
override def apply(): Unit = dialog.dispose()
})) = BorderPanel.Position.East
}
val _rows = 25
val _columns = 80
val textComponent = {
if (licenseText.startsWith("")) {
// set the same preferred width as a 25x80 text area
val prefWidth = new TextArea("", _rows, _columns).preferredSize.width
val pane = new JTextPane() {
setContentType("text/html")
setText(licenseText)
setEditable(false)
override def getPreferredSize: Dimension = {
val real = super.getPreferredSize
real.width = prefWidth
real
}
}
Component.wrap(pane)
} else {
new TextArea(licenseText) {
rows = _rows
columns = _columns
editable = false
lineWrap = true
peer.setWrapStyleWord(true)
}
}
}
val scroll = new ScrollPane(textComponent)
main.layout(scroll) = BorderPanel.Position.Center
main.layout(button) = BorderPanel.Position.South
dialog.contents = main
dialog.pack()
dialog.centerOnScreen()
dialog.visible = true
}
}
class AboutDialog(implicit frame: ScalismoFrame) extends Dialog(frame) {
modal = true
title = "About Scalismo UI"
private def withLogo(component: Component): BorderPanel = new BorderPanel {
layout(component) = BorderPanel.Position.Center
layout(new LogoPanel) = BorderPanel.Position.West
}
val main: BorderPanel = new BorderPanel {
val keyValue = List(
"Developers" -> "Ghazi Bouabene
Thomas Gerig
Christoph Langguth
Marcel Lüthi",
(null, null), // special: fill
"UI Version" -> BuildInfo.version,
"Scalismo Version" -> scalismo.BuildInfo.version,
"Scala Version" -> BuildInfo.scalaVersion,
"Build Time" -> BuildInfo.buildTime
)
private val kvPanel: BorderPanel = new BorderPanel {
val west: BorderPanel = new BorderPanel {
layout(new KeyValuePanel(keyValue)) = BorderPanel.Position.Center
}
layout(west) = BorderPanel.Position.West
layout(
new LinkLabel("Copyright (c) Graphics and Vision Research Group, University of Basel",
new URI("http://gravis.cs.unibas.ch/"),
alignment = Alignment.Left,
preventLinkStyle = true,
preventTooltip = true) {
private val b = s_10
border = BorderFactory.createEmptyBorder(b, b, b, b)
}
) = BorderPanel.Position.North
}
val tabsPane = new TabbedPane
// we explicitly use "null" for tooltips, as the default scala.swing implementation uses "", which displays a useless tooltip
tabsPane.pages += new Page("Scalismo UI", withLogo(kvPanel), null)
tabsPane.pages += new Page("Third Party", withLogo(new ThirdPartyPanel(frame)), null)
val bottomPanel: BorderPanel = new BorderPanel {
val ok = new Button(new Action("OK") {
override def apply(): Unit = AboutDialog.this.dispose()
})
layout(new BorderPanel {
layout(ok) = BorderPanel.Position.Center
private val b = s_5
border = BorderFactory.createEmptyBorder(b, b, b, b)
}) = BorderPanel.Position.East
}
layout(tabsPane) = BorderPanel.Position.Center
layout(bottomPanel) = BorderPanel.Position.South
border = BorderFactory.createEmptyBorder(s_5, 0, 0, 0)
}
contents = main
pack()
centerOnScreen()
visible = true
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy