Commit 42bdff67 authored by Mathieu leclaire's avatar Mathieu leclaire

Rely on Scaladget JsRxTags

parent 96ce80cc
...@@ -26,7 +26,7 @@ import scalatags.JsDom.svgAttrs ...@@ -26,7 +26,7 @@ import scalatags.JsDom.svgAttrs
import scalatags.JsDom.svgTags import scalatags.JsDom.svgTags
import scaladget.stylesheet.all._ import scaladget.stylesheet.all._
import scaladget.api.svg._ import scaladget.api.svg._
import client.JsRxTags._ import scaladget.tools.JsRxTags._
import org.scalajs.dom.raw._ import org.scalajs.dom.raw._
trait Selectable { trait Selectable {
...@@ -42,10 +42,12 @@ object Graph { ...@@ -42,10 +42,12 @@ object Graph {
val target: Var[Task]) extends Selectable val target: Var[Task]) extends Selectable
def task(title: String, x: Double, y: Double) = Task(Var(title), Var((x, y))) def task(title: String, x: Double, y: Double) = Task(Var(title), Var((x, y)))
def edge(source: Task, target: Task) = new Edge(Var(source), Var(target)) def edge(source: Task, target: Task) = new Edge(Var(source), Var(target))
} }
import Graph._ import Graph._
class Window(nodes: Seq[Task] = Seq(), edges: Seq[Edge] = Seq()) { class Window(nodes: Seq[Task] = Seq(), edges: Seq[Edge] = Seq()) {
val svgNode = { val svgNode = {
...@@ -75,10 +77,9 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) { ...@@ -75,10 +77,9 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) {
val DELETE_KEY = 46 val DELETE_KEY = 46
val NODE_RADIUS = 50 val NODE_RADIUS = 50
val END_ARROW: String = "end-arrow" val END_ARROW: String = "end-arrow"
val URL_END_ARROW: String = "url(#end-arrow)" val URL_END_ARROW: String = s"url(#${END_ARROW})"
val MARK_END_ARROW: String = "mark-end-arrow" val MARK_END_ARROW: String = "mark-end-arrow"
val URL_MARK_END_ARROW: String = "url(#mark-end-arrow)" val URL_MARK_END_ARROW: String = s"url(#${MARK_END_ARROW})"
class DragLine { class DragLine {
private val m: Var[(Int, Int)] = Var((0, 0)) private val m: Var[(Int, Int)] = Var((0, 0))
...@@ -97,10 +98,10 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) { ...@@ -97,10 +98,10 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) {
this this
} }
val render = Rx { val render: SVGElement = Rx {
path(ms = ms(s"$LINK_DRAGLINE ${ path(ms = ms(s"$LINK_DRAGLINE ${
if (dragging()) "" else HIDDEN if (dragging()) "" else HIDDEN
}")).m(m()._1, m()._2).l(l()._1, l()._2)(svgAttrs.markerEnd := URL_MARK_END_ARROW).render.render }")).m(m()._1, m()._2).l(l()._1, l()._2)(svgAttrs.markerEnd := URL_MARK_END_ARROW).render
} }
} }
...@@ -141,7 +142,7 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) { ...@@ -141,7 +142,7 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) {
// Hide the drag line // Hide the drag line
if (me.shiftKey && !dragLine.dragging.now) { if (me.shiftKey && !dragLine.dragging.now) {
val (x, y) = (me.clientX, me.clientY) val (x, y) = (me.clientX, me.clientY)
addTask(task(UUID.randomUUID().toString, x, y)) addTask(task(UUID.randomUUID().toString, x, y))
} }
mouseDownTask() = None mouseDownTask() = None
dragLine.dragging() = false dragLine.dragging() = false
...@@ -177,22 +178,18 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) { ...@@ -177,22 +178,18 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) {
} }
def circle(task: Task) = { def circle(task: Task) = {
val gCircle: SVGElement = { val element: SVGElement = Rx {
svgTags.g( svgTags.g(
rxSVGMod( ms(CIRCLE + {
Rx { if (task.selected()) s" $SELECTED" else ""
svgTags.g( })
ms(CIRCLE + { )(
if (task.selected()) s" $SELECTED" else "" svgAttrs.transform := s"translate(${
}) val location = task.location()
)( s"${location._1}, ${location._2}"
svgAttrs.transform := s"translate(${ })")(svgTags.circle(svgAttrs.r := NODE_RADIUS).render)
val location = task.location() }
s"${location._1}, ${location._2}" val gCircle = svgTags.g(element).render
})")(svgTags.circle(svgAttrs.r := NODE_RADIUS).render)
}
))
}.render
gCircle.onmousedown = (me: MouseEvent) => { gCircle.onmousedown = (me: MouseEvent) => {
mouseDownTask() = Some(task) mouseDownTask() = Some(task)
...@@ -217,30 +214,30 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) { ...@@ -217,30 +214,30 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) {
addEdge(edge(e.source.now, e.target.now)) addEdge(edge(e.source.now, e.target.now))
} }
def link(edge: Edge) = def link(edge: Edge) = {
svgTags.g( val sVGElement: SVGElement = Rx {
rxSVGMod(Rx { val p = path(ms = (if (edge.selected()) ms(SELECTED) else emptyMod) +++
val p = path(ms = (if (edge.selected()) ms("selected") else emptyMod) +++ ms(LINK)).m(
ms(LINK)).m( edge.source().location()._1.toInt,
edge.source().location()._1.toInt, edge.source().location()._2.toInt
edge.source().location()._2.toInt ).l(
).l( edge.target().location()._1.toInt,
edge.target().location()._1.toInt, edge.target().location()._2.toInt
edge.target().location()._2.toInt ).render(svgAttrs.markerEnd := URL_END_ARROW).render
).render(svgAttrs.markerEnd := URL_END_ARROW).render
p.onmousedown = (me: MouseEvent) => {
p.onmousedown = (me: MouseEvent) => { unselectTasks
unselectTasks unselectEdges
unselectEdges edge.selected() = !edge.selected.now
edge.selected() = !edge.selected.now
}
svgTags.g(p)
} }
) p
).render }
svgTags.g(sVGElement).render
}
def addToScene[T](s: Var[Seq[Var[T]]], draw: T=> SVGElement) = svgG.appendChild(svgTags.g( def addToScene[T](s: Var[Seq[Var[T]]], draw: T => SVGElement) = {
rxSVGMod(Rx { val element: SVGElement = Rx {
svgTags.g( svgTags.g(
for { for {
t <- s() t <- s()
...@@ -248,8 +245,9 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) { ...@@ -248,8 +245,9 @@ class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) {
draw(t.now) draw(t.now)
} }
) )
}).render }
).render) svgG.appendChild(svgTags.g(element).render).render
}
addToScene(edges, link) addToScene(edges, link)
addToScene(tasks, circle) addToScene(tasks, circle)
......
/*
* Copyright (C) 21/07/14 mathieu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero 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 <http://www.gnu.org/licenses/>.
*/
package client
import org.scalajs.dom.raw.{HTMLDivElement, HTMLElement, Node, SVGElement}
import scalatags.JsDom._
import scala.util.{Failure, Success}
import all._
import rx._
import org.scalajs.dom.Element
import scaladget.stylesheet.all._
/**
* A minimal binding between Scala.Rx and Scalatags and Scala-Js-Dom
*/
object JsRxTags {
implicit val ctx: Ctx.Owner = Ctx.Owner.safe()
/**
* Wraps reactive strings in spans, so they can be referenced/replaced
* when the Rx changes.
*/
implicit def RxStr[T](r: Rx[T])(implicit f: T Modifier): Modifier = {
rxHTMLMod(Rx(span(r())))
}
// implicit def RxModifierSeq(r: TypedTag[Rx[ModifierSeq]]): TypedTag[ModifierSeq] = {
// r.copy(modifiers = r.modifiers)
// }
/**
* Sticks some Rx into a Scalatags fragment, which means hooking up an Obs
* to propagate changes into the DOM via the element's ID. Monkey-patches
* the Obs onto the element itself so we have a reference to kill it when
* the element leaves the DOM (e.g. it gets deleted).
*/
implicit def rxHTMLMod[T <: HtmlTag](r: Rx[T]): Modifier = bindNode(rxHTMLNode(r))
// implicit def rxHTMLTagedType[T <: HtmlTag](r: Rx[T]): Modifier = bindNode(rxHTMLNode(r))
implicit def rxHTMLNode[T <: TypedTag[HTMLElement]](r: Rx[T]): HTMLElement = {
def rSafe = r.toTry match {
case Success(v) v.render
case Failure(e) span(e.toString, backgroundColor := "red").render
}
var last = rSafe
r.triggerLater {
val newLast = rSafe
last.parentNode.replaceChild(newLast, last)
last = newLast
}
last
}
/**
* Idem for SVG elements
*/
implicit def rxSVGMod[T <: TypedTag[SVGElement]](r: Rx[T]): SVGElement = {
def rSafe = r.now.render//r.toTry match {
// case Success(v) ⇒ v.render
// case Failure(e) ⇒ span(e.toString, backgroundColor := "red").render
// }
var last = rSafe
r.triggerLater {
val newLast = rSafe
last.parentNode.replaceChild(newLast, last)
last = newLast
}
last
}
implicit def RxAttrValue[T: scalatags.JsDom.AttrValue] = new scalatags.JsDom.AttrValue[Rx.Dynamic[T]] {
def apply(t: Element, a: Attr, r: Rx.Dynamic[T]): Unit = {
r.trigger {
implicitly[scalatags.JsDom.AttrValue[T]].apply(t, a, r.now)
}
}
}
implicit def RxStyleValue[T: scalatags.JsDom.StyleValue] = new scalatags.JsDom.StyleValue[Rx.Dynamic[T]] {
def apply(t: Element, s: Style, r: Rx.Dynamic[T]): Unit = {
r.trigger {
implicitly[scalatags.JsDom.StyleValue[T]].apply(t, s, r.now)
}
}
}
// Convenient implicit conversions
def rxIf[T](dynamic: Rx.Dynamic[Boolean], yes: T, no: T) = {
rx.Rx {
if (dynamic()) yes
else no
}
}
def rxIf[T](dynamic: Var[Boolean], yes: T, no: T) = {
rx.Rx {
if (dynamic()) yes
else no
}
}
implicit def rxNode(r: Rx[Node]): Node = {
def oo = {
def rSafe = r.now
var last = rSafe
r.triggerLater {
val newLast = rSafe
last.parentNode.replaceChild(newLast, last)
last = newLast
}
last
}
bindNode(oo).render
}
// implicit val ctx: Ctx.Owner = Ctx.Owner.safe()
//
// /**
// * Wraps reactive strings in spans, so they can be referenced/replaced
// * when the Rx changes.
// */
// implicit def RxStr[T](r: Rx[T])(implicit f: T ⇒ Modifier): Modifier = {
// rxHTMLMod(Rx(span(r())))
// }
//
// implicit def rxNode(r: Rx[Node]): Node = {
//
// def oo = {
// def rSafe = r.now
//
// var last = rSafe
//
// r.triggerLater {
// val newLast = rSafe
// last.parentNode.replaceChild(newLast, last)
// last = newLast
// }
// last
// }
//
// bindNode(oo).render
// }
// /**
// * Sticks some Rx into a Scalatags fragment, which means hooking up an Obs
// * to propagate changes into the DOM via the element's ID. Monkey-patches
// * the Obs onto the element itself so we have a reference to kill it when
// * the element leaves the DOM (e.g. it gets deleted).
// */
// implicit def rxHTMLMod[T <: HtmlTag](r: Rx[T]): Modifier = bindNode(rxHTMLNode(r))
//
// // implicit def rxHTMLTagedType[T <: HtmlTag](r: Rx[T]): Modifier = bindNode(rxHTMLNode(r))
//
// implicit def rxHTMLNode[T <: HtmlTag](r: Rx[T]): Node = {
// def rSafe = r.toTry match {
// case Success(v) ⇒ v.render
// case Failure(e) ⇒ span(e.toString, backgroundColor := "red").render
// }
// var last = rSafe
// r.triggerLater {
// val newLast = rSafe
// last.parentNode.replaceChild(newLast, last)
// last = newLast
// }
// last
// }
//
// /**
// * Idem for SVG elements
// */
// implicit def rxSVGMod[T <: TypedTag[SVGElement]](r: Rx[T]): Modifier = {
// def rSafe = r.toTry match {
// case Success(v) ⇒ v.render
// case Failure(e) ⇒ span(e.toString, backgroundColor := "red").render
// }
// var last = rSafe
// r.triggerLater {
// val newLast = rSafe
// last.parentNode.replaceChild(newLast, last)
// last = newLast
// }
// last
// }
//
// implicit def RxAttrValue[T: scalatags.JsDom.AttrValue] = new scalatags.JsDom.AttrValue[Rx.Dynamic[T]] {
// def apply(t: Element, a: Attr, r: Rx.Dynamic[T]): Unit = {
// r.trigger {
// implicitly[scalatags.JsDom.AttrValue[T]].apply(t, a, r.now)
// }
// }
// }
//
// implicit def RxStyleValue[T: scalatags.JsDom.StyleValue] = new scalatags.JsDom.StyleValue[Rx.Dynamic[T]] {
// def apply(t: Element, s: Style, r: Rx.Dynamic[T]): Unit = {
// r.trigger {
// implicitly[scalatags.JsDom.StyleValue[T]].apply(t, s, r.now)
// }
// }
// }
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment