aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanley Yip <stanley_yip@brown.edu>2020-01-13 15:25:07 -0500
committerStanley Yip <stanley_yip@brown.edu>2020-01-13 15:25:07 -0500
commitd86b4db095379d473820271868e8f7cd5830d502 (patch)
tree1bd9826f9a900d4002100f097a86249910f01451
parent36eed6cc3a3387a1f7755a848aea4e19e2645e14 (diff)
palette events are now on gestureOverlay, but cffv and dv is still capturing one and two finger events, which makes it impossible for the gestureoverlay to capture 5 fingers spread across different targets
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/util/InteractionUtils.ts10
-rw-r--r--src/client/views/GestureOverlay.tsx64
-rw-r--r--src/client/views/InkingControl.tsx5
-rw-r--r--src/client/views/Palette.scss10
-rw-r--r--src/client/views/Palette.tsx44
-rw-r--r--src/client/views/Touchable.tsx21
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx94
-rw-r--r--src/client/views/nodes/DocumentView.tsx24
-rw-r--r--src/new_fields/documentSchemas.ts2
-rw-r--r--src/server/authentication/models/current_user_utils.ts23
11 files changed, 224 insertions, 76 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1853be529..64abd4f57 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -103,6 +103,8 @@ export interface DocumentOptions {
ischecked?: ScriptField; // returns whether a font icon box is checked
activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts)
onClick?: ScriptField;
+ onPointerDown?: ScriptField;
+ onPointerUp?: ScriptField;
dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop
icon?: string;
@@ -116,6 +118,7 @@ export interface DocumentOptions {
color?: string;
limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
// [key: string]: Opt<Field>;
+ pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown
}
class EmptyBox {
diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts
index 2e4e8c7ca..76b43da3c 100644
--- a/src/client/util/InteractionUtils.ts
+++ b/src/client/util/InteractionUtils.ts
@@ -8,12 +8,14 @@ export namespace InteractionUtils {
const REACT_POINTER_PEN_BUTTON = 0;
const ERASER_BUTTON = 5;
- export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map<number, React.Touch>): React.Touch[] {
+ export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map<number, React.Touch>, ignorePen: boolean): React.Touch[] {
const myTouches = new Array<React.Touch>();
for (let i = 0; i < e.targetTouches.length; i++) {
- const pt = e.targetTouches.item(i);
+ const pt: any = e.targetTouches.item(i);
if (pt && prevPoints.has(pt.identifier)) {
- myTouches.push(pt);
+ if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) {
+ myTouches.push(pt);
+ }
}
}
return myTouches;
@@ -23,7 +25,7 @@ export namespace InteractionUtils {
switch (type) {
// pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2
case PENTYPE:
- return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? POINTER_PEN_BUTTON : REACT_POINTER_PEN_BUTTON);
+ return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
case ERASERTYPE:
return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
default:
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 848927912..830c06f1f 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { Touchable } from "./Touchable";
import { observer } from "mobx-react";
import "./GestureOverlay.scss"
-import { computed, observable, action } from "mobx";
+import { computed, observable, action, runInAction } from "mobx";
import { CreatePolyline } from "./InkingStroke";
import { GestureUtils } from "../../pen-gestures/GestureUtils";
import { InteractionUtils } from "../util/InteractionUtils";
@@ -12,14 +12,22 @@ import { Doc } from "../../new_fields/Doc";
import { LinkManager } from "../util/LinkManager";
import { DocUtils } from "../documents/Documents";
import { undoBatch } from "../util/UndoManager";
+import { Scripting } from "../util/Scripting";
+import { FieldValue, Cast } from "../../new_fields/Types";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import Palette from "./Palette";
@observer
export default class GestureOverlay extends Touchable {
static Instance: GestureOverlay;
@observable private _points: { X: number, Y: number }[] = [];
+ @observable private _palette?: JSX.Element;
+ @observable public Color: string = "rgb(244, 67, 54)";
+ @observable public Width: number = 5;
private _d1: Doc | undefined;
+ private thumbIdentifier?: number;
constructor(props: Readonly<{}>) {
super(props);
@@ -28,6 +36,47 @@ export default class GestureOverlay extends Touchable {
}
@action
+ handleHandDown = (e: React.TouchEvent) => {
+ const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
+ const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
+ this.thumbIdentifier = thumb?.identifier;
+ const others = fingers.filter(f => f !== thumb);
+ const minX = Math.min(...others.map(f => f.clientX));
+ const minY = Math.min(...others.map(f => f.clientY));
+ // const t = this.getTransform().transformPoint(minX, minY);
+ // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY);
+
+ const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc));
+ if (thumbDoc) {
+ this._palette = <Palette x={minX} y={minY} thumb={[thumb.clientX, thumb.clientY]} thumbDoc={thumbDoc} />;
+ }
+
+ document.removeEventListener("touchmove", this.onTouch);
+ document.removeEventListener("touchmove", this.handleHandMove);
+ document.addEventListener("touchmove", this.handleHandMove);
+ document.removeEventListener("touchend", this.handleHandUp);
+ document.addEventListener("touchend", this.handleHandUp);
+ }
+
+ @action
+ handleHandMove = (e: TouchEvent) => {
+ for (let i = 0; i < e.changedTouches.length; i++) {
+ const pt = e.changedTouches.item(i);
+ if (pt?.identifier === this.thumbIdentifier) {
+ }
+ }
+ }
+
+ @action
+ handleHandUp = (e: TouchEvent) => {
+ this.onTouchEnd(e);
+ if (this.prevPoints.size < 3) {
+ this._palette = undefined;
+ document.removeEventListener("touchend", this.handleHandUp);
+ }
+ }
+
+ @action
onPointerDown = (e: React.PointerEvent) => {
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) {
this._points.push({ X: e.clientX, Y: e.clientY });
@@ -82,6 +131,8 @@ export default class GestureOverlay extends Touchable {
return actionPerformed;
}
+
+
@action
onPointerUp = (e: PointerEvent) => {
if (this._points.length > 1) {
@@ -157,16 +208,21 @@ export default class GestureOverlay extends Touchable {
return (
<svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000 }}>
- {CreatePolyline(this._points, B.left, B.top)}
+ {CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)}
</svg>
);
}
render() {
return (
- <div className="gestureOverlay-cont" onPointerDown={this.onPointerDown}>
+ <div className="gestureOverlay-cont" onPointerDown={this.onPointerDown} onTouchStart={this.onTouchStart}>
{this.props.children}
+ {this._palette}
{this.currentStroke}
</div>);
}
-} \ No newline at end of file
+}
+
+Scripting.addGlobal("GestureOverlay", GestureOverlay);
+Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.Color = color; GestureOverlay.Instance.Width = width; }); });
+Scripting.addGlobal(function resetPen() { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; }); }); \ No newline at end of file
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 243123352..be07a9024 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -10,12 +10,13 @@ import { Scripting } from "../util/Scripting";
import { SelectionManager } from "../util/SelectionManager";
import { undoBatch, UndoManager } from "../util/UndoManager";
import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import GestureOverlay from "./GestureOverlay";
export class InkingControl {
@observable static Instance: InkingControl;
@computed private get _selectedTool(): InkTool { return FieldValue(NumCast(CurrentUserUtils.UserDocument.inkTool)) ?? InkTool.None; }
- @computed private get _selectedColor(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; }
- @computed private get _selectedWidth(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; }
+ @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; }
+ @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; }
@observable public _open: boolean = false;
constructor() {
diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss
index 60004c81f..2626774cb 100644
--- a/src/client/views/Palette.scss
+++ b/src/client/views/Palette.scss
@@ -4,15 +4,17 @@
height: 300px;
touch-action: pan-x;
overflow: scroll;
+ position: absolute;
.palette-thumbContent {
width: 100%;
height: 100%;
+ }
- .palette-button {
- width: 100px;
- height: 100px;
- }
+ .palette-button {
+ width: 100px;
+ height: 100px;
+ background: blue;
}
}
} \ No newline at end of file
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 390b7e2c2..3649cccfe 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -1,22 +1,60 @@
import * as React from "react";
import "./Palette.scss";
import { PointData } from "../../new_fields/InkField";
+import { Doc } from "../../new_fields/Doc";
+import { Docs } from "../documents/Documents";
+import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
+import { List } from "../../new_fields/List";
+import { DocumentView } from "./nodes/DocumentView";
+import { emptyPath, returnFalse, emptyFunction, returnOne, returnEmptyString, returnTrue } from "../../Utils";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { Transform } from "../util/Transform";
+import { computed, action } from "mobx";
+import { FieldValue, Cast } from "../../new_fields/Types";
+import { observer } from "mobx-react";
+import { DocumentContentsView } from "./nodes/DocumentContentsView";
+import { CollectionStackingView } from "./collections/CollectionStackingView";
+import { CollectionView } from "./collections/CollectionView";
export interface PaletteProps {
x: number;
y: number;
thumb: number[];
+ thumbDoc: Doc;
}
+@observer
export default class Palette extends React.Component<PaletteProps> {
render() {
return (
<div className="palette-container" style={{ transform: `translate(${this.props.x}px, ${this.props.y}px)` }}>
<div className="palette-thumb" style={{ transform: `translate(${this.props.thumb[0] - this.props.x}px, ${this.props.thumb[1] - this.props.y}px)` }}>
<div className="palette-thumbContent">
- <div className="palette-button" style={{ background: "green" }} onPointerDown={() => console.log("hi")}>1</div>
- <div className="palette-button" style={{ background: "red" }}>2</div>
- <div className="palette-button" style={{ background: "blue" }}>3</div>
+ <DocumentView
+ Document={this.props.thumbDoc}
+ DataDoc={undefined}
+ LibraryPath={emptyPath}
+ addDocument={undefined}
+ addDocTab={returnFalse}
+ pinToPres={emptyFunction}
+ removeDocument={undefined}
+ ruleProvider={undefined}
+ onClick={undefined}
+ ScreenToLocalTransform={Transform.Identity}
+ ContentScaling={returnOne}
+ PanelWidth={() => window.screen.width}
+ PanelHeight={() => window.screen.height}
+ renderDepth={0}
+ focus={emptyFunction}
+ backgroundColor={returnEmptyString}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}>
+ </DocumentView>
</div>
</div>
</div>
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 3eb66ff72..24ea801a0 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -21,15 +21,26 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
*/
@action
protected onTouchStart = (e: React.TouchEvent): void => {
+ const actualPts: React.Touch[] = [];
for (let i = 0; i < e.targetTouches.length; i++) {
const pt: any = e.targetTouches.item(i);
+ actualPts.push(pt);
// pen is also a touch, but with a radius of 0.5 (at least with the surface pens)
// and this seems to be the only way of differentiating pen and touch on touch events
- if (pt.radiusX > 0.5 && pt.radiusY > 0.5) {
+ if (pt.radiusX > 1 && pt.radiusY > 1) {
this.prevPoints.set(pt.identifier, pt);
}
}
+ const ptsToDelete: number[] = [];
+ this.prevPoints.forEach(pt => {
+ if (!actualPts.includes(pt)) {
+ ptsToDelete.push(pt.identifier);
+ }
+ });
+
+ ptsToDelete.forEach(pt => this.prevPoints.delete(pt));
+
if (this.prevPoints.size) {
switch (this.prevPoints.size) {
case 1:
@@ -57,13 +68,12 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
*/
@action
protected onTouch = (e: TouchEvent): void => {
- const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
// if we're not actually moving a lot, don't consider it as dragging yet
if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return;
this._touchDrag = true;
if (this.holdTimer) {
- console.log("clear");
clearTimeout(this.holdTimer);
this.holdTimer = undefined;
}
@@ -99,7 +109,6 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
}
}
if (this.holdTimer) {
- console.log("clear");
clearTimeout(this.holdTimer);
this.holdTimer = undefined;
}
@@ -152,7 +161,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
}
handleHandDown = (e: React.TouchEvent) => {
- e.stopPropagation();
- e.preventDefault();
+ // e.stopPropagation();
+ // e.preventDefault();
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d7cccc036..84945c6e6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,7 +9,7 @@ import { Id } from "../../../../new_fields/FieldSymbols";
import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
+import { BoolCast, Cast, DateCast, NumCast, StrCast, FieldValue } from "../../../../new_fields/Types";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
@@ -468,7 +468,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
handle1PointerMove = (e: TouchEvent) => {
// panning a workspace
if (!e.cancelBubble) {
- const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
const pt = myTouches[0];
if (pt) {
if (InkingControl.Instance.selectedTool === InkTool.None) {
@@ -490,7 +490,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
handle2PointersMove = (e: TouchEvent) => {
// pinch zooming
if (!e.cancelBubble) {
- const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
const pt1 = myTouches[0];
const pt2 = myTouches[1];
@@ -849,51 +849,47 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
private thumbIdentifier?: number;
- private hand?: React.Touch[];
- @action
- handleHandDown = (e: React.TouchEvent) => {
- const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
- this.hand = fingers;
- const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
- this.thumbIdentifier = thumb?.identifier;
- const others = fingers.filter(f => f !== thumb);
- const minX = Math.min(...others.map(f => f.clientX));
- const minY = Math.min(...others.map(f => f.clientY));
- const t = this.getTransform().transformPoint(minX, minY);
- const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY);
- this._palette = <Palette x={t[0]} y={t[1]} thumb={th} />;
-
- document.removeEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchmove", this.handleHandMove);
- document.addEventListener("touchmove", this.handleHandMove);
- document.removeEventListener("touchend", this.handleHandUp);
- document.addEventListener("touchend", this.handleHandUp);
- }
-
- @action
- handleHandMove = (e: TouchEvent) => {
- for (let i = 0; i < e.changedTouches.length; i++) {
- const pt = e.changedTouches.item(i);
- if (pt?.identifier === this.thumbIdentifier) {
- }
- }
- }
-
- @action
- handleHandUp = (e: TouchEvent) => {
- console.log(e.changedTouches.length);
- this.onTouchEnd(e);
- if (this.prevPoints.size < 3) {
- if (this.hand) {
- for (const h of this.hand) {
- this.prevPoints.has(h.identifier) && this.prevPoints.delete(h.identifier);
- }
- }
- this._palette = undefined;
- document.removeEventListener("touchend", this.handleHandUp);
- }
- }
+ // @action
+ // handleHandDown = (e: React.TouchEvent) => {
+ // const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
+ // const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
+ // this.thumbIdentifier = thumb?.identifier;
+ // const others = fingers.filter(f => f !== thumb);
+ // const minX = Math.min(...others.map(f => f.clientX));
+ // const minY = Math.min(...others.map(f => f.clientY));
+ // const t = this.getTransform().transformPoint(minX, minY);
+ // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY);
+
+ // const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc));
+ // if (thumbDoc) {
+ // this._palette = <Palette x={t[0]} y={t[1]} thumb={th} thumbDoc={thumbDoc} />;
+ // }
+
+ // document.removeEventListener("touchmove", this.onTouch);
+ // document.removeEventListener("touchmove", this.handleHandMove);
+ // document.addEventListener("touchmove", this.handleHandMove);
+ // document.removeEventListener("touchend", this.handleHandUp);
+ // document.addEventListener("touchend", this.handleHandUp);
+ // }
+
+ // @action
+ // handleHandMove = (e: TouchEvent) => {
+ // for (let i = 0; i < e.changedTouches.length; i++) {
+ // const pt = e.changedTouches.item(i);
+ // if (pt?.identifier === this.thumbIdentifier) {
+ // }
+ // }
+ // }
+
+ // @action
+ // handleHandUp = (e: TouchEvent) => {
+ // this.onTouchEnd(e);
+ // if (this.prevPoints.size < 3) {
+ // this._palette = undefined;
+ // document.removeEventListener("touchend", this.handleHandUp);
+ // }
+ // }
onContextMenu = (e: React.MouseEvent) => {
const layoutItems: ContextMenuProps[] = [];
@@ -958,12 +954,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
];
}
- @observable private _palette?: JSX.Element;
+ // @observable private _palette?: JSX.Element;
children = () => {
const eles: JSX.Element[] = [];
this.extensionDoc && (eles.push(...this.childViews()));
- this._palette && (eles.push(this._palette));
+ // this._palette && (eles.push(this._palette));
// this.currentStroke && (eles.push(this.currentStroke));
eles.push(<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />);
return eles;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 415d0e2ef..bdec94eb3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -59,6 +59,8 @@ export interface DocumentViewProps {
LibraryPath: Doc[];
fitToBox?: boolean;
onClick?: ScriptField;
+ onPointerDown?: ScriptField;
+ onPointerUp?: ScriptField;
dragDivName?: string;
addDocument?: (doc: Doc) => boolean;
removeDocument?: (doc: Doc) => boolean;
@@ -105,6 +107,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get nativeWidth() { return this.layoutDoc.nativeWidth || 0; }
@computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; }
@computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; }
+ @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; }
+ @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; }
@action
componentDidMount() {
@@ -180,7 +184,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
- onClick = async (e: React.MouseEvent) => {
+ onClick = async (e: React.MouseEvent | React.PointerEvent) => {
if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
e.stopPropagation();
@@ -240,8 +244,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
handle1PointerDown = (e: React.TouchEvent) => {
+ if (this.Document.onPointerDown) return;
if (!e.nativeEvent.cancelBubble) {
- const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0];
+ const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0];
this._downX = touch.clientX;
this._downY = touch.clientY;
this._hitTemplateDrag = false;
@@ -265,7 +270,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
document.removeEventListener("touchmove", this.onTouch);
}
else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) {
- const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0];
+ const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0];
if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) {
if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) {
document.removeEventListener("touchmove", this.onTouch);
@@ -293,7 +298,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@action
handle2PointersMove = (e: TouchEvent) => {
- const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
const pt1 = myTouches[0];
const pt2 = myTouches[1];
const oldPoint1 = this.prevPoints.get(pt1.identifier);
@@ -363,6 +368,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onPointerDown = (e: React.PointerEvent): void => {
+ if (this.onPointerDownHandler && this.onPointerDownHandler.script) {
+ this.onPointerDownHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointerup", this.onPointerUp);
+ return;
+ }
// console.log(e.button)
// console.log(e.nativeEvent)
// continue if the event hasn't been canceled AND we are using a moues or this is has an onClick or onDragStart function (meaning it is a button document)
@@ -412,6 +423,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onPointerUp = (e: PointerEvent): void => {
+ if (this.onPointerUpHandler && this.onPointerUpHandler.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+ this.onPointerUpHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ return;
+ }
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 21e69fbed..1cba3cba9 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -20,6 +20,8 @@ export const documentSchema = createSchema({
dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy")
removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
+ onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
+ onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped.
dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document.
ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 71dd34e68..2ade6f102 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -110,6 +110,29 @@ export class CurrentUserUtils {
}));
}
+ static setupThumbButtons(doc: Doc) {
+ const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
+ { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ ];
+ return docProtoData.map(data => Docs.Create.FontIconDocument({
+ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick,
+ onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
+ onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
+ ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true,
+ backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory,
+ }));
+ }
+
+ static setupThumbDoc(userDoc: Doc) {
+ if (!userDoc.thumbDoc) {
+ return Docs.Create.MasonryDocument(CurrentUserUtils.setupThumbButtons(userDoc), {
+ width: 300, columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5
+ });
+ }
+ return userDoc.thumbDoc;
+ }
+
static setupMobileDoc(userDoc: Doc) {
return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), {
columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5