{-# OPTIONS_GHC -fno-warn-unused-imports #-}

module Diagrams.Backend.HsQML.Tutorial (
-- * DiagramCanvas QML script
-- $diagramcanvas

-- * Context object
-- $contextobject

-- * Main window
-- $mainwindow

-- * Rendering diagrams
-- $rendering
) where

import Graphics.QML
import Diagrams.Backend.HsQML
import Diagrams.Backend.HsQML.DiagramObj.Type

{- $diagramcanvas
To use this backend you first need a custom Canvas object to handle signals coming from Haskell.
Example script to place in your QML search path (can be next to your main QML document):

> -- DiagramCanvas.qml
>import QtQuick 2.4
>
>Canvas {
>  id: canvas;
>  property variant diagram;
>  renderStrategy: Canvas.Threaded;
>  renderTarget: Canvas.FramebufferObject;
>
>  onDiagramChanged: {
>    var context = canvas.getContext('2d');
>    if(canvas.diagram && context) {
>    console.log("reconnect");
>    canvas.diagram.save.connect(function() {canvas.context.save()})
>    canvas.diagram.restore.connect(function () {canvas.context.restore()});
>    canvas.diagram.text.connect(function (text,x,y) {canvas.context.strokeText(text,x,y)});
>    canvas.diagram.beginPath.connect(function () {canvas.context.beginPath()});
>    canvas.diagram.closePath.connect(function () {canvas.context.closePath()});
>    canvas.diagram.stroke.connect(function () {canvas.context.stroke()});
>    canvas.diagram.fill.connect(function() {canvas.context.fill()});
>    canvas.diagram.moveTo.connect(function (x,y) {canvas.context.moveTo(x,y)});
>    canvas.diagram.lineTo.connect(function (x,y) {canvas.context.lineTo(x,y)});
>    canvas.diagram.bezierCurveTo.connect(function(cp1x,cp1y,cp2x,cp2y,x,y) 
>                                           {canvas.context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)});
>    canvas.diagram.connectLinearGradient.connect(connectLG);
>    canvas.diagram.connectRadialGradient.connect(connectRG);
>
>    canvas.diagram.setStrokeColour.connect(function(r,g,b,a) 
>                                            {canvas.context.strokeStyle = Qt.rgba(r,g,b,a).toString();});
>    canvas.diagram.setFillColour.connect(function(r,g,b,a) 
>                                            {canvas.context.fillStyle = Qt.rgba(r,g,b,a)});
>    canvas.diagram.setFont.connect(function(f) {canvas.context.font = f});
>    canvas.diagram.setLineWidth.connect(function(w) {canvas.context.lineWidth = w});
>    canvas.diagram.oddEvenFill.connect(function() {canvas.context.fillRule = Qt.OddEvenFill});
>    canvas.diagram.windingFill.connect(function() {canvas.context.fillRule = Qt.WindingFill});
>    } else {
>     console.log("warning! no diagram or context object to connect");
>    }
>  }
>
>  onPaint: {
>    if(canvas.diagram) {
>      canvas.diagram.reload();
>    }
>  }
>
>  function connectLG(gradient, x0, y0, x1, y1) {
>    var grad = canvas.context.createLinearGradient(x0, y0, x1, y1);
>    gradient.addStop.connect( function(r,g,b,a, off) {grad.addColorStop(off, Qt.rgba(r,g,b,a))});
>    canvas.diagram.setLineGradient.connect(function() {canvas.context.strokeStyle = grad;});
>    canvas.diagram.setFillGradient.connect(function() {canvas.context.fillStyle = grad;});
>
>  }
>
>  function connectRG(gradient, x0, y0, r0, x1, y1, r1) {
>    var grad = canvas.context.createRadialGradient(x0, y0, r0, x1, y1, r1);
>    gradient.addStop.connect(function(r,g,b,a, off) {grad.addColorStop(off, Qt.rgba(r,g,b,a))});
>    canvas.diagram.setLineGradient.connect(function() {canvas.context.strokeStyle = grad;});
>    canvas.diagram.setFillGradient.connect(function() {canvas.context.fillStyle = grad;});
>  }
>}
-}

{- $contextobject
You can make an 'ObjRef' to a 'DiagramObj' available to the QML script by placing it in a property of your context object:

> -- Main.hs
> module Main where
> import Control.Applicative
> import Control.Concurrent
> import Control.Concurrent.MVar
> import Data.Typeable
> import Diagrams.Prelude
> import Diagrams.Backend.HsQML
> import Graphics.QML
>
>data Repaint deriving Typeable
>
>repaint :: Proxy Repaint
>repaint = Proxy
>
>instance SignalKeyClass Repaint where
>  type SignalParams Repaint = IO ()
>
>data MainObj = MainObj 
>    { diagramObj :: MVar (ObjRef (DiagramObj ()))
>    }
>  deriving Typeable
>
>instance DefaultClass MainObj where
>  classMembers = 
>    [ defPropertySigRO' "mainDiagram" diagramChanged $ \this -> 
>       case fromObjRef this of
>          MainObj mvar -> readMVar mvar
>    , defPropertyConst' "self" return
>    , defSignal "repaint" repaint
>    ]
-}

{- $mainwindow
Then, place a DiagramCanvas somewhere in your application, and connect it to the controller object:

> -- main.qml
>import QtQuick 2.0
>import QtQuick.Controls 1.3
>import QtQuick.Window 2.2
>
>ApplicationWindow {
>  title: "test";
>  width: 800;
>  height: 800;
>  visible: true;
>
>  toolBar: ToolBar {
>    ToolButton {
>      onClicked: canvas.repaint();
>    }
>  }
>
>  DiagramCanvas {
>    id: canvas;
>    anchors.fill: parent;
>  
>    function repaint() {
>       canvas.diagram = self.mainDiagram;
>       canvas.paint(canvas.canvasWindow);
>    }
>
>    Connections {
>      target: self;
>      onRepaint: canvas.repaint();
>    }
>  }
>
>}
-}

{- $rendering
The 'renderHsQML' function creates an 'ObjRef' to the controller object.

> -- Main.hs
>diagram :: Diagram B R2
>diagram = circle 1
>
>main :: IO ()
>main = do
>  diag <- renderHsQML (mkSizeSpec (Just 800) (Just 800)) diagram
>  mainObj <- MainObj <$> newMVar diag
>  ctx <- newObjectDC mainObj
>  runEngineLoop defaultEngineConfig
>     { initialDocument = fileDocument "qml/main.qml"
>     , contextObject = Just $ anyObjRef ctx
>     }
-}