Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Commit

Permalink
feat: added a way to detect double clicks on nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
prescientmoon committed Jul 12, 2020
1 parent 613cc4a commit d58ead7
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 52 deletions.
7 changes: 6 additions & 1 deletion src/Component/Editor/Scene.purs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import Effect.Class (class MonadEffect, liftEffect)
import Effect.Unsafe (unsafePerformEffect)
import Halogen (Component, HalogenM, RefLabel(..), defaultEval, gets, mkComponent, mkEval, modify_, raise, subscribe)
import Halogen.HTML as HH
import Halogen.HTML.Events (onMouseDown, onMouseMove, onMouseUp)
import Halogen.HTML.Events (onDoubleClick, onMouseDown, onMouseMove, onMouseUp)
import Halogen.HTML.Properties as HP
import Halogen.Query.EventSource as ES
import Lunarbox.Component.Utils (className)
import Lunarbox.Config (Config)
import Lunarbox.Control.Monad.Effect (printString)
import Lunarbox.Foreign.Render (Context2d, ForeignAction(..), GeomEventHandler, GeometryCache, emptyGeometryCache, handleMouseDown, handleMouseMove, handleMouseUp, renderScene, resizeContext, withContext)
import Web.Event.Event (EventType(..))
import Web.HTML as Web
Expand All @@ -32,6 +33,7 @@ data Action
| ResizeCanvas
| HandleEvent GeomEventHandler MouseEvent
| HandleForeignAction ForeignAction
| P

type ChildSlots
= ()
Expand Down Expand Up @@ -97,6 +99,8 @@ component =
HandleForeignAction action -> case action of
NoAction -> pure unit
_ -> raise $ BubbleForeignAction action
P -> do
printString "woah"

handleQuery :: forall a. Query a -> HalogenM State Action ChildSlots Output m (Maybe a)
handleQuery = case _ of
Expand All @@ -120,4 +124,5 @@ component =
, onMouseMove $ Just <<< HandleEvent handleMouseMove
, onMouseUp $ Just <<< HandleEvent handleMouseUp
, onMouseDown $ Just <<< HandleEvent handleMouseDown
, onDoubleClick $ const $ Just P
]
1 change: 1 addition & 0 deletions src/Foreign/Render.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exports.resizeContext = (ctx) => exports.resizeCanvas(ctx.canvas)
exports.handleMouseMoveImpl = Native.onMouseMove
exports.handleMouseUpImpl = Native.onMouseUp
exports.handleMouseDownImpl = Native.onMouseDown
exports.handleDoubleClickImpl = Native.handleDoubleClick

// We cannot do
// export * from "../typescript/save"
Expand Down
86 changes: 46 additions & 40 deletions src/Foreign/Render.purs
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,6 @@ foreign import data Context2d :: Type
-- This is just a map of cached node geometries
foreign import data GeometryCache :: Type

-- Foreign helpers
foreign import resizeCanvas :: HTMLCanvasElement -> Effect Unit

foreign import resizeContext :: Context2d -> Effect Unit

foreign import getContext :: HTMLCanvasElement -> Effect Context2d

foreign import renderScene :: Context2d -> GeometryCache -> Effect Unit

foreign import emptyGeometryCache :: Effect GeometryCache

foreign import handleMouseMoveImpl :: NativeGeomEventHandler

foreign import handleMouseUpImpl :: NativeGeomEventHandler

foreign import handleMouseDownImpl :: NativeGeomEventHandler

foreign import geometryCacheFromJsonImpl :: ForeignEitherConfig String GeometryCache -> Json -> Either String GeometryCache

foreign import geometryCacheToJson :: GeometryCache -> Json

foreign import createNode :: GeometryCache -> NodeId -> Int -> Boolean -> Nullable String -> Effect Unit

foreign import refreshInputArcs :: GeometryCache -> NodeId -> NodeState -> Effect Unit

foreign import setUnconnectableInputs :: GeometryCache -> NativeSet { id :: NodeId, index :: Int } -> Effect Unit

foreign import setUnconnectableOutputs :: GeometryCache -> NativeSet NodeId -> Effect Unit

foreign import deleteNode :: GeometryCache -> NodeId -> Effect Unit

foreign import renderPreview :: Context2d -> ForeignTypeMap -> Effect Unit

foreign import centerNode :: GeometryCache -> NodeId -> Effect Unit

foreign import centerOutput :: GeometryCache -> Effect Unit

instance decodeJsonGeometryCache :: DecodeJson GeometryCache where
-- WARNING:
-- The error messages in for this are just the Error.message from js land
Expand Down Expand Up @@ -142,15 +105,19 @@ instance defaultForeignActionConfig :: Default ForeignActionConfig where

-- | Handle a mouse down event
handleMouseDown :: GeomEventHandler
handleMouseDown = runFn4 handleMouseDownImpl $ def
handleMouseDown = runFn4 handleMouseDownImpl def

-- | Handle a mouse up event
handleMouseUp :: GeomEventHandler
handleMouseUp = runFn4 handleMouseUpImpl $ def
handleMouseUp = runFn4 handleMouseUpImpl def

-- | Handle a mouse move event
handleMouseMove :: GeomEventHandler
handleMouseMove = runFn4 handleMouseMoveImpl $ def
handleMouseMove = runFn4 handleMouseMoveImpl def

-- | Handle double clicking a node
handleDoubleClick :: GeomEventHandler
handleDoubleClick = runFn4 handleMouseMoveImpl def

-- Halogen M which keeps track of a canvas
type CanvasHalogenM r a s o m a'
Expand All @@ -174,3 +141,42 @@ withContext ref continue = do
ctx <- liftEffect $ getContext canvas
modify_ _ { context = Just ctx }
continue ctx

-- Foreign helpers
foreign import resizeCanvas :: HTMLCanvasElement -> Effect Unit

foreign import resizeContext :: Context2d -> Effect Unit

foreign import getContext :: HTMLCanvasElement -> Effect Context2d

foreign import renderScene :: Context2d -> GeometryCache -> Effect Unit

foreign import emptyGeometryCache :: Effect GeometryCache

foreign import handleMouseMoveImpl :: NativeGeomEventHandler

foreign import handleMouseUpImpl :: NativeGeomEventHandler

foreign import handleMouseDownImpl :: NativeGeomEventHandler

foreign import handleDoubleClickImpl :: NativeGeomEventHandler

foreign import geometryCacheFromJsonImpl :: ForeignEitherConfig String GeometryCache -> Json -> Either String GeometryCache

foreign import geometryCacheToJson :: GeometryCache -> Json

foreign import createNode :: GeometryCache -> NodeId -> Int -> Boolean -> Nullable String -> Effect Unit

foreign import refreshInputArcs :: GeometryCache -> NodeId -> NodeState -> Effect Unit

foreign import setUnconnectableInputs :: GeometryCache -> NativeSet { id :: NodeId, index :: Int } -> Effect Unit

foreign import setUnconnectableOutputs :: GeometryCache -> NativeSet NodeId -> Effect Unit

foreign import deleteNode :: GeometryCache -> NodeId -> Effect Unit

foreign import renderPreview :: Context2d -> ForeignTypeMap -> Effect Unit

foreign import centerNode :: GeometryCache -> NodeId -> Effect Unit

foreign import centerOutput :: GeometryCache -> Effect Unit
55 changes: 50 additions & 5 deletions src/typescript/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
import { CanvasElement } from "./types/Hiccup"
import { draw } from "@thi.ng/hiccup-canvas"
import { TextWithBackground } from "./components/TextWithBackground"
import { ArrayLikeIterable } from "@thi.ng/api"

// Used in the Default purescript implementation of GeomCache
export const emptyGeometryCache = (): GeometryCache => ({
Expand Down Expand Up @@ -547,6 +548,31 @@ const pan = (cache: GeometryCache, offset: Vec) => {
concat(null, cache.camera, translation23([], offset))
}

/**
* Finds what the mouse is over based on an event.
*
* @param cache The cache to get the transform from.
* @param event The event to process.
* @param ctx The context to which the mouse should be relative to.
*/
const getEventData = (
cache: GeometryCache,
event: MouseEvent,
ctx: CanvasRenderingContext2D
): {
target: MouseTarget
mousePosition: Vec
transform: ArrayLikeIterable<number>
mouse: Vec
} => {
const mouse = [event.pageX, event.pageY]
const transform = getMouseTransform(ctx, cache)
const mousePosition = mulV23(null, transform, mouse)

const target = getMouseTarget(mousePosition, cache)
return { target, mousePosition, transform, mouse }
}

/**
* Handle a mouseUp event
*
Expand Down Expand Up @@ -586,11 +612,7 @@ export const onMouseDown = (
) => (): ForeignAction => {
let action = config.nothing

const mouse = [event.pageX, event.pageY]
const transform = getMouseTransform(ctx, cache)
const mousePosition = mulV23(null, transform, mouse)

const target = getMouseTarget(mousePosition, cache)
const { target, mousePosition } = getEventData(cache, event, ctx)

if (target._type === MouseTargetKind.Node) {
selectNode(cache, target.node, target.id)
Expand Down Expand Up @@ -759,3 +781,26 @@ export const onMouseMove = (

return config.nothing
}

/**
* Handle a double click event
*
* @param config The config for what we want to return
* @param ctx The context to re-render to.
* @param event The event to handle.
* @param cache The cache to mutate.
*/
export const onDoubleClick = (
config: ForeignActionConfig,
ctx: CanvasRenderingContext2D,
event: MouseEvent,
cache: GeometryCache
) => (): ForeignAction => {
const { target } = getEventData(cache, event, ctx)

if (target._type === MouseTargetKind.Node) {
return config.editNode(target.id)
}

return config.nothing
}
8 changes: 2 additions & 6 deletions src/typescript/types/ForeignAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ import { Fn3, Fn2, Fn } from "@thi.ng/api"
import { NodeId } from "./Node"

// Those are here so we can do purescript interop properly
export type ForeignAction =
| { readonly createConnection: unique symbol }
| { readonly selectInput: unique symbol }
| { readonly selectOutput: unique symbol }
| { readonly nothing: unique symbol }
| { readonly deleteConnection: unique symbol }
export type ForeignAction = { readonly foreignAction: unique symbol }

export interface ForeignActionConfig {
createConnection: Fn3<NodeId, NodeId, number, ForeignAction>
selectInput: Fn2<NodeId, number, ForeignAction>
selectOutput: Fn<NodeId, ForeignAction>
deleteConnection: Fn2<NodeId, number, ForeignAction>
editNode: Fn<NodeId, ForeignAction>
nothing: ForeignAction
}

0 comments on commit d58ead7

Please sign in to comment.