aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx44
-rw-r--r--src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx154
2 files changed, 190 insertions, 8 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c6db8290d..194c99c3d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -54,6 +54,8 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
+import { SmartDrawHandler } from './SmartDrawHandler';
+import { ImageLabelHandler } from './ImageLabelHandler';
@observer
class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
@@ -496,30 +498,33 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!this.Document.isGroup) {
// group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
+ const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
switch (Doc.ActiveTool) {
- case InkTool.Highlighter: break;
- case InkTool.Write: break;
- case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+ case InkTool.Highlighter:
+ break;
+ case InkTool.Write:
+ break;
+ case InkTool.Pen:
+ break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
case InkTool.StrokeEraser:
case InkTool.SegmentEraser:
this._batch = UndoManager.StartBatch('collectionErase');
this._eraserPts.length = 0;
- const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false);
break;
case InkTool.RadiusEraser:
this._batch = UndoManager.StartBatch('collectionErase');
this._eraserPts.length = 0;
- // const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
- // setupMoveUpEvents(this, e, this.onRadiusEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false);
+ setupMoveUpEvents(this, e, this.onRadiusEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false);
break;
+ case InkTool.SmartDraw:
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, this.createDrawing, hit !== -1, false);
case InkTool.None:
if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
- const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false);
}
break;
- default:
+ default:
}
}
}
@@ -1226,6 +1231,29 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
+ createDrawing = (e: PointerEvent, doubleTap?: boolean) => {
+ SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY);
+ if (SmartDrawHandler.Instance.coords) {
+ // const coords: InkData = SmartDrawHandler.Instance.coords;
+ // const inkField = new InkField(coords);
+ // const points = coords.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
+ // const bounds = InkField.getBounds(points);
+ // const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
+ // const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
+ // return Docs.Create.InkDocument(
+ // points,
+ // { title: 'stroke',
+ // x: B.x - inkWidth / 2,
+ // y: B.y - inkWidth / 2,
+ // _width: B.width + inkWidth,
+ // _height: B.height + inkWidth,
+ // stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ // inkWidth
+ // );
+ }
+ };
+
+ @action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
diff --git a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
new file mode 100644
index 000000000..fc88b5cc6
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
@@ -0,0 +1,154 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, makeObservable, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import React from 'react';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { Button, IconButton } from 'browndash-components';
+import ReactLoading from 'react-loading';
+import { AiOutlineSend } from 'react-icons/ai';
+import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import './ImageLabelHandler.scss';
+import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
+import { InkingStroke } from '../../InkingStroke';
+import { InkData } from '../../../../fields/InkField';
+
+@observer
+export class SmartDrawHandler extends ObservableReactComponent<{}> {
+ static Instance: SmartDrawHandler;
+
+ @observable private _display: boolean = false;
+ @observable private _pageX: number = 0;
+ @observable private _pageY: number = 0;
+ @observable private _yRelativeToTop: boolean = true;
+ @observable private _isLoading: boolean = false;
+ @observable private _userInput: string = '';
+ @observable public coords: InkData | undefined = undefined;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ SmartDrawHandler.Instance = this;
+ }
+
+ @action
+ setIsLoading = (isLoading: boolean) => {
+ this._isLoading = isLoading;
+ };
+
+ @action
+ setUserInput = (input: string) => {
+ this._userInput = input;
+ };
+
+ @action
+ displaySmartDrawHandler = (x: number, y: number) => {
+ this._pageX = x;
+ this._pageY = y;
+ this._display = true;
+ };
+
+ @action
+ hideLabelhandler = () => {
+ this._display = false;
+ };
+
+ @action
+ drawWithGPT = async (startPoint: {X: number, Y: number}, input: string) => {
+ this.setIsLoading(true);
+ try {
+ const res = await gptAPICall(input, GPTCallType.DRAW);
+ if (!res) {
+ console.error('GPT call failed');
+ return;
+ }
+ console.log("GPT response:", res);
+ // controlPts: {X: number, Y: number}[] = []
+ // code to extract list of coords from the string
+ // this.coords = controlPts.map(pt: {X: number, Y: number } => {pt.X + startPoint.X, pt.Y + startPoint.Y});
+
+
+ } catch (err) {
+ console.error('GPT call failed');
+ }
+
+ this.setIsLoading(false);
+ this.setUserInput('');
+ };
+
+ render() {
+ if (this._display) {
+ return (
+ <div
+ id="label-handler"
+ className="contextMenu-cont"
+ style={{
+ display: this._display ? '' : 'none',
+ left: this._pageX,
+ ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }),
+ background: SettingsManager.userBackgroundColor,
+ color: SettingsManager.userColor,
+ }}>
+ <div>
+ <IconButton tooltip={'Cancel'} onPointerDown={this.hideLabelhandler} icon={<FontAwesomeIcon icon="xmark" />} color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '19px' }} />
+ <input
+ aria-label="label-input"
+ id="new-label"
+ type="text"
+ style={{ color: 'black' }}
+ value={this._userInput}
+ onChange={e => {
+ this.setUserInput(e.target.value);
+ }}
+ placeholder="Enter item to draw"
+ />
+ <Button
+ style={{ alignSelf: 'flex-end' }}
+ text="Send"
+ icon={this._isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />}
+ iconPlacement="right"
+ color={MarqueeOptionsMenu.Instance.userColor}
+ onClick={e => {
+ this.drawWithGPT({X: e.clientX, Y: e.clientY}, this._userInput);
+ }}
+ />
+ {/* <IconButton
+ tooltip={'Generate Drawing'}
+ onPointerDown={() => {
+ const input = document.getElementById('new-label') as HTMLInputElement;
+ const newLabel = input.value;
+ // this.addLabel(newLabel);
+ // this._currentLabel = '';
+ input.value = '';
+ }}
+ icon={<FontAwesomeIcon icon="plus" />}
+ color={MarqueeOptionsMenu.Instance.userColor}
+ style={{ width: '19px' }}
+ />
+ <IconButton tooltip={'Group Images'} icon={<FontAwesomeIcon icon="object-group" />} color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '19px' }} /> */}
+ </div>
+ <div>
+ {/* {this._labelGroups.map(group => {
+ return (
+ <div>
+ <p>{group}</p>
+ <IconButton
+ tooltip={'Remove Label'}
+ onPointerDown={() => {
+ this.removeLabel(group);
+ }}
+ icon={'x'}
+ color={MarqueeOptionsMenu.Instance.userColor}
+ style={{ width: '19px' }}
+ />
+ </div>
+ );
+ })} */}
+ </div>
+ </div>
+ );
+ } else {
+ return <></>;
+ }
+ }
+}