aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBob Zeleznik <zzzman@gmail.com>2020-07-11 21:09:41 -0400
committerBob Zeleznik <zzzman@gmail.com>2020-07-11 21:09:41 -0400
commitcb837fde6b9219b68534717a29133e787cebe943 (patch)
tree4a5c2769cc12acdc8c9fe34890e25452b702173d /src
parent6cd4bbc78542cc81fb56decf223033fb0931aeb9 (diff)
parentff642c9f9846a182e7ca8893b2ca414bb8ce480f (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/HypothesisAuthenticationManager.tsx2
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/util/CurrentUserUtils.ts6
-rw-r--r--src/client/util/InteractionUtils.tsx15
-rw-r--r--src/client/util/SelectionManager.ts1
-rw-r--r--src/client/views/AntimodeMenu.tsx24
-rw-r--r--src/client/views/DocumentDecorations.tsx5
-rw-r--r--src/client/views/GestureOverlay.tsx23
-rw-r--r--src/client/views/InkingStroke.tsx34
-rw-r--r--src/client/views/MainView.tsx21
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.scss59
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.tsx361
-rw-r--r--src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss7
-rw-r--r--src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx320
-rw-r--r--src/client/views/nodes/ColorBox.tsx12
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
16 files changed, 746 insertions, 149 deletions
diff --git a/src/client/apis/HypothesisAuthenticationManager.tsx b/src/client/apis/HypothesisAuthenticationManager.tsx
index cffb87227..a7fcf86a4 100644
--- a/src/client/apis/HypothesisAuthenticationManager.tsx
+++ b/src/client/apis/HypothesisAuthenticationManager.tsx
@@ -34,7 +34,7 @@ export default class HypothesisAuthenticationManager extends React.Component<{}>
}
public fetchAccessToken = async (displayIfFound = false) => {
- let response: any = await Networking.FetchFromServer("/readHypothesisAccessToken");
+ const response: any = await Networking.FetchFromServer("/readHypothesisAccessToken");
// if this is an authentication url, activate the UI to register the new access token
if (!response) { // new RegExp(AuthenticationUrl).test(response)) {
this.isOpen = true;
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fa85d58f0..26abd4c3c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -671,7 +671,7 @@ export namespace Docs {
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString("data");
I.color = color;
- I.strokeWidth = strokeWidth;
+ I.strokeWidth = Number(strokeWidth);
I.strokeBezier = strokeBezier;
I.fillColor = fillColor;
I.arrowStart = arrowStart;
@@ -685,6 +685,7 @@ export namespace Docs {
I._width = options._width;
I._height = options._height;
I.author = Doc.CurrentUserEmail;
+ I.rotation = 0;
I.data = new InkField(points);
return I;
// return I;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 8099228c6..ce910c062 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -820,9 +820,9 @@ export class CurrentUserUtils {
doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
doc.activeInkWidth = StrCast(doc.activeInkWidth, "1");
doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
- doc.activeFillColor = StrCast(doc.activeFillColor, "none");
- doc.activeArrowStart = StrCast(doc.activeArrowStart, "none");
- doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "none");
+ doc.activeFillColor = StrCast(doc.activeFillColor, "");
+ doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
+ doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "");
doc.activeDash = StrCast(doc.activeDash, "0");
doc.fontSize = NumCast(doc.fontSize, 12);
doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); //
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 02b444cd3..07adbb8b1 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -94,6 +94,7 @@ export namespace InteractionUtils {
export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number,
color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string,
dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
+
let pts: { X: number; Y: number; }[] = [];
if (shape) { //if any of the shape are true
pts = makePolygon(shape, points);
@@ -119,15 +120,15 @@ export namespace InteractionUtils {
const dashArray = String(Number(width) * Number(dash));
const defGuid = Utils.GenerateGuid();
const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth)));
- return (<svg fill={fill === "none" ? color : fill}> {/* setting the svg fill sets the arrowhead fill */}
+ return (<svg fill={color}> {/* setting the svg fill sets the arrowStart fill */}
{nodefs ? (null) : <defs>
{arrowStart !== "dot" && arrowEnd !== "dot" ? (null) : <marker id={`dot${defGuid}`} orient="auto" overflow="visible">
<circle r={1} fill="context-stroke" />
</marker>}
- {arrowStart !== "arrowHead" && arrowEnd !== "arrowHead" ? (null) : <marker id={`arrowHead${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+ {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowStart${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
<polygon points={`${arrowDim} ${-Math.max(1, arrowDim / 2)}, ${arrowDim} ${Math.max(1, arrowDim / 2)}, -1 0`} />
</marker>}
- {arrowStart !== "arrowEnd" && arrowEnd !== "arrowEnd" ? (null) : <marker id={`arrowEnd${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+ {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowEnd${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
<polygon points={`${2 - arrowDim} ${-Math.max(1, arrowDim / 2)}, ${2 - arrowDim} ${Math.max(1, arrowDim / 2)}, 3 0`} />
</marker>}
</defs>}
@@ -136,7 +137,7 @@ export namespace InteractionUtils {
points={strpts}
style={{
filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
- fill,
+ fill: fill ? fill : "transparent",
opacity: strokeWidth !== width ? 0.5 : undefined,
pointerEvents: pevents as any,
stroke: color ?? "rgb(0, 0, 0)",
@@ -145,8 +146,8 @@ export namespace InteractionUtils {
strokeLinecap: "round",
strokeDasharray: dashArray
}}
- markerStart={`url(#${arrowStart + defGuid})`}
- markerEnd={`url(#${arrowEnd + defGuid})`}
+ markerStart={`url(#${arrowStart + "Start" + defGuid})`}
+ markerEnd={`url(#${arrowEnd + "End" + defGuid})`}
/>
</svg>);
@@ -155,7 +156,7 @@ export namespace InteractionUtils {
// export function makeArrow() {
// return (
// InkOptionsMenu.Instance.getColors().map(color => {
- // const id1 = "arrowHeadTest" + color;
+ // const id1 = "arrowStartTest" + color;
// console.log(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} />
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 024532f90..9a968aeda 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -14,6 +14,7 @@ export namespace SelectionManager {
SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
@action
SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
+
// if doc is not in SelectedDocuments, add it
if (!manager.SelectedDocuments.get(docView)) {
if (!ctrlPressed) {
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index cb293dee4..68ccefcb5 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -134,13 +134,35 @@ export default abstract class AntimodeMenu extends React.Component {
protected getElement(buttons: JSX.Element[]) {
return (
<div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
- style={{ left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay }}>
+ style={{
+ left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay,
+ position: this.Pinned ? "unset" : undefined
+ }}>
<div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />
{buttons}
</div>
);
}
+ protected getElementVert(buttons: JSX.Element[]) {
+ return (
+ <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+ style={{
+ left: this.Pinned ? undefined : this._left,
+ top: this.Pinned ? 0 : this._top,
+ right: this.Pinned ? 0 : undefined,
+ height: "inherit",
+ width: 200,
+ opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay,
+ position: this.Pinned ? "absolute" : undefined
+ }}>
+ {buttons}
+ </div>
+ );
+ }
+
+
+
protected getElementWithRows(rows: JSX.Element[], numRows: number, hasDragger: boolean = true) {
return (
<div className="antimodeMenu-cont with-rows" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index a45ef8862..0bf4814e7 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -152,8 +152,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (e.button === 0 && !e.altKey && !e.ctrlKey) {
let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0];
while (child.children.length) {
- const next = Array.from(child.children).find(c => !c.className.includes("collectionViewChrome"));
- if (next?.className.includes("documentView-node")) break;
+ const next = Array.from(child.children).find(c => typeof (c.className) !== "string" || !c.className.includes("collectionViewChrome"));
+ if (typeof (next?.className) === "string" && next?.className.includes("documentView-node")) break;
if (next) child = next;
else break;
}
@@ -293,6 +293,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
const doc = Document(element.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) {
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index cdc468066..fc8530811 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -24,6 +24,7 @@ import HorizontalPalette from "./Palette";
import { Touchable } from "./Touchable";
import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
import HeightLabel from "./collections/collectionMulticolumn/MultirowHeightLabel";
+import InkOptionsMenu from "./collections/collectionFreeForm/InkOptionsMenu";
@observer
export default class GestureOverlay extends Touchable {
@@ -623,7 +624,8 @@ export default class GestureOverlay extends Touchable {
this.makePolygon(this.InkShape, false);
this.dispatchGesture(GestureUtils.Gestures.Stroke);
this._points = [];
- if (this.InkShape !== "noRec") {
+ if (InkOptionsMenu.Instance._double === "") {
+
this.InkShape = "";
}
}
@@ -675,12 +677,19 @@ export default class GestureOverlay extends Touchable {
} else {
this._points = [];
}
- SetActiveArrowStart("none");
- GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart();
- SetActiveArrowEnd("none");
- GestureOverlay.Instance.SavedArrowEnd = ActiveArrowEnd();
+ //get out of ink mode after each stroke=
+ console.log("now");
+ if (InkOptionsMenu.Instance._double === "") {
+ Doc.SetSelectedTool(InkTool.None);
+ InkOptionsMenu.Instance._selected = InkOptionsMenu.Instance._shapesNum;
+ SetActiveArrowStart("none");
+ GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart();
+ SetActiveArrowEnd("none");
+ GestureOverlay.Instance.SavedArrowEnd = ActiveArrowEnd();
+ }
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
+
}
makePolygon = (shape: string, gesture: boolean) => {
@@ -876,7 +885,9 @@ export default class GestureOverlay extends Touchable {
render() {
return (
- <div className="gestureOverlay-cont" onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
+
+ <div className="gestureOverlay-cont" style={{ position: "relative" }}
+ onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
{this.showMobileInkOverlay ? <MobileInkOverlay /> : <></>}
{this.elements}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 7d6e7e5dd..d7f956e4f 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -15,6 +15,8 @@ import { FieldView, FieldViewProps } from "./nodes/FieldView";
import React = require("react");
import { Scripting } from "../util/Scripting";
import { Doc } from "../../fields/Doc";
+import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
+import { action } from "mobx";
library.add(faPaintBrush);
@@ -38,10 +40,16 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
this.props.Document.isInkMask = true;
}
+ @action
+ private formatShape = () => {
+ FormatShapePane.Instance.Pinned = true;
+ }
+
render() {
TraceMobx();
const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
- const strokeWidth = Number(StrCast(this.layoutDoc.strokeWidth, ActiveInkWidth()));
+ // const strokeWidth = Number(StrCast(this.layoutDoc.strokeWidth, ActiveInkWidth()));
+ const strokeWidth = Number(this.layoutDoc.strokeWidth);
const xs = data.map(p => p.X);
const ys = data.map(p => p.Y);
const left = Math.min(...xs) - strokeWidth / 2;
@@ -52,14 +60,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const height = bottom - top;
const scaleX = (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth);
const scaleY = (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth);
- const strokeColor = StrCast(this.layoutDoc.color, ActiveInkColor());
+ const strokeColor = StrCast(this.layoutDoc.color, "");
const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
- StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), StrCast(this.layoutDoc.fillColor, ActiveFillColor()),
- StrCast(this.layoutDoc.arrowStart, ActiveArrowStart()), StrCast(this.layoutDoc.arrowEnd, ActiveArrowEnd()),
- StrCast(this.layoutDoc.dash, ActiveDash()), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
+ StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
+ StrCast(this.layoutDoc.strokeArrowStart), StrCast(this.layoutDoc.strokeArrowEnd),
+ 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, ActiveInkBezierApprox()), StrCast(this.layoutDoc.fillColor, ActiveFillColor()),
+ StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
"none", "none", "0", scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
return (
<svg className="inkingStroke"
@@ -72,8 +80,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
overflow: "visible",
}}
onContextMenu={() => {
- ContextMenu.Instance?.addItem({ description: "Analyze Stroke", event: this.analyzeStrokes, icon: "paint-brush" });
- ContextMenu.Instance?.addItem({ description: "Make Mask", event: this.makeMask, icon: "paint-brush" });
+ const cm = ContextMenu.Instance;
+ if (cm) {
+ cm.addItem({ description: "Analyze Stroke", event: this.analyzeStrokes, icon: "paint-brush" });
+ cm.addItem({ description: "Make Mask", event: this.makeMask, icon: "paint-brush" });
+ cm.addItem({ description: "Format Shape", event: this.formatShape, icon: "paint-brush" });
+ }
}}
><defs>
</defs>
@@ -94,9 +106,9 @@ export function SetActiveArrowEnd(value: string) { ActiveInkPen() && (ActiveInkP
export function SetActiveDash(dash: string): void { !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); }
export function ActiveInkPen(): Doc { return Cast(Doc.UserDoc().activeInkPen, Doc, null); }
export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, "black"); }
-export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, "none"); }
-export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, "none"); }
-export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, "none"); }
+export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ""); }
+export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); }
+export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); }
export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); }
export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); }
export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 81195b550..ab23a6ee9 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -6,7 +6,7 @@ import {
faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle,
faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight,
- faHeading
+ faHeading, faRulerCombined, faFillDrip
} from '@fortawesome/free-solid-svg-icons';
import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -60,9 +60,9 @@ import { DocumentManager } from '../util/DocumentManager';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { LinkMenu } from './linking/LinkMenu';
import { LinkDocPreview } from './nodes/LinkDocPreview';
-import { Fade } from '@material-ui/core';
import { LinkCreatedBox } from './nodes/LinkCreatedBox';
import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
+import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager';
@observer
@@ -147,7 +147,7 @@ export class MainView extends React.Component {
faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell,
faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight,
- faHeading);
+ faHeading, faRulerCombined, faFillDrip);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -468,7 +468,9 @@ export class MainView extends React.Component {
return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
<div className="mainView-mainContent" style={{
color: this.darkScheme ? "rgb(205,205,205)" : "black",
- height: RichTextMenu.Instance?.Pinned ? `calc(100% - ${ANTIMODEMENU_HEIGHT})` : "100%"
+ //change to times 2 for both pinned
+ height: (RichTextMenu.Instance?.Pinned || InkOptionsMenu.Instance?.Pinned) ? (RichTextMenu.Instance?.Pinned && InkOptionsMenu.Instance?.Pinned) ? `calc(100% - 2*${ANTIMODEMENU_HEIGHT})` : `calc(100% - ${ANTIMODEMENU_HEIGHT})` : "100%",
+ width: (FormatShapePane.Instance?.Pinned) ? `calc(100% - 200px)` : "100%"
}} >
<div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
<div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
@@ -607,7 +609,13 @@ export class MainView extends React.Component {
<GoogleAuthenticationManager />
<HypothesisAuthenticationManager />
<DocumentDecorations />
- <GestureOverlay>
+ <InkOptionsMenu />
+
+
+ <GestureOverlay >
+ <FormatShapePane />
+
+
<RichTextMenu key="rich" />
{this.mainContent}
</GestureOverlay>
@@ -619,10 +627,11 @@ export class MainView extends React.Component {
linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href}
addDocTab={LinkDocPreview.LinkInfo.addDocTab} /> : (null)}
<ContextMenu />
+ <FormatShapePane />
<RadialMenu />
<PDFMenu />
<MarqueeOptionsMenu />
- <InkOptionsMenu />
+
<OverlayView />
<TimelineMenu />
{this.snapLines}
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
new file mode 100644
index 000000000..7720140b9
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
@@ -0,0 +1,59 @@
+.antimodeMenu-button {
+ width: 200px;
+ position: relative;
+ text-align: left;
+
+ .color-previewI {
+ width: 100%;
+ height: 40%;
+ }
+
+ .color-previewII {
+ width: 100%;
+ height: 100%;
+ }
+}
+
+.antimenu-Buttonup {
+ position: absolute;
+ width: 20;
+ height: 10;
+ right: 0;
+ padding: 0;
+}
+
+.sketch-picker {
+ background: #323232;
+
+ .flexbox-fit {
+ background: #323232;
+ }
+}
+
+.btn-group {
+ display: grid;
+ grid-template-columns: auto auto auto auto;
+ /* Make the buttons appear below each other */
+}
+
+.btn-group-palette {
+ display: block;
+ /* Make the buttons appear below each other */
+}
+
+.btn-draw {
+ display: inline;
+ /* Make the buttons appear below each other */
+}
+
+.btn2-group {
+ display: block;
+ background: #323232;
+ grid-template-columns: auto;
+
+ /* Make the buttons appear below each other */
+ .antimodeMenu-button {
+ background: #323232;
+ display: block;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
new file mode 100644
index 000000000..77d1b646d
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -0,0 +1,361 @@
+import React = require("react");
+import AntimodeMenu from "../../AntimodeMenu";
+import { observer } from "mobx-react";
+import { observable, action, computed } from "mobx";
+import "./FormatShapePane.scss";
+import { Scripting } from "../../../util/Scripting";
+import { InkField } from "../../../../fields/InkField";
+import { Doc, Opt, Field } from "../../../../fields/Doc";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { DocumentView } from "../../../views/nodes/DocumentView";
+import { Document } from "../../../../fields/documentSchemas";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
+
+@observer
+export default class FormatShapePane extends AntimodeMenu {
+ static Instance: FormatShapePane;
+
+ 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"];
+ private _subMenu = ["fill", "line", "size", "position"];
+
+ @observable private _subOpen = [false, false, false, false];
+ @observable private _currMode: string = "fill-drip";
+ @observable private _lock = false;
+ @observable private _fillBtn = false;
+ @observable private _lineBtn = false;
+
+ getField(key: string) {
+ return this.inks?.reduce((p, i) =>
+ (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>)
+ }
+
+ @computed get inks() {
+ const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK);
+ return inks.length ? inks : undefined;
+ }
+ @computed get _noFill() { return this.inks?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; }
+ @computed get _solidFill() { return this.inks?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; }
+ @computed get _noLine() { return this.inks?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; }
+ @computed get _solidLine() { return this.inks?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; }
+ @computed get _arrowStart() { return this.getField("strokeArrowStart") || ""; }
+ @computed get _arrowEnd() { return this.getField("strokeArrowEnd") || ""; }
+ @computed get _dashLine() { return !this._noLine && this.getField("strokeDash") || ""; }
+ @computed get _currSizeHeight() { return this.getField("_height"); }
+ @computed get _currSizeWidth() { return this.getField("_width"); }
+ @computed get _currRotation() { return this.getField("rotation"); }
+ @computed get _currXpos() { return this.getField("x"); }
+ @computed get _currYpos() { return this.getField("y"); }
+ @computed get _currStrokeWidth() { return this.getField("strokeWidth"); }
+ @computed get _currFill() { const cfill = this.getField("fillColor") || ""; cfill && (this._lastFill = cfill); return cfill; }
+ @computed get _currColor() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
+ set _noFill(value) { this._currFill = value ? "" : this._lastFill; }
+ set _solidFill(value) { this._noFill = !value; }
+ set _currFill(value) { value && (this._lastFill = value); this.inks?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); }
+ set _currColor(value) { value && (this._lastLine = value); this.inks?.forEach(i => i.rootDoc.color = value ? value : undefined); }
+ set _arrowStart(value) { this.inks?.forEach(i => i.rootDoc.strokeArrowStart = value); }
+ set _arrowEnd(value) { this.inks?.forEach(i => i.rootDoc.strokeArrowEnd = value); }
+ set _noLine(value) { this._currColor = value ? "" : this._lastLine; }
+ set _solidLine(value) { this._dashLine = ""; this._noLine = !value; }
+ set _dashLine(value) {
+ value && (this._lastDash = value) && (this._noLine = false);
+ this.inks?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined);
+ }
+ set _currXpos(value) { this.inks?.forEach(i => i.rootDoc.x = Number(value)); }
+ set _currYpos(value) { this.inks?.forEach(i => i.rootDoc.y = Number(value)); }
+ set _currRotation(value) { this.inks?.forEach(i => i.rootDoc.rotation = Number(value)); }
+ set _currStrokeWidth(value) { this.inks?.forEach(i => i.rootDoc.strokeWidth = Number(value)); }
+ set _currSizeWidth(value) {
+ this.inks?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+ const oldWidth = NumCast(i.rootDoc._width);
+ i.rootDoc._width = Number(value);
+ this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth);
+ });
+ }
+ set _currSizeHeight(value) {
+ this.inks?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+ const oldHeight = NumCast(i.rootDoc._height);
+ i.rootDoc._height = Number(value);
+ this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight);
+ });
+ }
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+ FormatShapePane.Instance = this;
+ this._canFade = false;
+ this.Pinned = BoolCast(Doc.UserDoc()["formatShapePane-pinned"]);
+ }
+
+ @action
+ closePane = () => {
+ this.jumpTo(-300, -300);
+ this.Pinned = false;
+ }
+
+ @action
+ upDownButtons = (dirs: string, field: string) => {
+ switch (field) {
+ case "horizontal": this.inks?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break;
+ case "vertical": this.inks?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break;
+ case "rotation": this.rotate((dirs === "up" ? .1 : -.1)); break;
+ case "width": this.inks?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
+ case "sizeWidth":
+ this.inks?.forEach(i => {
+ const doc = i.rootDoc;
+ if (doc._width && doc._height) {
+ const oldWidth = NumCast(doc._width);
+ const oldHeight = NumCast(doc._height);
+ doc._width = NumCast(doc._width) + (dirs === "up" ? 10 : - 10);
+ if (this._lock) {
+ doc._height = (NumCast(doc._width) * oldHeight) / oldWidth;
+ }
+ }
+ });
+ break;
+ case "sizeHeight":
+ this.inks?.forEach(i => {
+ const doc = i.rootDoc;
+ if (doc._width && doc._height) {
+ const oldWidth = NumCast(doc._width);
+ const oldHeight = NumCast(doc._height);
+ doc._height = NumCast(doc._height) + (dirs === "up" ? 10 : - 10);
+ if (this._lock) {
+ doc._width = (NumCast(doc._height) * oldWidth) / oldHeight;
+ }
+ }
+ });
+ break;
+ }
+ }
+
+ @computed get close() {
+ return <button className="antimodeMenu-button" key="close" onPointerDown={action(() => this.closePane())} style={{ position: "absolute", right: 0 }}>
+ X
+ </button>;
+ }
+
+ //select either coor&fill or size&position
+ @computed get modes() {
+ return <div className="antimodeMenu-button-tab" key="modes">
+ {this._mode.map(mode =>
+ <button className="antimodeMenu-button" key={mode} onPointerDown={action(() => { this._currMode = mode; })}
+ style={{ backgroundColor: this._currMode === mode ? "121212" : "", position: "relative", top: 30 }}>
+ <FontAwesomeIcon icon={mode as IconProp} size="lg" />
+ </button>)}
+ </div>;
+ }
+
+ @action
+ rotate = (degrees: number) => {
+ SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+ const doc = Document(element.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);
+ const ys = ink.map(p => p.Y);
+ const left = Math.min(...xs);
+ 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 });
+
+ 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;
+ 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) * element.props.ScreenToLocalTransform().Scale;
+ doc._width = (right2 - left2) * element.props.ScreenToLocalTransform().Scale;
+ }
+ }
+ }));
+ }
+
+ @computed get subMenu() {
+ const fillCheck = <div key="fill" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+ <input id="nofill" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._noFill} onChange={action(() => this._noFill = true)} />
+ No Fill
+ <br />
+ <input id="solidfill" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._solidFill} onChange={action(() => this._solidFill = true)} />
+ Solid Fill
+ <br />
+ <br />
+ {this._solidFill ? "Color" : ""}
+ {this._solidFill ? this.fillButton : ""}
+ {this._fillBtn && this._solidFill ? this.fillPicker : ""}
+ </div>;
+
+ const arrows = <>
+ <input id="arrowStart" key="arrowstart" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowStart !== ""} onChange={action(() => this._arrowStart = this._arrowStart ? "" : "arrow")} />
+ Arrow Head
+ <br />
+ <input id="arrowEnd" key="arrowend" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowEnd !== ""} onChange={action(() => this._arrowEnd = this._arrowEnd ? "" : "arrow")} />
+ Arrow End
+ <br />
+ </>;
+
+ const lineCheck = <div key="lineCheck" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+ <input id="noLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._noLine} onChange={action(() => this._noLine = true)} />
+ No Line
+ <br />
+ <input id="solidLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._solidLine} onChange={action(() => this._solidLine = true)} />
+ Solid Line
+ <br />
+ <input id="dashLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._dashLine ? true : false} onChange={action(() => this._dashLine = "2")} />
+ Dash Line
+ <br />
+ <br />
+ {(this._solidLine || this._dashLine) ? "Color" : ""}
+ {(this._solidLine || this._dashLine) ? this.lineButton : ""}
+ {this._lineBtn && (this._solidLine || this._dashLine) ? this.linePicker : ""}
+ <br />
+ {(this._solidLine || this._dashLine) ? "Width" : ""}
+ {(this._solidLine || this._dashLine) ? this.widthInput : ""}
+ {(this._solidLine || this._dashLine) ? <input type="range" defaultValue={Number(this._currStrokeWidth || "1")} min={1} max={100} onChange={e => this._currStrokeWidth = e.target.value} /> : (null)}
+ <br />
+ <br />
+ {(this._solidLine || this._dashLine) ? arrows : ""}
+ </div>;
+
+ const sizeCheck = <div key="sizeCheck" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+ Height {this.sizeHeightInput}
+ <br />
+ <br />
+
+ Width {this.sizeWidthInput}
+ <br />
+ <br />
+
+ <input id="lock" style={{ width: "inherit", position: "absolute", right: 0 }} type="checkbox" checked={this._lock} onChange={action(() => this._lock = !this._lock)} />
+ Lock Ratio
+ <br />
+ <br />
+
+ Rotation {this.rotationInput}
+ <br />
+ <br />
+ </div>;
+
+ const positionCheck = <div key="posCheck" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+ Horizontal {this.positionHorizontalInput}
+ <br />
+ <br />
+
+ Vertical {this.positionVerticalInput}
+ <br />
+ <br />
+ </div>;
+
+ return <div className="antimodeMenu-sub" key="submenu" style={{ position: "absolute", width: "inherit", top: 60 }}>
+ {this._subMenu.map((subMenu, i) => {
+ if (subMenu === "fill" || subMenu === "line") {
+ return <div key={subMenu} style={{ width: "inherit" }}>
+ <button className="antimodeMenu-button"
+ onPointerDown={action(() => { this._subOpen[i] = this._subOpen[i] ? false : true; })}
+ style={{ backgroundColor: "121212", position: "relative", display: this._currMode === "fill-drip" ? "" : "none", width: "inherit" }}>
+ {this._subOpen[i] ? "▼" : "▶︎"}
+ {subMenu}
+ </button>
+ {this._currMode === "fill-drip" && subMenu === "fill" && this._subOpen[0] ? fillCheck : ""}
+ {this._currMode === "fill-drip" && subMenu === "line" && this._subOpen[1] ? lineCheck : ""}
+ </div>;
+ }
+ else if (subMenu === "size" || subMenu === "position") {
+ return <div key={subMenu} style={{ width: "inherit" }}>
+ <button className="antimodeMenu-button"
+ onPointerDown={action(() => { this._subOpen[i] = this._subOpen[i] ? false : true; })}
+ style={{ backgroundColor: "121212", position: "relative", display: this._currMode === "ruler-combined" ? "" : "none", width: "inherit" }}>
+ {this._subOpen[i] ? "▼" : "▶︎"}
+ {subMenu}
+ </button>
+ {this._currMode === "ruler-combined" && subMenu === "size" && this._subOpen[2] ? sizeCheck : ""}
+ {this._currMode === "ruler-combined" && subMenu === "position" && this._subOpen[3] ? positionCheck : ""}
+ </div>;
+ }
+ })}
+ </div>;
+ }
+
+ colorPicker(setter: (color: string) => {}) {
+ return <div className="btn-group-palette" key="colorpicker" >
+ {this._palette.map(color =>
+ <button className="antimodeMenu-button" key={color} onPointerDown={action(() => setter(color))} style={{ zIndex: 1001, position: "relative" }}>
+ <div className="color-previewII" style={{ backgroundColor: color }} />
+ </button>)}
+ </div>;
+ }
+ inputBox = (key: string, value: any, setter: (val: string) => {}) => {
+ return <>
+ <input style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+ type="text" value={value}
+ onChange={e => setter(e.target.value)}
+ autoFocus />
+ <button className="antiMenu-Buttonup" key="up" onPointerDown={action(() => this.upDownButtons("up", key))}>
+ ˄
+ </button>
+ <br />
+ <button className="antiMenu-Buttonup" key="down" onPointerDown={action(() => this.upDownButtons("down", key))} style={{ marginTop: -8 }}>
+ ˅
+ </button>
+ </>;
+ }
+
+ colorButton(value: string, setter: () => {}) {
+ return <>
+ <button className="antimodeMenu-button" key="fill" onPointerDown={action(e => setter())} style={{ position: "absolute", right: 80 }}>
+ <FontAwesomeIcon icon="fill-drip" size="lg" />
+ <div className="color-previewI" style={{ backgroundColor: value ?? "121212" }} />
+ </button>
+ <br></br>
+ <br></br>
+ </>;
+ }
+
+ @computed get fillButton() { return this.colorButton(this._currFill, () => this._fillBtn = !this._fillBtn); }
+ @computed get lineButton() { return this.colorButton(this._currColor, () => this._lineBtn = !this._lineBtn); }
+
+ @computed get fillPicker() { return this.colorPicker((color: string) => this._currFill = color); }
+ @computed get linePicker() { return this.colorPicker((color: string) => this._currColor = color); }
+
+ @computed get widthInput() { return this.inputBox("width", this._currStrokeWidth, (val: string) => this._currStrokeWidth = val); }
+ @computed get sizeHeightInput() { return this.inputBox("height", this._currSizeHeight, (val: string) => this._currSizeHeight = val); }
+ @computed get sizeWidthInput() { return this.inputBox("height", this._currSizeWidth, (val: string) => this._currSizeWidth = val); }
+ @computed get rotationInput() { return this.inputBox("rotation", this._currRotation, (val: string) => this._currRotation = val); }
+ @computed get positionHorizontalInput() { return this.inputBox("horizontal", this._currXpos, (val: string) => this._currXpos = val); }
+ @computed get positionVerticalInput() { return this.inputBox("vertical", this._currYpos, (val: string) => this._currYpos = val); }
+
+ render() {
+ return this.getElementVert([this.close, this.modes, this.subMenu]);
+ }
+}
+Scripting.addGlobal(function activatePen2(penBtn: any) {
+ if (penBtn) {
+ //no longer changes to inkmode
+ // Doc.SetSelectedTool(InkTool.Pen);
+ FormatShapePane.Instance.jumpTo(300, 300);
+ FormatShapePane.Instance.Pinned = true;
+ } else {
+ // Doc.SetSelectedTool(InkTool.None);
+ FormatShapePane.Instance.Pinned = false;
+ FormatShapePane.Instance.fadeOut(true);
+ }
+}); \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
index 753de6bef..03c6c97ab 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
@@ -25,8 +25,13 @@
/* Make the buttons appear below each other */
}
+.btn-draw {
+ display: inline;
+ /* Make the buttons appear below each other */
+}
+
.btn2-group {
- display: block;
+ display: grid;
background: #323232;
grid-template-columns: auto;
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index f47fca6ac..7f0bb5364 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -17,6 +17,8 @@ import { DocumentType } from "../../../documents/DocumentTypes";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faSleigh, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve, } from "@fortawesome/free-solid-svg-icons";
+import { Cast, StrCast, BoolCast } from "../../../../fields/Types";
+import FormatShapePane from "./FormatShapePane";
library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve);
@@ -26,23 +28,33 @@ library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSup
export default class InkOptionsMenu extends AntimodeMenu {
static Instance: InkOptionsMenu;
- private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", "none"];
+ private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
private _width = ["1", "5", "10", "100"];
// private _buttons = ["circle", "triangle", "rectangle", "arrow", "line"];
// private _icons = ["O", "∆", "ロ", "➜", "-"];
- private _buttons = ["circle", "triangle", "rectangle", "line", "noRec", "",];
- private _icons = ["O", "∆", "ロ", "⎯", "✖︎", " "];
+ // private _buttons = ["circle", "triangle", "rectangle", "line", "noRec", "",];
+ // private _icons = ["O", "∆", "ロ", "⎯⎯⎯", "✖︎", " "];
//arrowStart and arrowEnd must match and defs must exist in Inking Stroke
- private _arrowStart = ["arrowHead", "arrowHead", "dot", "dot", "none"];
- private _arrowEnd = ["none", "arrowEnd", "none", "dot", "none"];
- private _arrowIcons = ["→", "↔︎", "•", "••", " "];
+ // private _arrowStart = ["arrowStart", "arrowStart", "dot", "dot", "none"];
+ // private _arrowEnd = ["none", "arrowEnd", "none", "dot", "none"];
+ // private _arrowIcons = ["→", "↔︎", "•", "••", " "];
+ private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"];
+ private _head = ["", "", "arrow", "", "", "arrow", "", "", ""];
+ private _end = ["", "arrow", "arrow", "", "arrow", "arrow", "", "", ""];
+ private _shape = ["line", "line", "line", "", "", "", "rectangle", "circle", "triangle"];
+
+ @observable _shapesNum = this._shape.length;
+ @observable _selected = this._shapesNum;
+
+ @observable private collapsed: boolean = false;
+ @observable _double = "";
@observable _colorBtn = false;
@observable _widthBtn = false;
@observable _fillBtn = false;
- @observable _arrowBtn = false;
- @observable _dashBtn = false;
- @observable _shapeBtn = false;
+ // @observable _arrowBtn = false;
+ // @observable _dashBtn = false;
+ // @observable _shapeBtn = false;
@@ -50,18 +62,40 @@ export default class InkOptionsMenu extends AntimodeMenu {
super(props);
InkOptionsMenu.Instance = this;
this._canFade = false; // don't let the inking menu fade away
+ this.Pinned = BoolCast(Doc.UserDoc()["inkOptionsMenu-pinned"]);
+
}
- getColors = () => {
- return this._palette;
+ @action
+ toggleMenuPin = (e: React.MouseEvent) => {
+ Doc.UserDoc()["inkOptionsMenu-pinned"] = this.Pinned = !this.Pinned;
+ if (!this.Pinned) {
+ // this.fadeOut(true);
+ }
}
@action
- changeArrow = (arrowStart: string, arrowEnd: string) => {
- SetActiveArrowStart(arrowStart);
- SetActiveArrowEnd(arrowEnd);
+ protected toggleCollapse = (e: React.MouseEvent) => {
+ this.collapsed = !this.collapsed;
+ setTimeout(() => {
+ const x = Math.min(this._left, window.innerWidth - InkOptionsMenu.Instance.width);
+ InkOptionsMenu.Instance.jumpTo(x, this._top, true);
+ }, 0);
+ }
+
+
+
+
+ getColors = () => {
+ return this._palette;
}
+ // @action
+ // changeArrow = (arrowStart: string, arrowEnd: string) => {
+ // SetActiveArrowStart(arrowStart);
+ // SetActiveArrowEnd(arrowEnd);
+ // }
+
@action
changeColor = (color: string, type: string) => {
const col: ColorState = {
@@ -93,14 +127,8 @@ export default class InkOptionsMenu extends AntimodeMenu {
case "bezier":
// doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300;
break;
- case "arrowStart":
- doc.arrowStart = String(value);
- break;
- case "arrowEnd":
- doc.arrowEnd = String(value);
- break;
case "dash":
- doc.dash = Number(value);
+ doc.strokeDash = Number(value);
default:
break;
}
@@ -117,43 +145,103 @@ export default class InkOptionsMenu extends AntimodeMenu {
@action
changeDash = (e: React.PointerEvent): void => {
SetActiveDash(ActiveDash() === "0" ? "2" : "0");
- this.editProperties(ActiveDash(), "dash");
+ this.editProperties(ActiveDash(), "strokeDash");
}
- @computed get arrowPicker() {
- var currIcon;
- for (var i = 0; i < this._arrowStart.length; i++) {
- if (this._arrowStart[i] === ActiveArrowStart() && this._arrowEnd[i] === ActiveArrowEnd()) {
- currIcon = this._arrowIcons[i];
- if (this._arrowIcons[i] === " ") {
- currIcon = "➤";
- }
- }
- }
- var arrowPicker = <button
- className="antimodeMenu-button"
- key="arrow"
- onPointerDown={action(e => this._arrowBtn = !this._arrowBtn)}
- style={{ backgroundColor: this._arrowBtn ? "121212" : "" }}>
- {currIcon}
- </button>;
- if (this._arrowBtn) {
- arrowPicker = <div className="btn2-group" key="arrows">
- {arrowPicker}
- {this._arrowStart.map((arrowStart, i) => {
- return <button
- className="antimodeMenu-button"
- key={arrowStart}
- onPointerDown={action(() => { SetActiveArrowStart(arrowStart); SetActiveArrowEnd(this._arrowEnd[i]); this.editProperties(arrowStart, "arrowStart"), this.editProperties(this._arrowEnd[i], "arrowEnd"); this._arrowBtn = false; })}
- style={{ backgroundColor: this._arrowBtn ? "121212" : "" }}>
- {this._arrowIcons[i]}
- </button>;
- })}
- </div>;
- }
- return arrowPicker;
+ @computed get drawButtons() {
+ const drawButtons = <div className="btn-draw" key="draw">
+ {this._draw.map((icon, i) => {
+ return <button
+ className="antimodeMenu-button"
+ key={icon}
+ onPointerDown={action(() => {
+ this._double = "";
+
+ if (this._selected !== i) {
+ this._selected = i;
+ Doc.SetSelectedTool(InkTool.Pen);
+ SetActiveArrowStart(this._head[i]);
+ SetActiveArrowEnd(this._end[i]);
+ SetActiveBezierApprox("300");
+
+ GestureOverlay.Instance.InkShape = this._shape[i];
+ } else {
+ this._selected = this._shapesNum;
+ Doc.SetSelectedTool(InkTool.None);
+ SetActiveArrowStart("");
+ SetActiveArrowEnd("");
+ GestureOverlay.Instance.InkShape = "";
+ SetActiveBezierApprox("0");
+
+ }
+ console.log(this._selected);
+
+ })}
+ onDoubleClick={action(() => {
+ console.log("double");
+ this._double = this._draw[i];
+ if (this._selected !== i) {
+ this._selected = i;
+ Doc.SetSelectedTool(InkTool.Pen);
+ SetActiveArrowStart(this._head[i]);
+ SetActiveArrowEnd(this._end[i]);
+ SetActiveBezierApprox("300");
+
+ GestureOverlay.Instance.InkShape = this._shape[i];
+ } else {
+ this._selected = this._shapesNum;
+ Doc.SetSelectedTool(InkTool.None);
+ SetActiveArrowStart("");
+ SetActiveArrowEnd("");
+ GestureOverlay.Instance.InkShape = "";
+ SetActiveBezierApprox("0");
+
+ }
+
+
+
+ })}
+ style={{ backgroundColor: i === this._selected ? "121212" : "", fontSize: "20" }}>
+ {this._draw[i]}
+ </button>;
+ })}</div>;
+ return drawButtons;
}
+ // @computed get arrowPicker() {
+ // var currIcon;
+ // for (var i = 0; i < this._arrowStart.length; i++) {
+ // if (this._arrowStart[i] === ActiveArrowStart() && this._arrowEnd[i] === ActiveArrowEnd()) {
+ // currIcon = this._arrowIcons[i];
+ // if (this._arrowIcons[i] === " ") {
+ // currIcon = "➤";
+ // }
+ // }
+ // }
+ // var arrowPicker = <button
+ // className="antimodeMenu-button"
+ // key="arrow"
+ // onPointerDown={action(e => this._arrowBtn = !this._arrowBtn)}
+ // style={{ backgroundColor: this._arrowBtn ? "121212" : "" }}>
+ // {currIcon}
+ // </button>;
+ // if (this._arrowBtn) {
+ // arrowPicker = <div className="btn2-group" key="arrows">
+ // {arrowPicker}
+ // {this._arrowStart.map((arrowStart, i) => {
+ // return <button
+ // className="antimodeMenu-button"
+ // key={arrowStart}
+ // onPointerDown={action(() => { SetActiveArrowStart(arrowStart); SetActiveArrowEnd(this._arrowEnd[i]); this.editProperties(arrowStart, "arrowStart"), this.editProperties(this._arrowEnd[i], "arrowEnd"); this._arrowBtn = false; })}
+ // style={{ backgroundColor: this._arrowBtn ? "121212" : "" }}>
+ // {this._arrowIcons[i]}
+ // </button>;
+ // })}
+ // </div>;
+ // }
+ // return arrowPicker;
+ // }
+
@computed get widthPicker() {
var widthPicker = <button
className="antimodeMenu-button"
@@ -170,7 +258,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
className="antimodeMenu-button"
key={wid}
onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
- style={{ backgroundColor: this._widthBtn ? "121212" : "" }}>
+ style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001 }}>
{wid}
</button>;
})}
@@ -200,7 +288,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
className="antimodeMenu-button"
key={color}
onPointerDown={action(() => { this.changeColor(color, "color"); this._colorBtn = false; this.editProperties(color, "color"); })}
- style={{ backgroundColor: this._colorBtn ? "121212" : "" }}>
+ style={{ backgroundColor: this._colorBtn ? "121212" : "", zIndex: 1001 }}>
{/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
<div className="color-previewII" style={{ backgroundColor: color }}></div>
</button>;
@@ -209,6 +297,14 @@ export default class InkOptionsMenu extends AntimodeMenu {
}
return colorPicker;
}
+ @computed get formatPane() {
+ 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" />
+ </button>;
+ }
@computed get fillPicker() {
var fillPicker = <button
@@ -221,14 +317,14 @@ export default class InkOptionsMenu extends AntimodeMenu {
<div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? "121212" }}></div>
</button>;
if (this._fillBtn) {
- fillPicker = <div className="btn-group" key="fill">
+ fillPicker = <div className="btn-group" key="fill" >
{fillPicker}
{this._palette.map(color => {
return <button
className="antimodeMenu-button"
key={color}
onPointerDown={action(() => { this.changeColor(color, "fill"); this._fillBtn = false; this.editProperties(color, "fill"); })}
- style={{ backgroundColor: this._fillBtn ? "121212" : "" }}>
+ style={{ backgroundColor: this._fillBtn ? "121212" : "", zIndex: 1001 }}>
<div className="color-previewII" style={{ backgroundColor: color }}></div>
</button>;
})}
@@ -238,48 +334,48 @@ export default class InkOptionsMenu extends AntimodeMenu {
return fillPicker;
}
- @computed get shapePicker() {
- var currIcon;
- if (GestureOverlay.Instance.InkShape === "") {
- currIcon = <FontAwesomeIcon icon="shapes" size="lg" />;
- } else {
- for (var i = 0; i < this._icons.length; i++) {
- if (GestureOverlay.Instance.InkShape === this._buttons[i]) {
- currIcon = this._icons[i];
- }
- }
- }
- var shapePicker = <button
- className="antimodeMenu-button"
- key="shape"
- onPointerDown={action(e => this._shapeBtn = !this._shapeBtn)}
- style={{ backgroundColor: this._shapeBtn ? "121212" : "" }}>
- {currIcon}
- </button>;
- if (this._shapeBtn) {
- shapePicker = <div className="btn2-group" key="shape">
- {shapePicker}
- {this._buttons.map((btn, i) => {
- var ttl = btn;
- if (btn === "") {
- ttl = "no shape";
- }
- if (btn === "noRec") {
- ttl = "disable shape recognition";
- }
- return <button
- className="antimodeMenu-button"
- title={`Draw ${btn}`}
- key={ttl}
- onPointerDown={action((e) => { GestureOverlay.Instance.InkShape = btn; this._shapeBtn = false; })}
- style={{ backgroundColor: this._shapeBtn ? "121212" : "" }}>
- {this._icons[i]}
- </button>;
- })}
- </div>;
- }
- return shapePicker;
- }
+ // @computed get shapePicker() {
+ // var currIcon;
+ // if (GestureOverlay.Instance.InkShape === "") {
+ // currIcon = <FontAwesomeIcon icon="shapes" size="lg" />;
+ // } else {
+ // for (var i = 0; i < this._icons.length; i++) {
+ // if (GestureOverlay.Instance.InkShape === this._buttons[i]) {
+ // currIcon = this._icons[i];
+ // }
+ // }
+ // }
+ // var shapePicker = <button
+ // className="antimodeMenu-button"
+ // key="shape"
+ // onPointerDown={action(e => this._shapeBtn = !this._shapeBtn)}
+ // style={{ backgroundColor: this._shapeBtn ? "121212" : "" }}>
+ // {currIcon}
+ // </button>;
+ // if (this._shapeBtn) {
+ // shapePicker = <div className="btn2-group" key="shape">
+ // {shapePicker}
+ // {this._buttons.map((btn, i) => {
+ // var ttl = btn;
+ // if (btn === "") {
+ // ttl = "no shape";
+ // }
+ // if (btn === "noRec") {
+ // ttl = "disable shape recognition";
+ // }
+ // return <button
+ // className="antimodeMenu-button"
+ // title={`Draw ${btn}`}
+ // key={ttl}
+ // onPointerDown={action((e) => { GestureOverlay.Instance.InkShape = btn; this._shapeBtn = false; })}
+ // style={{ backgroundColor: this._shapeBtn ? "121212" : "", fontSize: "20" }}>
+ // {this._icons[i]}
+ // </button>;
+ // })}
+ // </div>;
+ // }
+ // return shapePicker;
+ // }
@computed get bezierButton() {
return <button
@@ -310,23 +406,35 @@ export default class InkOptionsMenu extends AntimodeMenu {
// <button className="antimodeMenu-button" title="Drag" key="drag" onPointerDown={e => this.dragStart(e)}>
// <FontAwesomeIcon icon="arrows-alt" size="lg" />
// </button>,
- this.shapePicker,
- this.bezierButton,
+ // this.shapePicker,
+ // this.bezierButton,
this.widthPicker,
this.colorPicker,
this.fillPicker,
- this.arrowPicker,
- this.dashButton,
+ this.drawButtons,
+ this.formatPane,
+ // this.arrowPicker,
+ // this.dashButton,
+ <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}>
+ <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
+ </button>
];
+
+ // return this.getElement(buttons);
return this.getElement(buttons);
}
}
Scripting.addGlobal(function activatePen(penBtn: any) {
if (penBtn) {
- Doc.SetSelectedTool(InkTool.Pen);
+ //no longer changes to inkmode
+ // Doc.SetSelectedTool(InkTool.Pen);
InkOptionsMenu.Instance.jumpTo(300, 300);
+ InkOptionsMenu.Instance.Pinned = true;
+
} else {
- Doc.SetSelectedTool(InkTool.None);
+ // Doc.SetSelectedTool(InkTool.None);
+ InkOptionsMenu.Instance.Pinned = false;
InkOptionsMenu.Instance.fadeOut(true);
+
}
});
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index b3a9b6fee..57028b0ca 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -14,7 +14,7 @@ import { ViewBoxBaseComponent } from "../DocComponent";
import { ActiveInkPen, ActiveInkWidth, ActiveInkBezierApprox, SetActiveInkColor, SetActiveInkWidth, SetActiveBezierApprox } from "../InkingStroke";
import "./ColorBox.scss";
import { FieldView, FieldViewProps } from './FieldView';
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
+import { DocumentType } from "../../documents/DocumentTypes";
type ColorDocument = makeInterface<[typeof documentSchema]>;
const ColorDocument = makeInterface(documentSchema);
@@ -63,9 +63,15 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
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)} />
+ <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)} />
+ <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>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 962d0012f..3c5c48c7c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1168,7 +1168,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
} else if ([this._editorView!.state.schema.nodes.ordered_list, this._editorView!.state.schema.nodes.listItem].includes(node?.type) &&
node !== (this._editorView!.state.selection as NodeSelection)?.node && pcords) {
- this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos!)));
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos)));
}
}
if ((e.nativeEvent as any).formattedHandled) { e.stopPropagation(); return; }