Skip to content

Commit

Permalink
Merge branch 'release/1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
lexaay committed Jul 16, 2017
2 parents 9a3739a + 95e27b8 commit e9e2d2d
Show file tree
Hide file tree
Showing 13 changed files with 85 additions and 41 deletions.
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,4 +1,4 @@
Copyright (C) 2015-2016 National University of Singapore
Copyright (C) 2015-2017 National University of Singapore

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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -59,6 +59,6 @@ All code contributions must follow the code style as set out in the [style guide

## License

Copyright © 2015-2016 National University of Singapore
Copyright © 2015-2017 National University of Singapore

Licensed under the GNU General Public License v3. See LICENSE for details.
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -14,7 +14,7 @@ javafx {
mainClass 'org.narrativeandplay.hypedyn.Main'
}

version = "0.26-beta"
version = "1.0"

ext {
majorScalaVersion = "2.11"
Expand Down
19 changes: 19 additions & 0 deletions doc/README.txt
@@ -0,0 +1,19 @@
HypeDyn 2 version 1.0

Installing HypeDyn 2

MacOS: To install HypeDyn, unzip the HypeDyn 2-1.0-MacOS.zip folder, and copy the entire folder to your Applications folder.
Windows: To install HypeDyn, unzip the HypeDyn 2-1.0-Windows.zip folder, and copy the entire folder to somewhere safe, such as C:\Users\yourname\Documents, where "yourname" is your username. Always keep all the files in this folder together.

Running HypeDyn 2

Windows: To run HypeDyn, double-click on HypeDyn 2-1.0-Windows\HypeDyn 2\HypeDyn.exe.
MacOS: To run HypeDyn, double-click on HypeDyn 2-1.0-MacOS\HypeDyn.app.

Tutorials and examples

Tutorials can be found in the tutorials folder, and the accompanying examples can be found in the examples folder. Please visit http://www.narrativeandplay.org/hypedyn for more information on HypeDyn 2.

Bugs

If you encounter any bugs, please report them at https://github.com/narrativeandplay/hypedyn2/issues
Expand Down
3 changes: 2 additions & 1 deletion doc/hypedyn-tutorial-1.tex
@@ -1,6 +1,7 @@
\documentclass{article}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{textcomp}

\begin{document}

Expand Down Expand Up @@ -407,7 +408,7 @@ \subsection{Testing the rule}

\section{Reading the story}

Once you have created and saved your story, you can export your story so that it can be read by other people. Choose \textit{File->Export\ldots} and specify the name of the folder where you want to export your story. HypeDyn will create a folder with that name, and save your story there as a web page. The folder contains a number of supporting files, plus an ``index.html'' file. Keep all these files together.
Once you have created and saved your story, you can export your story so that it can be read by other people. Choose \textit{File\textrightarrow Export\ldots} and specify the name of the folder where you want to export your story. HypeDyn will create a folder with that name, and save your story there as a web page. The folder contains a number of supporting files, plus an ``index.html'' file. Keep all these files together.

To read your exported story, open the ``index.html'' file in a web browser. This is exactly the same as running the story from within HypeDyn.

Expand Down
2 changes: 1 addition & 1 deletion doc/hypedyn-tutorial-4.tex
Expand Up @@ -162,7 +162,7 @@ \subsection{Adding the first rule}
\item Add a condition. Choose ``Number Fact'' as the type of condition in the first pulldown menu.
\item Notice that after changing the condition to a number fact
condition, the condition changes to show three pulldown menus. The first pulldown menu contains a list of number facts. Choose the ``whichEnding?'' fact.
\item The second pulldown menu contains \textit{$<$}, \textit{$>$}, \textit{$\le$}, \textit{$\ge$}, \textit{=} and \textit{not =}. This is the \textit{comparator} which will be used to compare the chosen number fact (in this case ``whichEnding?'') with the right-hand side of the condition.
\item The second pulldown menu contains \textit{\textless}, \textit{\textgreater}, \textit{$\le$}, \textit{$\ge$}, \textit{=} and \textit{not =}. This is the \textit{comparator} which will be used to compare the chosen number fact (in this case ``whichEnding?'') with the right-hand side of the condition.

For now, set the comparator to be ``=''.
\item To the right of the comparator is another pulldown menu, which has two options: input and fact. Choosing ``Input'' lets you enter a specific number for the comparison, whereas choosing ``Fact'' lets you compare against another fact. Set the choice to ``Input''.
Expand Down
2 changes: 1 addition & 1 deletion hypedyn-ui/build.gradle
@@ -1,7 +1,7 @@
dependencies {
compile "org.scalafx:scalafx_${rootProject.majorScalaVersion}:8.0.102-R11"
compile 'com.miglayout:miglayout-javafx:5.0'
compile 'org.fxmisc.richtext:richtextfx:0.6.10'
compile 'org.fxmisc.richtext:richtextfx:0.7-M5'
compile 'org.fxmisc.easybind:easybind:1.0.3'

compile 'com.typesafe.akka:akka-http-experimental_2.11:2.4.4'
Expand Down
Expand Up @@ -142,15 +142,15 @@ object Main extends JFXApp {
graphic = new ImageView(icon)
contentText =
"""Hypertext Fiction Editor
|Version 0.26-beta
|Version 1.0
""".stripMargin
}

def loadedFileName_=(newFilename: String): Unit = loadedFilename() = newFilename
def loadedFileName = loadedFilename()

def runInBrowser(filePath: File, fileToRun: String): Unit = {
val fileToLoad = "http://"+Server.hostname+":"+Server.port+"/"+fileToRun
val fileToLoad = s"${Server.address}/$fileToRun"
Server.storyPath = filePath.getAbsolutePath

hostServices.showDocument(fileToLoad)
Expand Down
Expand Up @@ -55,6 +55,10 @@ class FactEditor private (dialogTitle: String,

initOwner(ownerWindow)

// HACK: make fact editor always on top
// Possibly due to some touch screen issues
dialogPane().scene().window().asInstanceOf[javafx.stage.Stage].alwaysOnTop = true

dialogPane().buttonTypes.addAll(ButtonType.OK, ButtonType.Cancel)
val okButton = dialogPane().lookupButton(ButtonType.OK)

Expand Down
@@ -1,39 +1,43 @@
package org.narrativeandplay.hypedyn.dialogs

import java.io.{DataOutputStream, DataInputStream}
import java.io.{DataInputStream, DataOutputStream}
import java.util
import java.util.function.BiConsumer
import javafx.collections.ObservableList
import javafx.{event => jfxe}
import javafx.event.{ActionEvent => JfxActionEvent, EventHandler}
import javafx.event.{EventHandler, ActionEvent => JfxActionEvent}
import javafx.scene.control.{IndexRange => JfxIndexRange}
import javafx.scene.{input => jfxsi}
import javafx.scene.input.{KeyEvent => JfxKeyEvent}
import javafx.scene.text.{Text, TextFlow}

import scalafx.Includes._
import scalafx.beans.property.ObjectProperty
import scalafx.collections.ObservableBuffer
import scalafx.event.{Event, ActionEvent}
import scalafx.geometry.{Pos, Insets, Orientation}
import scalafx.event.{ActionEvent, Event}
import scalafx.geometry.{Insets, Orientation, Pos}
import scalafx.scene.control._
import scalafx.scene.input.{MouseEvent, KeyEvent}
import scalafx.scene.input.{KeyEvent, MouseEvent}
import scalafx.scene.layout._
import scalafx.stage.{Modality, Window}
import scalafx.scene.Parent.sfxParent2jfx
import scalafx.scene.control.Tab.sfxTab2jfx

import org.fxmisc.easybind.EasyBind
import org.fxmisc.richtext.{Codec, StyleSpan, InlineStyleTextArea}
import org.fxmisc.richtext.model.{Codec, StyleSpan, StyledText}
import org.fxmisc.richtext.StyledTextArea

import org.narrativeandplay.hypedyn.dialogs.NodeEditor.{NodeContentTextArea, LinkStyleInfo}
import org.narrativeandplay.hypedyn.dialogs.NodeEditor.{LinkStyleInfo, NodeContentTextArea}
import org.narrativeandplay.hypedyn.events.UiEventDispatcher
import org.narrativeandplay.hypedyn.story.NodalContent.{RulesetId, TextIndex, RulesetIndexes}
import org.narrativeandplay.hypedyn.story.NodalContent.{RulesetId, RulesetIndexes, TextIndex}
import org.narrativeandplay.hypedyn.story.UiNodeContent.UiRuleset
import org.narrativeandplay.hypedyn.story._
import org.narrativeandplay.hypedyn.story.rules.ActionLocationType.{NodeAction, NodeContentAction}
import org.narrativeandplay.hypedyn.story.rules._
import org.narrativeandplay.hypedyn.story.InterfaceToUiImplementation._
import org.narrativeandplay.hypedyn.uicomponents.RulesPane
import org.narrativeandplay.hypedyn.uicomponents.Sidebar.SidebarButton
import org.narrativeandplay.hypedyn.utils.{ExpandableEmptySpace, CollapsibleSplitPane}
import org.narrativeandplay.hypedyn.utils.{CollapsibleSplitPane, ExpandableEmptySpace}
import org.narrativeandplay.hypedyn.utils.Scala2JavaFunctionConversions._

/**
Expand Down Expand Up @@ -246,7 +250,9 @@ class NodeEditor private (dialogTitle: String,
node onChange { (_, _, newNode) =>
indexUpdateSub.cancel()
replaceText(newNode.content.text)
setStyle(0, text().length, new LinkStyleInfo()) // Clear text styling before applying text styling from rulesets
if (newNode.content.text.length > 0) {
setStyle(0, text().length, new LinkStyleInfo()) // Clear text styling before applying text styling from rulesets
}
newNode.content.rulesetsProperty() foreach { ruleset =>
setStyle(ruleset.indexes.startIndex.index.toInt,
ruleset.indexes.endIndex.index.toInt,
Expand Down Expand Up @@ -537,12 +543,23 @@ object NodeEditor {

override def toString = s"hasRule: ${ruleset.isDefined}"
}
private[this] def linkStyleInfo2Css = { t: LinkStyleInfo => t.css}
private[this] def applyParagraphStyle = new BiConsumer[TextFlow, util.Collection[String]] {
override def accept(paragraph: TextFlow, styleClasses: util.Collection[String]) =
paragraph.getStyleClass.addAll(styleClasses)
}
private[this] def applyStyle = new BiConsumer[Text, LinkStyleInfo] {
override def accept(text: Text, style: LinkStyleInfo) = text.setStyle(style.css)
}

/**
* An extended rich text area to provide some convenience methods
*/
class NodeContentTextArea extends InlineStyleTextArea[LinkStyleInfo](new LinkStyleInfo(), linkStyleInfo2Css) {
class NodeContentTextArea extends StyledTextArea[util.Collection[String], LinkStyleInfo](
util.Collections.emptyList(),
applyParagraphStyle,
new LinkStyleInfo(),
applyStyle
) {
addEventFilter(KeyEvent.KeyTyped, { keyEvent: JfxKeyEvent =>
if (keyEvent.shiftDown && keyEvent.character == " ") {
useInitialStyleForInsertion = true
Expand Down Expand Up @@ -575,16 +592,6 @@ object NodeEditor {

selectRange(ruleRange.start, ruleRange.end)
}
case 2 =>
val range = getSelection
val selectedText = getSelectedText

if (selectedText.startsWith(" ")) {
selectRange(range.getStart + 1, range.getEnd)
}
else if (selectedText.endsWith(" ")) {
selectRange(range.getStart, range.getEnd - 1)
}
case _ =>
}
}
Expand Down Expand Up @@ -626,8 +633,10 @@ object NodeEditor {

def text = textProperty()

def styleCodec = getStyleCodec
def styleCodec_=(codec: Codec[LinkStyleInfo]) = setStyleCodec(codec)


def styleCodec = getStyleCodecs.get()._2
def styleCodec_=(codec: Codec[LinkStyleInfo]) = setStyleCodecs(Codec.collectionCodec(Codec.STRING_CODEC), StyledText.codec(codec))

def onMouseClicked = { me: MouseEvent => getOnMouseClicked.handle(me) }
def onMouseClicked_=[T >: MouseEvent <: Event, U >: jfxsi.MouseEvent <: jfxe.Event](lambda: T => Unit)(implicit jfx2sfx: U => T) = {
Expand Down
Expand Up @@ -34,6 +34,10 @@ class StoryPropertiesDialog(story: Narrative, ownerWindow: Window) extends Dialo

initOwner(ownerWindow)

// HACK: make story properties always on top
// Possibly due to some touch screen issues
dialogPane().scene().window().asInstanceOf[javafx.stage.Stage].alwaysOnTop = true

val metadata: UiStoryMetadata = story.metadata

dialogPane().buttonTypes.addAll(ButtonType.OK, ButtonType.Cancel)
Expand Down
Expand Up @@ -10,8 +10,8 @@ import org.narrativeandplay.hypedyn.logging.Logger
object Server {
private var _storyPath = ""

val hostname = "localhost"
val port = 8080
private val hostname = "localhost"
private var port = -1 // -1 represents an uninitialised port

implicit val webserver = ActorSystem("hypedyn")
implicit val materializer = ActorMaterializer()
Expand All @@ -26,16 +26,18 @@ object Server {
}
}

private val bindingFuture = Http().bindAndHandle(route, hostname, port)
// We bind to port 0 to let Akka randomly pick an available port to bind to
private val bindingFuture = Http().bindAndHandle(route, hostname, 0)

bindingFuture.onFailure {
case ex: Exception =>
Logger.error("Server failed to bind to "+hostname+":"+port, ex)
Logger.error("Server failed to start: ", ex)
}

bindingFuture.onSuccess {
case _: Http.ServerBinding =>
Logger.info("Server online at http://"+hostname+":"+port)
case binding =>
port = binding.localAddress.getPort
Logger.info(s"Server online at $address")
}

def shutdown(): Unit = {
Expand All @@ -44,4 +46,6 @@ object Server {

def storyPath = _storyPath
def storyPath_=(s: String) = _storyPath = s

def address = s"http://$hostname:$port"
}
@@ -1,6 +1,7 @@
package org.narrativeandplay.hypedyn.uicomponents

import java.lang
import javafx.beans.binding.BooleanExpression

import scalafx.Includes._
import scalafx.application.Platform
Expand All @@ -20,6 +21,8 @@ import org.narrativeandplay.hypedyn.utils.{HypedynPreferences, System}
* Menu bar for the application
*/
class Menubar(mainStageFocused: ReadOnlyBooleanProperty) extends MenuBar {
private val noNodeSelected = BooleanExpression.booleanExpression(EasyBind monadic UiEventDispatcher.selectedNode map[lang.Boolean] (_.isEmpty))

useSystemMenuBar = true
menus.addAll(fileMenu, editMenu, helpMenu)

Expand Down Expand Up @@ -152,7 +155,7 @@ class Menubar(mainStageFocused: ReadOnlyBooleanProperty) extends MenuBar {
private lazy val cut = new MenuItem("Cut") {
accelerator = KeyCombinations.Cut

disable <== EasyBind monadic UiEventDispatcher.selectedNode map[lang.Boolean] (_.isEmpty)
disable <== noNodeSelected || !mainStageFocused

onAction = { _ =>
UiEventDispatcher.requestCut()
Expand All @@ -162,7 +165,7 @@ class Menubar(mainStageFocused: ReadOnlyBooleanProperty) extends MenuBar {
private lazy val copy = new MenuItem("Copy") {
accelerator = KeyCombinations.Copy

disable <== EasyBind monadic UiEventDispatcher.selectedNode map[lang.Boolean] (_.isEmpty)
disable <== noNodeSelected || !mainStageFocused

onAction = { _ =>
UiEventDispatcher.requestCopy()
Expand All @@ -172,7 +175,7 @@ class Menubar(mainStageFocused: ReadOnlyBooleanProperty) extends MenuBar {
private lazy val paste = new MenuItem("Paste") {
accelerator = KeyCombinations.Paste

disable <== EasyBind monadic UiEventDispatcher.selectedNode map[lang.Boolean] (_.isEmpty)
disable <== noNodeSelected || !mainStageFocused

onAction = { _ =>
UiEventDispatcher.requestPaste()
Expand Down

0 comments on commit e9e2d2d

Please sign in to comment.