aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/InteractionUtils.tsx46
-rw-r--r--src/client/views/DocumentDecorations.tsx53
-rw-r--r--src/client/views/GestureOverlay.tsx88
-rw-r--r--src/client/views/InkingStroke.tsx97
-rw-r--r--src/client/views/MainView.tsx3
-rw-r--r--src/client/views/collections/CollectionMenu.tsx35
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.tsx376
-rw-r--r--src/client/views/nodes/ColorBox.tsx45
9 files changed, 591 insertions, 156 deletions
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 8b3614ea7..04a750f93 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -99,6 +99,15 @@ export namespace InteractionUtils {
if (shape) { //if any of the shape are true
pts = makePolygon(shape, points);
}
+ else if (points.length >= 5 && points[3].X === points[4].X) {
+ for (var i = 0; i < points.length - 3; i += 4) {
+ const array = [[points[i].X, points[i].Y], [points[i + 1].X, points[i + 1].Y], [points[i + 2].X, points[i + 2].Y], [points[i + 3].X, points[i + 3].Y]];
+ for (var t = 0; t < 1; t += 0.01) {
+ const point = beziercurve(t, array);
+ pts.push({ X: point[0], Y: point[1] });
+ }
+ }
+ }
else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) {
//pointer is up (first and last points are the same)
const newPoints = points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
@@ -118,6 +127,12 @@ export namespace InteractionUtils {
pts.pop();
}
}
+ if (isNaN(scalex)) {
+ scalex = 1;
+ }
+ if (isNaN(scaley)) {
+ scaley = 1;
+ }
const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc +
`${(pt.X - left - width / 2) * scalex + width / 2},
${(pt.Y - top - width / 2) * scaley + width / 2} `, "");
@@ -136,7 +151,6 @@ export namespace InteractionUtils {
<polygon points={`${2 - arrowDim} ${-Math.max(1, arrowDim / 2)}, ${2 - arrowDim} ${Math.max(1, arrowDim / 2)}, 3 0`} />
</marker>}
</defs>}
-
<polyline
points={strpts}
style={{
@@ -157,17 +171,6 @@ export namespace InteractionUtils {
</svg>);
}
- // export function makeArrow() {
- // return (
- // InkOptionsMenu.Instance.getColors().map(color => {
- // const id1 = "arrowStartTest" + color;
- // <marker id={id1} orient="auto" overflow="visible" refX="0" refY="1" markerWidth="10" markerHeight="7">
- // <polygon points="0 0, 3 1, 0 2" fill={"#" + color} />
- // </marker>;
- // })
- // );
- // }
-
export function makePolygon(shape: string, points: { X: number, Y: number }[]) {
if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) {
//pointer is up (first and last points are the same)
@@ -217,10 +220,28 @@ export namespace InteractionUtils {
points.push({ X: left, Y: top });
return points;
case "triangle":
+ // points.push({ X: left, Y: bottom });
+ // points.push({ X: right, Y: bottom });
+ // points.push({ X: (right + left) / 2, Y: top });
+ // points.push({ X: left, Y: bottom });
+
+ points.push({ X: left, Y: bottom });
points.push({ X: left, Y: bottom });
+
points.push({ X: right, Y: bottom });
+ points.push({ X: right, Y: bottom });
+ points.push({ X: right, Y: bottom });
+ points.push({ X: right, Y: bottom });
+
+ points.push({ X: (right + left) / 2, Y: top });
+ points.push({ X: (right + left) / 2, Y: top });
points.push({ X: (right + left) / 2, Y: top });
+ points.push({ X: (right + left) / 2, Y: top });
+
+ points.push({ X: left, Y: bottom });
points.push({ X: left, Y: bottom });
+
+
return points;
case "circle":
const centerX = (right + left) / 2;
@@ -256,6 +277,7 @@ export namespace InteractionUtils {
// points.push({ X: x2, Y: y2 });
// return points;
case "line":
+
points.push({ X: left, Y: top });
points.push({ X: right, Y: bottom });
return points;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4257ec3c9..190dbc8c3 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -21,7 +21,7 @@ import e = require('express');
import { CollectionDockingView } from './collections/CollectionDockingView';
import { SnappingManager } from '../util/SnappingManager';
import { HtmlField } from '../../fields/HtmlField';
-import { InkData, InkField, InkTool } from "../../fields/InkField";
+import { InkField } from "../../fields/InkField";
import { Tooltip } from '@material-ui/core';
library.add(faCaretUp);
@@ -59,6 +59,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
private _prevX = 0;
private _prevY = 0;
private _centerPoints: { X: number, Y: number }[] = [];
+ private _inkDocs: { x: number, y: number, width: number, height: number }[] = [];
@observable private _accumulatedTitle = "";
@observable private _titleControlString: string = "#title";
@@ -309,8 +310,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const right = Math.max(...xs);
const bottom = Math.max(...ys);
- doc._height = (bottom - top) * element.props.ScreenToLocalTransform().Scale;
- doc._width = (right - left) * element.props.ScreenToLocalTransform().Scale;
+ // doc._height = (bottom - top) * element.props.ScreenToLocalTransform().Scale;
+ // doc._width = (right - left) * element.props.ScreenToLocalTransform().Scale;
+ doc._height = (bottom - top);
+ doc._width = (right - left);
}
index++;
@@ -329,6 +332,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
_dragHeights = new Map<Doc, number>();
@action
onPointerDown = (e: React.PointerEvent): void => {
+
+ this._inkDocs = [];
+ SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+ const doc = Document(element.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height) {
+ this._inkDocs.push({ x: doc.x, y: doc.y, width: doc._width, height: doc._height });
+ }
+
+ }));
+
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });
if (e.button === 0) {
this._resizeHdlId = e.currentTarget.id;
@@ -505,6 +518,27 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
(e.button === 0) && this._resizeUndo?.end();
this._resizeUndo = undefined;
SnappingManager.clearSnapLines();
+
+
+ //need to change points for resize, or else rotation/control points will fail.
+ SelectionManager.SelectedDocuments().forEach(action((element: DocumentView, index) => {
+ const doc = Document(element.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+ const newPoints: { X: number, Y: number }[] = [];
+ for (var i = 0; i < ink.length; i++) {
+ // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+ const newX = (doc.x - this._inkDocs[index].x) + (ink[i].X * doc._width) / this._inkDocs[index].width;
+ const newY = (doc.y - this._inkDocs[index].y) + (ink[i].Y * doc._height) / this._inkDocs[index].height;
+ newPoints.push({ X: newX, Y: newY });
+ }
+ doc.data = new InkField(newPoints);
+
+ }
+
+ }
+ }));
}
@computed
@@ -595,6 +629,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (bounds.y > bounds.b) {
bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight);
}
+ var offset = 0;
+ //make offset larger for ink to edit points
+ if (seldoc.rootDoc.type === DocumentType.INK) {
+ offset = 20;
+ }
return (<div className="documentDecorations" style={{ background: darkScheme }} >
<div className="documentDecorations-background" style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
@@ -607,10 +646,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
</div>
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
<div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{
- width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
- height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
- left: bounds.x - this._resizeBorderWidth / 2,
- top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
+ width: (bounds.r - bounds.x + this._resizeBorderWidth + offset) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + offset) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2 - offset / 2,
+ top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight - offset / 2,
}}>
{maximizeIcon}
{titleArea}
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index ed77da8ca..7c0a8635e 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -22,6 +22,7 @@ import { RadialMenu } from "./nodes/RadialMenu";
import HorizontalPalette from "./Palette";
import { Touchable } from "./Touchable";
import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
+import * as fitCurve from 'fit-curve';
import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
@observer
@@ -630,6 +631,22 @@ export default class GestureOverlay extends Touchable {
// if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document
if (!actionPerformed) {
+ const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
+ newPoints.pop();
+ const controlPoints: { X: number, Y: number }[] = [];
+
+ const bezierCurves = fitCurve(newPoints, 10);
+ for (const curve of bezierCurves) {
+
+ controlPoints.push({ X: curve[0][0], Y: curve[0][1] });
+ controlPoints.push({ X: curve[1][0], Y: curve[1][1] });
+ controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
+ controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
+
+
+ }
+ this._points = controlPoints;
+
this.dispatchGesture(GestureUtils.Gestures.Stroke);
}
this._points = [];
@@ -649,6 +666,10 @@ export default class GestureOverlay extends Touchable {
}
makePolygon = (shape: string, gesture: boolean) => {
+ //take off gesture recognition for now
+ if (gesture) {
+ return false;
+ }
const xs = this._points.map(p => p.X);
const ys = this._points.map(p => p.Y);
var right = Math.max(...xs);
@@ -684,18 +705,53 @@ export default class GestureOverlay extends Touchable {
//must be (points[0].X,points[0]-1)
case "rectangle":
this._points.push({ X: left, Y: top });
+ this._points.push({ X: left, Y: top });
+
+ this._points.push({ X: right, Y: top });
+ this._points.push({ X: right, Y: top });
this._points.push({ X: right, Y: top });
+ this._points.push({ X: right, Y: top });
+
+ this._points.push({ X: right, Y: bottom });
this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: right, Y: bottom });
+
this._points.push({ X: left, Y: bottom });
+ this._points.push({ X: left, Y: bottom });
+ this._points.push({ X: left, Y: bottom });
+ this._points.push({ X: left, Y: bottom });
+
+ this._points.push({ X: left, Y: top });
this._points.push({ X: left, Y: top });
- this._points.push({ X: left, Y: top - 1 });
+ // this._points.push({ X: left, Y: top });
+ // this._points.push({ X: left, Y: top });
+
+ // this._points.push({ X: left, Y: top - 1 });
break;
case "triangle":
+ // this._points.push({ X: left, Y: bottom });
+ // this._points.push({ X: right, Y: bottom });
+ // this._points.push({ X: (right + left) / 2, Y: top });
+ // this._points.push({ X: left, Y: bottom });
+ // this._points.push({ X: left, Y: bottom - 1 });
this._points.push({ X: left, Y: bottom });
+ this._points.push({ X: left, Y: bottom });
+
+ this._points.push({ X: right, Y: bottom });
this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: right, Y: bottom });
+
this._points.push({ X: (right + left) / 2, Y: top });
+ this._points.push({ X: (right + left) / 2, Y: top });
+ this._points.push({ X: (right + left) / 2, Y: top });
+ this._points.push({ X: (right + left) / 2, Y: top });
+
+ this._points.push({ X: left, Y: bottom });
this._points.push({ X: left, Y: bottom });
- this._points.push({ X: left, Y: bottom - 1 });
+
+
break;
case "circle":
const centerX = (right + left) / 2;
@@ -712,11 +768,37 @@ export default class GestureOverlay extends Touchable {
}
this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+ // this._points.push({ X: centerX, Y: top });
+ // this._points.push({ X: centerX + radius / 2, Y: top });
+
+ // this._points.push({ X: right, Y: top + radius / 2 });
+ // this._points.push({ X: right, Y: top + radius });
+ // this._points.push({ X: right, Y: top + radius });
+ // this._points.push({ X: right, Y: bottom - radius / 2 });
+
+ // this._points.push({ X: right - radius / 2, Y: bottom });
+ // this._points.push({ X: right - radius, Y: bottom });
+ // this._points.push({ X: right - radius, Y: bottom });
+ // this._points.push({ X: left + radius / 2, Y: bottom });
+
+ // this._points.push({ X: left, Y: bottom - radius / 2 });
+ // this._points.push({ X: left, Y: bottom - radius });
+ // this._points.push({ X: left, Y: bottom - radius });
+ // this._points.push({ X: left, Y: top + radius / 2 });
+
+ // this._points.push({ X: left + radius / 2, Y: top });
+ // this._points.push({ X: left + radius, Y: top });
+
+
+
+
+
+
+
break;
case "line":
this._points.push({ X: left, Y: top });
this._points.push({ X: right, Y: bottom });
- // this._points.push({ X: right, Y: bottom - 1 });
break;
case "arrow":
const x1 = left;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index e26ad47f9..5892e8346 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -17,6 +17,7 @@ import { Scripting } from "../util/Scripting";
import { Doc } from "../../fields/Doc";
import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
import { action } from "mobx";
+import { setupMoveUpEvents } from "../../Utils";
library.add(faPaintBrush);
@@ -45,6 +46,38 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
FormatShapePane.Instance.Pinned = true;
}
+ private _prevX = 0;
+ private _prevY = 0;
+ private _controlNum = 0;
+ @action
+ onControlDown = (e: React.PointerEvent, i: number): void => {
+ setupMoveUpEvents(this, e, this.onControlMove, this.onControlup, (e) => { });
+ this._prevX = e.clientX;
+ this._prevY = e.clientY;
+ this._controlNum = i;
+ }
+
+ @action
+ changeCurrPoint = (i: number) => {
+ FormatShapePane.Instance._currPoint = i;
+ }
+
+ @action
+ onControlMove = (e: PointerEvent, down: number[]): boolean => {
+ const xDiff = this._prevX - e.clientX;
+ const yDiff = this._prevY - e.clientY;
+ FormatShapePane.Instance.control(xDiff, yDiff, this._controlNum);
+ this._prevX = e.clientX;
+ this._prevY = e.clientY;
+ return false;
+ }
+
+ onControlup = (e: PointerEvent) => {
+ this._prevX = 0;
+ this._prevY = 0;
+ this._controlNum = 0;
+ }
+
public static MaskDim = 50000;
render() {
TraceMobx();
@@ -62,14 +95,74 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const scaleX = (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth);
const scaleY = (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth);
const strokeColor = StrCast(this.layoutDoc.color, "");
+
const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
+
const hpoints = InteractionUtils.CreatePolyline(data, left, top,
this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
"none", "none", "0", scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
+
+ var controlPoints: { X: number, Y: number, I: number }[] = [];
+ var handlePoints: { X: number, Y: number, I: number, dot1: number, dot2: number }[] = [];
+ var handleLine: { X1: number, Y1: number, X2: number, Y2: number, X3: number, Y3: number, dot1: number, dot2: number }[] = [];
+ if (data.length >= 4) {
+ for (var i = 0; i <= data.length - 4; i += 4) {
+ controlPoints.push({ X: data[i].X, Y: data[i].Y, I: i });
+ controlPoints.push({ X: data[i + 3].X, Y: data[i + 3].Y, I: i + 3 });
+ handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 });
+ handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 });
+ }
+
+ handleLine.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 });
+ for (var i = 2; i < data.length - 4; i += 4) {
+
+ handleLine.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
+
+ }
+ handleLine.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 });
+
+
+ }
+ if (data.length <= 4) {
+ handlePoints = [];
+ handleLine = [];
+ controlPoints = [];
+ for (var i = 0; i < data.length; i++) {
+ controlPoints.push({ X: data[i].X, Y: data[i].Y, I: i });
+ }
+
+ }
+ const dotsize = String(Math.min(width * scaleX, height * scaleY) / 40);
+
+ const controls = controlPoints.map((pts, i) =>
+
+ <svg height="10" width="10">
+ <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="black" stroke-width={String(Number(dotsize) / 2)} fill="red"
+ onPointerDown={(e) => { this.changeCurrPoint(pts.I); this.onControlDown(e, pts.I); }} pointerEvents="all" cursor="all-scroll" />
+ </svg>);
+ const handles = handlePoints.map((pts, i) =>
+
+ <svg height="10" width="10">
+ <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="black" stroke-width={String(Number(dotsize) / 2)} fill="green"
+ onPointerDown={(e) => this.onControlDown(e, pts.I)} pointerEvents="all" cursor="all-scroll" display={(pts.dot1 === FormatShapePane.Instance._currPoint || pts.dot2 === FormatShapePane.Instance._currPoint) ? "inherit" : "none"} />
+ </svg>);
+ const handleLines = handleLine.map((pts, i) =>
+
+ <svg height="100" width="100">
+ <line x1={(pts.X1 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y1 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" stroke-width={String(Number(dotsize) / 2)}
+ display={(pts.dot1 === FormatShapePane.Instance._currPoint || pts.dot2 === FormatShapePane.Instance._currPoint) ? "inherit" : "none"} />
+ <line x1={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ x2={(pts.X3 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y3 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" stroke-width={String(Number(dotsize) / 2)}
+ display={(pts.dot1 === FormatShapePane.Instance._currPoint || pts.dot2 === FormatShapePane.Instance._currPoint) ? "inherit" : "none"} />
+
+ </svg>);
+
+
return (
<svg className="inkingStroke"
width={width}
@@ -92,6 +185,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
</defs>
{hpoints}
{points}
+ {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? controls : ""}
+ {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? handles : ""}
+ {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? handleLines : ""}
+
</svg>
);
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 33cd43678..37d99fb16 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -147,7 +147,8 @@ export class MainView extends React.Component {
fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faChevronLeft, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
- fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper);
+ fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper,
+ fa.faBezierCurve, fa.faCircle, fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight);
this.initEventListeners();
this.initAuthenticationRouters();
}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 3eda2e2f2..f9b944bb1 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -26,6 +26,7 @@ import { DocumentView } from "../nodes/DocumentView";
import { ColorState } from "react-color";
import { ObjectField } from "../../../fields/ObjectField";
import { ScriptField } from "../../../fields/ScriptField";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
@observer
export default class CollectionMenu extends AntimodeMenu {
@@ -370,11 +371,17 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
}
private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
private _width = ["1", "5", "10", "100"];
- private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"];
- private _head = ["", "", "arrow", "", "", "arrow", "", "", ""];
- private _end = ["", "arrow", "arrow", "", "arrow", "arrow", "", "", ""];
- private _shape = ["line", "line", "line", "", "", "", "rectangle", "circle", "triangle"];
-
+ // private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"];
+ // private _head = ["", "", "arrow", "", "", "arrow", "", "", ""];
+ // private _end = ["", "arrow", "arrow", "", "arrow", "arrow", "", "", ""];
+ // private _shape = ["line", "line", "line", "", "", "", "rectangle", "circle", "triangle"];
+ private _dotsize = [10, 20, 30, 40];
+ private _draw = ["∿", "⎯", "→", "↔︎", "ロ", "O"];
+ private _head = ["", "", "", "arrow", "", ""];
+ private _end = ["", "", "arrow", "arrow", "", ""];
+ private _shape = ["", "line", "line", "line", "rectangle", "circle"];
+ private _title = ["pen", "line", "line with arrow", "line with double arrows", "square", "circle",];
+ private _faName = ["pen-fancy", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"];
@observable _shapesNum = this._shape.length;
@observable _selected = this._shapesNum;
@@ -437,9 +444,11 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
});
return <div className="btn-draw" key="draw">
{this._draw.map((icon, i) =>
- <button className="antimodeMenu-button" key={icon} onPointerDown={() => func(i, false)} onDoubleClick={() => func(i, true)}
+ <button className="antimodeMenu-button" title={this._title[i]} key={icon} onPointerDown={() => func(i, false)} onDoubleClick={() => func(i, true)}
style={{ backgroundColor: i === this._selected ? "121212" : "", fontSize: "20" }}>
- {this._draw[i]}
+ {/* {this._draw[i]} */}
+ <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
+
</button>)}
</div>;
}
@@ -458,11 +467,11 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
return !this._widthBtn ? widthPicker :
<div className="btn2-group" key="width">
{widthPicker}
- {this._width.map(wid =>
- <button className="antimodeMenu-button" key={wid}
+ {this._width.map((wid, i) =>
+ <button className="antimodeMenu-button" key={wid} title="change width"
onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
- style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001 }}>
- {wid}
+ style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: "center" }}>
+ •
</button>)}
</div>;
}
@@ -502,7 +511,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
return <button className="antimodeMenu-button" key="format" title="toggle foramatting pane"
onPointerDown={action(e => FormatShapePane.Instance.Pinned = !FormatShapePane.Instance.Pinned)}
style={{ backgroundColor: this._fillBtn ? "121212" : "" }}>
- <FontAwesomeIcon icon="chevron-right" size="lg" />
+ <FontAwesomeIcon icon="angle-double-right" size="lg" />
</button>;
}
@@ -531,10 +540,10 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
}
{!this.props.isOverlay || this.props.docView.layoutDoc.isAnnotating ?
<>
+ {this.drawButtons}
{this.widthPicker}
{this.colorPicker}
{this.fillPicker}
- {this.drawButtons}
{this.formatPane}
</> :
(null)
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
index 88876471c..010beb836 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
@@ -24,11 +24,13 @@
.formatShapePane-inputBtn {
width: inherit;
- position: absolute;
+ position: absolute;
}
.sketch-picker {
background: #323232;
+ width: 160px !important;
+ height: 80% !important;
.flexbox-fit {
background: #323232;
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 4e328d838..cd5ca7fd4 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -12,6 +12,8 @@ import { SelectionManager } from "../../../util/SelectionManager";
import AntimodeMenu from "../../AntimodeMenu";
import "./FormatShapePane.scss";
import { undoBatch } from "../../../util/UndoManager";
+import { ColorState, SketchPicker } from 'react-color';
+import { DocumentView } from "../../../views/nodes/DocumentView"
@observer
export default class FormatShapePane extends AntimodeMenu {
@@ -20,14 +22,16 @@ export default class FormatShapePane extends AntimodeMenu {
private _lastFill = "#D0021B";
private _lastLine = "#D0021B";
private _lastDash = "2";
- private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF"];
private _mode = ["fill-drip", "ruler-combined"];
- @observable private _subOpen = [false, false, false, false];
+ @observable private _subOpen = [false, false];
@observable private _currMode = "fill-drip";
- @observable private _lock = false;
+ @observable _lock = false;
@observable private _fillBtn = false;
@observable private _lineBtn = false;
+ @observable _controlBtn = false;
+ @observable private _controlPoints: { X: number, Y: number }[] = [];
+ @observable _currPoint = -1;
getField(key: string) {
return this.selectedInk?.reduce((p, i) =>
@@ -101,19 +105,58 @@ export default class FormatShapePane extends AntimodeMenu {
upDownButtons = (dirs: string, field: string) => {
switch (field) {
case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
+ // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break;
case "Xps": this.selectedInk?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break;
case "Yps": this.selectedInk?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break;
case "stk": this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
case "wid": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+ //redraw points
const oldWidth = NumCast(i.rootDoc._width);
+ const oldHeight = NumCast(i.rootDoc._height);
+ const oldX = NumCast(i.rootDoc.x);
+ const oldY = NumCast(i.rootDoc.y);
i.rootDoc._width = oldWidth + (dirs === "up" ? 10 : - 10);
this._lock && (i.rootDoc._height = (i.rootDoc._width / oldWidth * NumCast(i.rootDoc._height)));
+ const doc = Document(i.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
+ console.log(doc.x, doc.y, doc._height, doc._width);
+ const ink = Cast(doc.data, InkField)?.inkData;
+ console.log(ink);
+ if (ink) {
+ const newPoints: { X: number, Y: number }[] = [];
+ for (var j = 0; j < ink.length; j++) {
+ // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+ const newX = (doc.x - oldX) + (ink[j].X * doc._width) / oldWidth;
+ const newY = (doc.y - oldY) + (ink[j].Y * doc._height) / oldHeight;
+ newPoints.push({ X: newX, Y: newY });
+ }
+ doc.data = new InkField(newPoints);
+ }
+ }
});
break;
case "hgt": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+ const oldWidth = NumCast(i.rootDoc._width);
const oldHeight = NumCast(i.rootDoc._height);
- i.rootDoc._height = oldHeight + (dirs === "up" ? 10 : - 10);
+ const oldX = NumCast(i.rootDoc.x);
+ const oldY = NumCast(i.rootDoc.y); i.rootDoc._height = oldHeight + (dirs === "up" ? 10 : - 10);
this._lock && (i.rootDoc._width = (i.rootDoc._height / oldHeight * NumCast(i.rootDoc._width)));
+ const doc = Document(i.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
+ console.log(doc.x, doc.y, doc._height, doc._width);
+ const ink = Cast(doc.data, InkField)?.inkData;
+ console.log(ink);
+ if (ink) {
+ const newPoints: { X: number, Y: number }[] = [];
+ for (var j = 0; j < ink.length; j++) {
+ // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+ const newX = (doc.x - oldX) + (ink[j].X * doc._width) / oldWidth;
+ const newY = (doc.y - oldY) + (ink[j].Y * doc._height) / oldHeight;
+ newPoints.push({ X: newX, Y: newY });
+ }
+ doc.data = new InkField(newPoints);
+ }
+ }
});
break;
}
@@ -121,12 +164,11 @@ export default class FormatShapePane extends AntimodeMenu {
@undoBatch
@action
- rotate = (degrees: number) => {
- this.selectedInk?.forEach(action(inkView => {
+ rotate = (angle: number) => {
+ const _centerPoints: { X: number, Y: number }[] = [];
+ SelectionManager.SelectedDocuments().forEach(action(inkView => {
const doc = Document(inkView.rootDoc);
if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- const angle = Number(degrees) - Number(doc.rotation);
- doc.rotation = Number(degrees);
const ink = Cast(doc.data, InkField)?.inkData;
if (ink) {
const xs = ink.map(p => p.X);
@@ -135,143 +177,280 @@ export default class FormatShapePane extends AntimodeMenu {
const top = Math.min(...ys);
const right = Math.max(...xs);
const bottom = Math.max(...ys);
- const _centerPoints: { X: number, Y: number }[] = [];
_centerPoints.push({ X: left, Y: top });
+ }
+ }
+ }));
+
+ var index = 0;
+ SelectionManager.SelectedDocuments().forEach(action(inkView => {
+ const doc = Document(inkView.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ doc.rotation = Number(doc.rotation) + Number(angle);
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
const newPoints: { X: number, Y: number }[] = [];
for (var i = 0; i < ink.length; i++) {
- const newX = Math.cos(angle) * (ink[i].X - _centerPoints[0].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[0].Y) + _centerPoints[0].X;
- const newY = Math.sin(angle) * (ink[i].X - _centerPoints[0].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[0].Y) + _centerPoints[0].Y;
+ const newX = Math.cos(angle) * (ink[i].X - _centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].X;
+ const newY = Math.sin(angle) * (ink[i].X - _centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[index].Y) + _centerPoints[index].Y;
newPoints.push({ X: newX, Y: newY });
}
doc.data = new InkField(newPoints);
- const xs2 = newPoints.map(p => p.X);
- const ys2 = newPoints.map(p => p.Y);
- const left2 = Math.min(...xs2);
- const top2 = Math.min(...ys2);
- const right2 = Math.max(...xs2);
- const bottom2 = Math.max(...ys2);
- doc._height = (bottom2 - top2) * inkView.props.ScreenToLocalTransform().Scale;
- doc._width = (right2 - left2) * inkView.props.ScreenToLocalTransform().Scale;
+ const xs = newPoints.map(p => p.X);
+ const ys = newPoints.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+
+ doc._height = (bottom - top);
+ doc._width = (right - left);
}
+ index++;
}
}));
}
+ @undoBatch
+ @action
+ control = (xDiff: number, yDiff: number, controlNum: number) => {
+ this.selectedInk?.forEach(action(inkView => {
+ if (this.selectedInk?.length === 1) {
+ const doc = Document(inkView.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+
+ const newPoints: { X: number, Y: number }[] = [];
+ const order = controlNum % 4;
+ for (var i = 0; i < ink.length; i++) {
+ if (controlNum === i ||
+ (order === 0 && i === controlNum + 1) ||
+ (order === 0 && controlNum !== 0 && i === controlNum - 2) ||
+ (order === 0 && controlNum !== 0 && i === controlNum - 1) ||
+ (order === 3 && i === controlNum - 1) ||
+ (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) ||
+ (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2)
+ || ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1))
+ ) {
+ newPoints.push({ X: ink[i].X - (xDiff * inkView.props.ScreenToLocalTransform().Scale), Y: ink[i].Y - (yDiff * inkView.props.ScreenToLocalTransform().Scale) });
+ }
+ else {
+ newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
+ }
+ const oldx = doc.x;
+ const oldy = doc.y;
+ const xs = ink.map(p => p.X);
+ const ys = ink.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ doc.data = new InkField(newPoints);
+ const xs2 = newPoints.map(p => p.X);
+ const ys2 = newPoints.map(p => p.Y);
+ const left2 = Math.min(...xs2);
+ const top2 = Math.min(...ys2);
+ const right2 = Math.max(...xs2);
+ const bottom2 = Math.max(...ys2);
+ doc._height = (bottom2 - top2);
+ doc._width = (right2 - left2);
+ //if points move out of bounds
- colorPicker(setter: (color: string) => {}) {
- return <div className="btn-group-palette" key="colorpicker" >
- {this._palette.map(color =>
- <button className="antimodeMenu-button" key={color} onPointerDown={undoBatch(action(() => setter(color)))} style={{ zIndex: 1001, position: "relative" }}>
- <div className="color-previewII" style={{ backgroundColor: color }} />
- </button>)}
+ doc.x = oldx - (left - left2);
+ doc.y = oldy - (top - top2);
+
+ }
+ }
+ }
+ }));
+ }
+
+ @undoBatch
+ @action
+ switchStk = (color: ColorState) => {
+ const val = String(color.hex);
+ this.colorStk = val;
+ return true;
+ }
+
+ @undoBatch
+ @action
+ switchFil = (color: ColorState) => {
+ const val = String(color.hex);
+ this.colorFil = val;
+ return true;
+ }
+
+
+ colorPicker(setter: (color: string) => {}, type: string) {
+ return <div className="btn-group-palette" key="colorpicker" style={{ width: 160, margin: 10 }}>
+ <SketchPicker onChange={type === "stk" ? this.switchStk : this.switchFil} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+ color={type === "stk" ? this.colorStk : this.colorFil} />
</div>;
}
inputBox = (key: string, value: any, setter: (val: string) => {}) => {
return <>
- <input style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+ <input style={{ color: "black", width: 40, position: "absolute", right: 20 }}
type="text" value={value}
- onChange={e => setter(e.target.value)}
+ onChange={undoBatch(action((e) => setter(e.target.value)))}
autoFocus />
- <button className="antiMenu-Buttonup" key="up" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))}>
+ <button className="antiMenu-Buttonup" key="up1" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))}>
˄
</button>
<br />
- <button className="antiMenu-Buttonup" key="down" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} style={{ marginTop: -8 }}>
+ <button className="antiMenu-Buttonup" key="down1" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} style={{ marginTop: -8 }}>
˅
</button>
</>;
}
+ inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => {
+ return <>
+ {title1}
+ <p style={{ marginTop: -20, right: 70, position: "absolute" }}>{title2}</p>
+
+ <input style={{ color: "black", width: 40, position: "absolute", right: 130 }}
+ type="text" value={value}
+ onChange={e => setter(e.target.value)}
+ autoFocus />
+ <button className="antiMenu-Buttonup" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} style={{ right: 110 }}>
+ ˄
+ </button>
+ <button className="antiMenu-Buttonup" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} style={{ marginTop: 12, right: 110 }}>
+ ˅
+ </button>
+ {title2 === "" ? "" : <>
+ <input style={{ color: "black", width: 40, position: "absolute", right: 20 }}
+ type="text" value={value2}
+ onChange={e => setter2(e.target.value)}
+ autoFocus />
+ <button className="antiMenu-Buttonup" key="up3" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key2)))}>
+ ˄
+ </button>
+ <br />
+ <button className="antiMenu-Buttonup" key="down3" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key2)))} style={{ marginTop: -8 }}>
+ ˅
+ </button></>}
+ </>;
+ }
+
+
colorButton(value: string, setter: () => {}) {
return <>
- <button className="antimodeMenu-button" key="fill" onPointerDown={undoBatch(action(e => setter()))} style={{ position: "absolute", right: 80 }}>
- <FontAwesomeIcon icon="fill-drip" size="lg" />
- <div className="color-previewI" style={{ backgroundColor: value ?? "121212" }} />
+ <button className="antimodeMenu-button" key="fill" onPointerDown={undoBatch(action(e => setter()))} style={{ position: "relative", marginTop: -5 }}>
+ <div className="color-previewII" style={{ backgroundColor: value ?? "121212" }} />
+ {value === "" || value === "transparent" ? <p style={{ fontSize: 25, color: "red", marginTop: -23, position: "fixed" }}>☒</p> : ""}
+ </button>
+ </>;
+ }
+
+ controlPointsButton() {
+ return <>
+ <button className="antimodeMenu-button" title="Edit points" key="fill" onPointerDown={action(() => this._controlBtn = this._controlBtn ? false : true)} style={{ position: "relative", marginTop: 10, backgroundColor: this._controlBtn ? "black" : "" }}>
+ <FontAwesomeIcon icon="bezier-curve" size="lg" />
+ </button>
+ <button className="antimodeMenu-button" title="Lock ratio" key="fill" onPointerDown={action(() => this._lock = this._lock ? false : true)} style={{ position: "relative", marginTop: 10, backgroundColor: this._lock ? "black" : "" }}>
+ <FontAwesomeIcon icon="lock" size="lg" />
+
+ </button>
+ <button className="antimodeMenu-button" key="fill" title="Rotate 90˚" onPointerDown={action(() => this.rotate(Math.PI / 2))} style={{ position: "relative", marginTop: 10, fontSize: 15 }}>
+ ⟲
</button>
<br /> <br />
</>;
}
- @computed get fillButton() { return this.colorButton(this.colorFil, () => this._fillBtn = !this._fillBtn); }
- @computed get lineButton() { return this.colorButton(this.colorStk, () => this._lineBtn = !this._lineBtn); }
+ lockRatioButton() {
+ return <>
+ <button className="antimodeMenu-button" key="fill" onPointerDown={action(() => this._lock = this._lock ? false : true)} style={{ position: "absolute", right: 80, backgroundColor: this._lock ? "black" : "" }}>
+ {/* <FontAwesomeIcon icon="bezier-curve" size="lg" /> */}
+ <FontAwesomeIcon icon="lock" size="lg" />
+
+ </button>
+ <br /> <br />
+ </>;
+ }
- @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color); }
- @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color); }
+ rotate90Button() {
+ return <>
+ <button className="antimodeMenu-button" key="fill" onPointerDown={action(() => this.rotate(Math.PI / 2))} style={{ position: "absolute", right: 80, }}>
+ {/* <FontAwesomeIcon icon="bezier-curve" size="lg" /> */}
+ ⟲
+
+ </button>
+ <br /> <br />
+ </>;
+ }
+ @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); }
+ @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); }
+
+ @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); }
+ @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); }
@computed get stkInput() { return this.inputBox("stk", this.widthStk, (val: string) => this.widthStk = val); }
- @computed get hgtInput() { return this.inputBox("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val); }
+ @computed get dashInput() { return this.inputBox("dsh", this.widthStk, (val: string) => this.widthStk = val); }
+
+ @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); }
@computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); }
- @computed get rotInput() { return this.inputBox("rot", this.shapeRot, (val: string) => this.shapeRot = val); }
- @computed get XpsInput() { return this.inputBox("Xps", this.shapeXps, (val: string) => this.shapeXps = val); }
+ @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); }
+
+ @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); }
@computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); }
- @computed get propertyGroupItems() {
- const fillCheck = <div key="fill" style={{ display: this._subOpen[0] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
- <input className="formatShapePane-inputBtn" type="radio" checked={this.unFilled} onChange={undoBatch(action(() => this.unFilled = true))} />
- No Fill
- <br />
- <input className="formatShapePane-inputBtn" type="radio" checked={this.solidFil} onChange={undoBatch(action(() => this.solidFil = true))} />
- Solid Fill
- <br /> <br />
- {this.solidFil ? "Color" : ""}
- {this.solidFil ? this.fillButton : ""}
- {this._fillBtn && this.solidFil ? this.fillPicker : ""}
- </div>;
+ @computed get controlPoints() { return this.controlPointsButton(); }
+ @computed get lockRatio() { return this.lockRatioButton(); }
+ @computed get rotate90() { return this.rotate90Button(); }
- const markers = <>
- <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.markHead !== ""} onChange={undoBatch(action(() => this.markHead = this.markHead ? "" : "arrow"))} />
- Arrow Head
- <br />
- <input key="markTail" className="formatShapePane-inputBtn" type="checkbox" checked={this.markTail !== ""} onChange={undoBatch(action(() => this.markTail = this.markTail ? "" : "arrow"))} />
- Arrow End
- <br />
- </>;
- const lineCheck = <div key="lineCheck" style={{ display: this._subOpen[1] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
- <input className="formatShapePane-inputBtn" type="radio" checked={this.unStrokd} onChange={undoBatch(action(() => this.unStrokd = true))} />
- No Line
- <br />
- <input className="formatShapePane-inputBtn" type="radio" checked={this.solidStk} onChange={undoBatch(action(() => this.solidStk = true))} />
- Solid Line
- <br />
- <input className="formatShapePane-inputBtn" type="radio" checked={this.dashdStk ? true : false} onChange={undoBatch(action(() => this.dashdStk = "2"))} />
- Dash Line
- <br />
- <br />
- {(this.solidStk || this.dashdStk) ? "Color" : ""}
- {(this.solidStk || this.dashdStk) ? this.lineButton : ""}
- {(this.solidStk || this.dashdStk) && this._lineBtn ? this.linePicker : ""}
- <br />
+ @computed get propertyGroupItems() {
+ const fillCheck = <div key="fill" style={{ display: (this._subOpen[0] && this.selectedInk && this.selectedInk.length >= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
+ Fill:
+ {this.fillButton}
+ <div style={{ float: "left", width: 100 }} >
+ Stroke:
+ {this.lineButton}
+ </div>
+
+ {this._fillBtn ? this.fillPicker : ""}
+ {this._lineBtn ? this.linePicker : ""}
+ {this._fillBtn || this._lineBtn ? "" : <br />}
{(this.solidStk || this.dashdStk) ? "Width" : ""}
{(this.solidStk || this.dashdStk) ? this.stkInput : ""}
- {(this.solidStk || this.dashdStk) ? <input type="range" defaultValue={Number(this.widthStk)} min={1} max={100} onChange={e => this.widthStk = e.target.value} /> : (null)}
- <br /> <br />
- {(this.solidStk || this.dashdStk) ? markers : ""}
- </div>;
- const sizeCheck = <div key="sizeCheck" style={{ display: this._subOpen[2] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
- Height {this.hgtInput}
- <br /> <br />
- Width {this.widInput}
- <br /> <br />
- <input className="formatShapePane-inputBtn" style={{ right: 0 }} type="checkbox" checked={this._lock} onChange={undoBatch(action(() => this._lock = !this._lock))} />
- Lock Ratio
- <br /> <br />
- Rotation {this.rotInput}
- <br /> <br />
- </div>;
- const positionCheck = <div key="posCheck" style={{ display: this._subOpen[3] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
- Horizontal {this.XpsInput}
- <br /> <br />
- Vertical {this.YpsInput}
- <br /> <br />
+ {(this.solidStk || this.dashdStk) ? <input type="range" defaultValue={Number(this.widthStk)} min={1} max={100} onChange={undoBatch(action((e) => this.widthStk = e.target.value))} /> : (null)}
+ <br />
+ {(this.solidStk || this.dashdStk) ? <>
+ <p style={{ position: "absolute", fontSize: 12 }}>Arrow Head</p>
+ <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.markHead !== ""} onChange={undoBatch(action(() => this.markHead = this.markHead ? "" : "arrow"))} style={{ position: "absolute", right: 110, width: 20 }} />
+ <p style={{ position: "absolute", fontSize: 12, right: 30 }}>Arrow End</p>
+ <input key="markTail" className="formatShapePane-inputBtn" type="checkbox" checked={this.markTail !== ""} onChange={undoBatch(action(() => this.markTail = this.markTail ? "" : "arrow"))} style={{ position: "absolute", right: 0, width: 20 }} />
+ <br />
+ </> : ""}
+ Dash: <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.dashdStk === "2"} onChange={undoBatch(action(() => this.dashdStk = this.dashdStk === "2" ? "0" : "2"))} style={{ position: "absolute", right: 110, width: 20 }} />
+
+
+
</div>;
- const subMenus = this._currMode === "fill-drip" ? [`fill`, `line`] : [`size`, `position`];
- const menuItems = this._currMode === "fill-drip" ? [fillCheck, lineCheck] : [sizeCheck, positionCheck];
- const indexOffset = this._currMode === "fill-drip" ? 0 : 2;
+
+
+ const sizeCheck =
+
+ <div key="sizeCheck" style={{ display: (this._subOpen[1] && this.selectedInk && this.selectedInk.length >= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
+ {this.controlPoints}
+ {this.hgtInput}
+ {this.XpsInput}
+ {this.rotInput}
+
+ </div>;
+
+
+ const subMenus = this._currMode === "fill-drip" ? [`Appearance`, 'Transform'] : [];
+ const menuItems = this._currMode === "fill-drip" ? [fillCheck, sizeCheck] : [];
+ const indexOffset = 0;
+
return <div className="antimodeMenu-sub" key="submenu" style={{ position: "absolute", width: "inherit", top: 60 }}>
{subMenus.map((subMenu, i) =>
<div key={subMenu} style={{ width: "inherit" }}>
@@ -302,6 +481,7 @@ export default class FormatShapePane extends AntimodeMenu {
}
render() {
- return this.getElementVert([this.closeBtn, this.propertyGroupBtn, this.propertyGroupItems]);
+ return this.getElementVert([this.closeBtn,
+ this.propertyGroupItems]);
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 57028b0ca..b186d9ffc 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -54,27 +54,30 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
}
render() {
const selDoc = SelectionManager.SelectedDocuments()?.[0]?.rootDoc;
- return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`}
- onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
- style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+ // return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`}
+ // onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
+ // style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
- <SketchPicker onChange={ColorBox.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
- color={StrCast(ActiveInkPen()?.backgroundColor,
- StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} />
- <div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}>
- <div> {ActiveInkWidth() ?? 2}</div>
- <input type="range" defaultValue={ActiveInkWidth() ?? 2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- SetActiveInkWidth(e.target.value);
- SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value));
- }} />
- <div> {ActiveInkBezierApprox() ?? 2}</div>
- <input type="range" defaultValue={ActiveInkBezierApprox() ?? 2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- SetActiveBezierApprox(e.target.value);
- SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeBezier = e.target.value);
- }} />
- <br />
- <br />
- </div>
- </div>;
+ // <SketchPicker onChange={ColorBox.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+ // color={StrCast(ActiveInkPen()?.backgroundColor,
+ // StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} />
+
+ // <div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}>
+ // <div> {ActiveInkWidth() ?? 2}</div>
+ // <input type="range" defaultValue={ActiveInkWidth() ?? 2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+ // SetActiveInkWidth(e.target.value);
+ // SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value));
+ // }} />
+ // <div> {ActiveInkBezierApprox() ?? 2}</div>
+ // <input type="range" defaultValue={ActiveInkBezierApprox() ?? 2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+ // SetActiveBezierApprox(e.target.value);
+ // SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeBezier = e.target.value);
+ // }} />
+ // <br />
+ // <br />
+ // </div>
+ // </div>
+ // ;
+ return <></>;
}
}