Commit 2fa0f2d8 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

[sigma] mouse clicking works now

Hover still doesn't though.
parent faf6c03f
......@@ -227,7 +227,7 @@ loadPage { session, nodeId, listId, query, params: {limit, offset, orderBy } } =
case eSearchResult of
Left err -> pure $ Left err
Right (SearchResult {result}) -> do
liftEffect $ here.log2 "[loadPage] result" result
--liftEffect $ here.log2 "[loadPage] result" result
-- $ SearchQuery {query: concat query, expected: SearchDoc}
pure $ Right $ case result of
SearchResultDoc {docs} -> Docs {docs: doc2view <$> Seq.fromFoldable docs}
......
......@@ -292,6 +292,7 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
, equilateral: { numPoints: 3 }
, gargType
, hidden : false
, highlighted: false
, id : n.id_
, label : n.label
, size : DN.log (toNumber n.size + 1.0)
......@@ -406,9 +407,9 @@ transformGraph graph { edgeConfluence'
nodeMarked :: Record SigmaxT.Node -> Record SigmaxT.Node
nodeMarked node@{ id } =
if Set.member id selectedNodeIds' then
node { borderColor = "#000", type = "selected" }
node { borderColor = "#000", highlighted = true, type = "selected" }
else
node
node { highlighted = false }
nodeHideSize :: Record SigmaxT.Node -> Record SigmaxT.Node
nodeHideSize node@{ size } =
......
......@@ -59,12 +59,13 @@ drawGraphCpt = R.memo' $ here.component "graph" cpt where
boxes <- AppStore.use
{ showEdges
, graphStage
, graph
, startForceAtlas
, selectedNodeIds
, multiSelectEnabled
, graphStage
, hyperdataGraph
, mouseSelectorSize
, multiSelectEnabled
, selectedNodeIds
, startForceAtlas
} <- GraphStore.use
showEdges' <- R2.useLive' showEdges
......@@ -125,6 +126,7 @@ drawGraphCpt = R.memo' $ here.component "graph" cpt where
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do
-- bind the click event only initially, when ref was empty
Sigmax.bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabled
Sigmax.bindShiftWheel sigma mouseSelectorSize
_ <- Sigma.bindMouseSelectorPlugin sigma
pure unit
......
......@@ -80,8 +80,8 @@ docListWrapperCpt = here.component "wrapper" cpt where
-- | Hooks
-- |
R.useEffect1' selectedNodeIds' $
T.write_ (selectedNodeIds' # toSearchQuery >>> Just) query
R.useEffect1' selectedNodeIds' $ do
T.write_ (Just $ toSearchQuery selectedNodeIds') query
-- | Render
-- |
......
......@@ -85,7 +85,7 @@ mouseSelectorSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element
mouseSelectorSizeButton sigmaRef state =
sizeButton {
state
, caption: "Selector size"
, caption: "Selector size (Shift + wheel)"
, min: 1.0
, max: 50.0
, onChange: \e -> do
......@@ -98,7 +98,5 @@ mouseSelectorSizeButton sigmaRef state =
Sigma.setSettings s {
mouseSelectorSize: newValue
}
here.log "[mouseSelectorSizeButton] write start"
T.write_ newValue state
here.log "[mouseSelectorSizeButton] write stop"
}
......@@ -24,7 +24,8 @@ import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as ST
import Gargantext.Utils.Console as C
import Gargantext.Utils.Reactix as R2
import Prelude (Unit, bind, discard, flip, map, not, pure, unit, ($), (&&), (*>), (<<<), (<>), (>>=))
import Gargantext.Utils.Set as GSet
import Prelude (Unit, bind, discard, flip, map, not, pure, unit, ($), (&&), (*>), (<<<), (<>), (>>=), (+), (>), negate)
import Reactix as R
import Toestand as T
......@@ -184,20 +185,15 @@ updateNodes sigma nodesMap = do
-- | Toggles item visibility in the selected set
-- Basically: add items that are NOT in `selected` and remove items
-- that are in `selected`.
multiSelectUpdate :: ST.NodeIds -> ST.NodeIds -> ST.NodeIds
multiSelectUpdate new selected = foldl fld selected new
where
fld selectedAcc item =
if Set.member item selectedAcc then
Set.delete item selectedAcc
else
Set.insert item selectedAcc
multiSelectUpdate new selected = foldl GSet.toggle selected new
bindSelectedNodesClick :: Sigma.Sigma -> T.Box ST.NodeIds -> T.Box Boolean -> Effect Unit
bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabled =
Sigma.bindClickNodes sigma $ \nodeIds' -> do
console.log2 "[bindSelectedNodesClick] nodeIds'" nodeIds'
let nodeIds = Set.fromFoldable nodeIds'
multiSelectEnabled' <- T.read multiSelectEnabled
if multiSelectEnabled' then
......@@ -205,15 +201,16 @@ bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabled =
else
T.write_ nodeIds selectedNodeIds
bindSelectedEdgesClick :: R.Ref Sigma -> R.State ST.EdgeIds -> Effect Unit
bindSelectedEdgesClick sigmaRef (_ /\ setEdgeIds) =
dependOnSigma (R.readRef sigmaRef) "[graphCpt] no sigma" $ \sigma -> do
Sigma.bindClickEdge sigma $ \edge -> do
setEdgeIds \eids ->
if Set.member edge.id eids then
Set.delete edge.id eids
else
Set.insert edge.id eids
bindShiftWheel :: Sigma.Sigma -> T.Box Number -> Effect Unit
bindShiftWheel sigma mouseSelectorSize =
Sigma.bindShiftWheel sigma $ \delta -> do
let step = if delta > 0.0 then 5.0 else -5.0
val <- T.read mouseSelectorSize
let newVal = val + step
Sigma.setSettings sigma {
mouseSelectorSize: newVal
}
T.write_ newVal mouseSelectorSize
selectorWithSize :: Sigma.Sigma -> Int -> Effect Unit
selectorWithSize _ _ = do
......@@ -224,10 +221,10 @@ performDiff sigma g = do
-- if (Seq.null addEdges) && (Seq.null addNodes) && (Set.isEmpty removeEdges) && (Set.isEmpty removeNodes) then
-- pure unit
-- else do
console.log2 "[performDiff] addNodes" addNodes
console.log2 "[performDiff] addEdges" $ A.fromFoldable addEdges
console.log2 "[performDiff] removeNodes" removeNodes
console.log2 "[performDiff] removeEdges" removeEdges
-- console.log2 "[performDiff] addNodes" addNodes
-- console.log2 "[performDiff] addEdges" $ A.fromFoldable addEdges
-- console.log2 "[performDiff] removeNodes" removeNodes
-- console.log2 "[performDiff] removeEdges" removeEdges
traverse_ (Graphology.addNode sigmaGraph) addNodes
traverse_ (Graphology.addEdge sigmaGraph) addEdges
traverse_ (Graphology.removeEdge sigmaGraph) removeEdges
......
......@@ -63,73 +63,55 @@ let sigmaMouseSelector = function(sigma, options) {
const distance = (x1, y1, x2, y2) => Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
let mouseSelector = () => {
let _self = this;
let _offset = null;
const _self = this;
const _s = sigma;
//const _renderer = renderer;
const captor = sigma.mouseCaptor;
const _container = captor.container;
//renderer.initDOM('canvas', 'mouseSelector');
// A hack to force resize to be called (there is a width/height equality
// check which can't be escaped in any other way).
//renderer.resize(renderer.width - 1, renderer.height - 1);
//renderer.resize(renderer.width + 1, renderer.height + 1);
//const _context = _renderer.contexts.mouseSelector;
const _context = _container.getContext('2d');
// These are used to prevent using the 'click' event when in fact this was a drag
let _clickPositionX = null;
let _clickPositionY = null;
let _isValidClick = false;
_container.onmousemove = (e) => { return mouseMove(e); };
_container.onclick = (e) => { return onClick(e); };
//_context.canvas.onclick = function(e) { return onClick(e); };
_container.onmousedown = (e) => { return onMouseDown(e); }
_container.onmouseup = (e) => { return onMouseUp(e); }
captor.on('click', (e) => { return onClick(e); });
// The mouseSelector canvas will pass its events down to the "mouse" canvas.
//_context.canvas.style.pointerEvents = 'none';
sigma.on('kill', () => _self.unbindAll());
this.unbindAll = () => {
// console.log('[sigmaMouseSelector] unbinding');
_container.onclick = null;
//_context.canvas.onmousemove = null;
_container.onmousemove = null;
_container.onmousedown = null;
_container.onmouseup = null;
const unbindAll = () => {
// TODO Maybe not needed if sigma is killed and we did bind to
// mouse captor instead of the canvas?
// _container.onclick = null;
// _container.onmousemove = null;
// _container.onmousedown = null;
// _container.onmouseup = null;
}
const onMouseDown = (e) => {
_clickPositionX = e.clientX;
_clickPositionY = e.clientY;
const bindAll = () => {
captor.on('mousemove', mouseMove);
captor.on('click', onClick);
captor.on('wheel', onWheel);
sigma.on('kill', () => unbindAll());
}
const onMouseUp = (e) => {
// Prevent triggering click when in fact this was a drag
if ((_clickPositionX != e.clientX) || (_clickPositionY != e.clientY)) {
_clickPositionX = null;
_clickPositionY = null;
_isValidClick = false;
} else {
_isValidClick = true;
const onWheel = (e) => {
const shiftPressed = e.original.shiftKey;
// zoom in has e.delta > 0 (around 0.44)
// zoom out has e.delta < 0 (around -0.44)
if(shiftPressed) {
// TODO Fix this so that the canvas is not zoomed.
console.log('[onWheel] e', e);
e.original.preventDefault();
e.original.stopPropagation();
sigma.emit('shiftWheel', {
delta: e.delta
});
}
}
// Responsible for rendering the selector properly
const mouseMove = (e) => {
const size = sigma.settings['mouseSelectorSize'] || 3;
//const x = e.clientX + document.body.scrollLeft - _offset.left - size/2;
//const y = e.clientY + document.body.scrollTop - _offset.top - size/2;
const x = e.layerX;
const y = e.layerY;
_context.clearRect(0, 0, _context.canvas.width, _context.canvas.height);
_context.fillStyle = 'rgba(91, 192, 222, 0.7)';
_context.beginPath();
_context.arc(
x,
y,
e.x,
e.y,
size,
0,
Math.PI * 2,
......@@ -140,61 +122,33 @@ let sigmaMouseSelector = function(sigma, options) {
}
const onClick = (e) => {
// TODO For some reason this event is sent again, with
// _clickPositionX/Y empty
if(!_isValidClick || !_clickPositionX || !_clickPositionY) {
return;
}
const size = sigma.settings['mouseSelectorSize'] || 3;
//const x = e.data.clientX + document.body.scrollLeft - _offset.left - size/2;
//const y = e.data.clientY + document.body.scrollTop - _offset.top - size/2;
//const prefix = _renderer.options.prefix;
//console.log('[sigmaMouseSelector] clicked', e, x, y, size);
let nodeIds = [];
for(let nodeId in sigma.nodeDataCache) {
let data = sigma.nodeDataCache[nodeId];
let position = sigma.framedGraphToViewport(data);
// TODO Either distance or node is clicked directly
if(distance(e.x, e.y, position.x, position.y) <= size) {
nodeIds.push(nodeId);
}
}
/*
sigma.graph.forEachNode((node, attrs) => {
if(distance(x, y, attrs.x, attrs.y) <= size) {
nodes.push(node);
}
});
*/
//console.log('[sigmaMouseSelector] nodes', nodes);
// nodes.forEach((n) => {
// sigma.emit('clickNode', { node: n });
// })
// handle node click when our selector doesn't cover it's center
// (e.g. large nodes)
const nodeAtPosition = sigma.getNodeAtPosition(e);
if((nodeAtPosition && (nodeIds.indexOf(nodeAtPosition) == -1))) {
nodeIds.push(nodeAtPosition);
}
sigma.emit('clickNodes', {
nodeIds: nodeIds
//captor: e.data
})
_clickPositionX = null;
_clickPositionY = null;
return false;
}
const calculateOffset = (element) => {
var style = window.getComputedStyle(element);
var getCssProperty = function(prop) {
return parseInt(style.getPropertyValue(prop).replace('px', '')) || 0;
};
return {
left: element.getBoundingClientRect().left + getCssProperty('padding-left'),
top: element.getBoundingClientRect().top + getCssProperty('padding-top')
};
};
// Container resize event listener
// @TODO: debounce?
const onContainerResize = (entries) => {
_offset = calculateOffset(_container);
};
const _resizeObserver = new ResizeObserver( onContainerResize );
_resizeObserver.observe(_container);
bindAll();
}
mouseSelector();
......@@ -209,9 +163,8 @@ let sigmaMouseSelector = function(sigma, options) {
function _sigma(left, right, el, opts) {
try {
let graph = new Graph();
console.log('initializing sigma with el', el);
console.log('initializing sigma with opts', opts);
let s = new sigma(graph, el, opts);
console.log('initializing sigma with el', el, 'opts', 'sigma', s);
sigmaMouseSelector(s);
return right(s);
} catch(e) {
......
......@@ -137,6 +137,14 @@ bindClickNodes s f = on_ s "clickNodes" $ \e -> do
unbindClickNodes :: Sigma -> Effect Unit
unbindClickNodes s = unbind_ s "clickNodes"
-- | Shift + mousewheel changes selector size
bindShiftWheel :: Sigma -> (Number -> Effect Unit) -> Effect Unit
bindShiftWheel s f = on_ s "shiftWheel" $ \e -> do
let delta = e .. "delta" :: Number
f delta
unbindShiftWheel :: Sigma -> Effect Unit
unbindShiftWheel s = unbind_ s "shiftWheel"
-- | Bind a `overNode` event.
bindOverNode :: Sigma -> (Record Types.Node -> Effect Unit) -> Effect Unit
bindOverNode s f = bindNodeEvent s "overNode" f
......
......@@ -40,6 +40,7 @@ type Node = (
, equilateral :: { numPoints :: Int }
, gargType :: GT.Mode
, hidden :: Boolean
, highlighted :: Boolean
, id :: NodeId
, label :: String
, size :: Number
......
module Gargantext.Utils.Set where
import Data.Ord (class Ord)
import Data.Set as Set
-- | If `a` is in Set, remove it, otherwise add it
toggle :: forall a. Ord a => Set.Set a -> a -> Set.Set a
toggle s x = if Set.member x s then Set.delete x s else Set.insert x s
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