Skip to content

Canvas marquee & multi select #153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion editor-packages/editor-canvas/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
# A Interactive canvas for runtime frames.
# A Html5 backend Interactive canvas for runtime frames.

## The system

- Canvas
- Hud
- Event
- Math
- Iframe
- Node
- Host

## General architecture

- `ScaffoldCanvas` - A single component canvas that holds both renderer and eventsystem
- `RenderOnlyCanvas + EventSystem` - A Customizable system for complex and heavy rendering. (use saperate render host with iframe)

## Events

gesture events

- move (pan)
- zoom (pinch)

- create node
- remove node
- move node
- resize node
- rename node
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ export function CanvasEventTarget({
const [first_wheel_event, set_first_wheel_event] =
useState<FullGestureState<"wheel">>();

// this is a hack to prevent from onDragStart being called even when no movement is detected.
const [drag_start_emitted, set_drag_start_emitted] = useState(false);

useGesture(
{
onPinch: onZooming,
Expand Down Expand Up @@ -150,7 +153,10 @@ export function CanvasEventTarget({
return;
}

onDragStart(s);
if (s.delta[0] || s.delta[1]) {
onDragStart(s);
set_drag_start_emitted(true);
}
},
onDrag: (s) => {
if (isSpacebarPressed) {
Expand All @@ -161,6 +167,10 @@ export function CanvasEventTarget({
return;
}

if ((s.delta[0] || s.delta[1]) && !drag_start_emitted) {
set_drag_start_emitted(true);
onDragStart(s);
}
onDrag(s);
},
onDragEnd: (s) => {
Expand All @@ -169,6 +179,7 @@ export function CanvasEventTarget({
return;
}

set_drag_start_emitted(false);
onDragEnd(s);
},
onMouseDown: onPointerDown,
Expand Down
90 changes: 73 additions & 17 deletions editor-packages/editor-canvas/canvas/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ import {
OnPointerDownHandler,
OnDragHandler,
} from "../canvas-event-target";
import { get_hovering_target, centerOf } from "../math";
import {
target_of_point,
centerOf,
edge_scrolling,
target_of_area,
} from "../math";
import { utils } from "@design-sdk/core";
import { LazyFrame } from "@code-editor/canvas/lazy-frame";
import { HudCustomRenderers, HudSurface } from "./hud-surface";
import { HudCustomRenderers, HudSurface } from "../hud";
import type { Box, XY, CanvasTransform, XYWH } from "../types";
import type { FrameOptimizationFactors } from "../frame";
const designq = utils.query;
Expand Down Expand Up @@ -89,7 +94,7 @@ export function Canvas({
...props
}: {
viewbound: Box;
onSelectNode?: (node?: ReflectSceneNode) => void;
onSelectNode?: (...node: ReflectSceneNode[]) => void;
onClearSelection?: () => void;
} & CanvasCustomRenderers &
CanvasState & {
Expand Down Expand Up @@ -131,6 +136,7 @@ export function Canvas({
? [offset[0] / zoom, offset[1] / zoom]
: [0, 0];
const [isPanning, setIsPanning] = useState(false);
const [isDraggomg, setIsDragging] = useState(false);
const [marquee, setMarquee] = useState<XYWH>(null);

const cvtransform: CanvasTransform = {
Expand All @@ -151,17 +157,46 @@ export function Canvas({
setHoveringLayer(wshighlight);
}, [highlightedLayer]);

// area selection hook
useEffect(() => {
if (marquee) {
const area: XYWH = [
marquee[0] / zoom,
marquee[1] / zoom,
marquee[2] / zoom,
marquee[3] / zoom,
];

const selections = target_of_area({
area,
tree: nodes,
contain: false,
});

// https://stackoverflow.com/a/19746771
const same =
selectedNodes.length === selections?.length &&
selectedNodes.every((value, index) => value === selections[index].id);

if (!same) {
onSelectNode(...selections);
}
}
//
}, [marquee]);

const onPointerMove: OnPointerMoveHandler = (state) => {
if (isPanning || isZooming) {
if (isPanning || isZooming || isDraggomg) {
// don't perform hover calculation while transforming.
return;
}
const hovering = get_hovering_target({
const hovering = target_of_point({
point: state.xy,
tree: nodes,
zoom: zoom,
offset: nonscaled_offset,
margin: LAYER_HOVER_HIT_MARGIN,
reverse: true,
});

if (!hovering) {
Expand Down Expand Up @@ -223,27 +258,48 @@ export function Canvas({
setOffset([newx, newy]);
};

const onDragStart: OnDragHandler = (s) => {
onClearSelection();
setIsDragging(true);
setHoveringLayer(null);

// set the marquee start point
const [x, y] = s.initial;
const [ox, oy] = offset;
const [x1, y1] = [x - ox, y - oy];
setMarquee([x1, y1, 0, 0]);
};

const onDrag: OnDragHandler = (s) => {
const [x1, y1] = s.initial;
const [x2, y2] = [
const [ox, oy] = offset;
const [x, y] = [
// @ts-ignore
s.event.clientX,
// @ts-ignore
s.event.clientY,
];

const [ox, oy] = offset;
const [x, y, w, h] = [
x1 - ox,
y1 - oy,
x2 - x1, // w
y2 - y1, // h
];
setMarquee([x, y, w, h]);
const [x1, y1] = [x - ox, y - oy];

if (marquee) {
const [w, h] = [
x1 - marquee[0], // w
y1 - marquee[1], // h
];
setMarquee([marquee[0], marquee[1], w, h]);
}

// edge scrolling
const [cx, cy] = [x, y];
const [dx, dy] = edge_scrolling(cx, cy, viewbound);
if (dx || dy) {
setOffset([ox + dx, oy + dy]);
}
};

const onDragEnd: OnDragHandler = (s) => {
setMarquee(null);
setIsDragging(false);
};

const is_canvas_transforming = isPanning || isZooming;
Expand Down Expand Up @@ -299,8 +355,8 @@ export function Canvas({
onPointerMoveStart={() => {}}
onPointerMoveEnd={() => {}}
onPointerDown={onPointerDown}
onDragStart={onDragStart}
onDrag={onDrag}
onDragStart={() => {}} // TODO:
onDragEnd={onDragEnd}
>
<HudSurface
Expand Down Expand Up @@ -363,7 +419,7 @@ function CanvasTransformRoot({
width: 0,
height: 0,
willChange: "transform",
transform: `scale(${scale}) translateX(${xy[0]}px) translateY(${xy[1]}px)`,
transform: `scale(${scale}) translate3d(${xy[0]}px, ${xy[1]}px, 0)`,
isolation: "isolate",
}}
>
Expand Down
7 changes: 7 additions & 0 deletions editor-packages/editor-canvas/docs/commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- move back
- move front

- copy
- paste
- move under parent
- lock / unlock
25 changes: 25 additions & 0 deletions editor-packages/editor-canvas/docs/feature-area-selection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Area selection (Marquee selection)

> This feature yet does not consider vector networks. (boolean operation)

Keymode

- default
- cmd

Parameters

- raycast mode:
- hit - if the target has an intersection with the area.
- contain - if the target is contained in the area.
- type
- frame
- group
- frame
- element (others)
- is root - this only effects to the frame node.

Final output for painting

- selections (each selection will get a highlight)
- bounding box (abstract group)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Canvas Drag Scroll (Scroll / Pan while dragging)

scroll (translate) a canvas while dragging (marquee, move, resize element). if hit the edge of the canvas, the canvas will scroll.
42 changes: 42 additions & 0 deletions editor-packages/editor-canvas/docs/hud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Hud system

node

- hover outline
- resize knob
- rotate knob
- border radius knob
- frame title
- draft editor (text editing)
- multiplayer cursor
- resizing size indicator
- rotating rotation indicator

ruler & guide

- ruler
- guide (user defined guide line)
- highlight guide (hovered, selected guide line)
- snap line (snapping guide line)
- layout grids (grid-template-columns)
- spacing guide (size between 2 nodes)

layout

- layout placement guide (preview the place of an moving item after placement inside a certain lyout - row / col)

- interaction knob
- interaction line

popover

- popovers

feedback (comments)

- pin
- create pin

vector

- TODO:
1 change: 1 addition & 0 deletions editor-packages/editor-canvas/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Html5 Backend Canvas docs
2 changes: 2 additions & 0 deletions editor-packages/editor-canvas/hud/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { HudSurface } from "./hud-surface";
export type { HudCustomRenderers, DisplayNodeMeta } from "./hud-surface";
4 changes: 2 additions & 2 deletions editor-packages/editor-canvas/lazy-frame/lazy-frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function LazyFrame({
pointerEvents: "none",
transition: "opacity 50ms ease-out 0s",
transformOrigin: "left top",
transform: `translateX(${x}px) translateY(${y}px)`,
transform: `translate3d(${x}px, ${y}px, 0)`,
willChange: "transform",
display: "block",
position: "fixed",
Expand All @@ -40,7 +40,7 @@ export function LazyFrame({
style={{
width: "100%",
height: "100%",
display: inViewport ? "block" : "none",
contentVisibility: inViewport ? "visible" : "hidden",
}}
>
{children}
Expand Down
2 changes: 1 addition & 1 deletion editor-packages/editor-canvas/marquee/marquee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function Marquee({ rect }: { rect: XYWH }) {
height: Math.abs(h),
willChange: "transform, opacity",
transformOrigin: "0px 0px",
transform: `translateX(${x}px) translateY(${y}px) rotate(${r}deg)`,
transform: `translate3d(${x}px, ${y}px, 0) rotate(${r}deg)`,
border: `${canvasSelectionRectBorderColor} 1px solid`,
}}
></div>
Expand Down
Loading