diff options
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 120 | 
1 files changed, 90 insertions, 30 deletions
| diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 312acd5b2..313d8be23 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -3,8 +3,8 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";  import { action, computed, observable, reaction, runInAction } from "mobx";  import { observer } from "mobx-react"; -import { Doc, DataSym, Field } from "../../new_fields/Doc"; -import { PositionDocument } from '../../new_fields/documentSchemas'; +import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../new_fields/Doc"; +import { Document } from '../../new_fields/documentSchemas';  import { ScriptField } from '../../new_fields/ScriptField';  import { Cast, StrCast, NumCast } from "../../new_fields/Types";  import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils"; @@ -20,6 +20,7 @@ import React = require("react");  import { Id } from '../../new_fields/FieldSymbols';  import e = require('express');  import { CollectionDockingView } from './collections/CollectionDockingView'; +import { SnappingManager } from '../util/SnappingManager';  library.add(faCaretUp);  library.add(faObjectGroup); @@ -46,6 +47,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>      private _linkBoxHeight = 20 + 3; // link button height + margin      private _titleHeight = 20;      private _resizeUndo?: UndoManager.Batch; +    private _offX = 0; _offY = 0;  // offset from click pt to inner edge of resize border +    private _snapX = 0; _snapY = 0; // last snapped location of resize border      @observable private _accumulatedTitle = "";      @observable private _titleControlString: string = "#title";      @observable private _edtingTitle = false; @@ -170,8 +173,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>      }      @undoBatch      @action -    onCloseClick = async (e: PointerEvent) => { -        if (e.button === 0) { +    onCloseClick = async (e: PointerEvent | undefined) => { +        if (!e?.button) {              const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;              const selected = SelectionManager.SelectedDocuments().slice();              SelectionManager.DeselectAll(); @@ -233,19 +236,60 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>          return false;      } +    _initialAutoHeight = false; +    _dragHeights = new Map<Doc, number>();      @action      onPointerDown = (e: React.PointerEvent): void => {          setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });          if (e.button === 0) {              this._resizeHdlId = e.currentTarget.id; +            const bounds = e.currentTarget.getBoundingClientRect(); +            this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX; +            this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;              this.Interacting = true;              this._resizeUndo = UndoManager.StartBatch("DocDecs resize"); +            SelectionManager.SelectedDocuments()[0].props.setupDragLines?.();          } +        this._snapX = e.pageX; +        this._snapY = e.pageY; +        this._initialAutoHeight = true; +        DragManager.docsBeingDragged = SelectionManager.SelectedDocuments().map(dv => dv.rootDoc); +        SelectionManager.SelectedDocuments().map(dv => { +            this._dragHeights.set(dv.layoutDoc, NumCast(dv.layoutDoc._height)); +            dv.layoutDoc._delayAutoHeight = dv.layoutDoc._height; +        });      }      onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { -        let dX = 0, dY = 0, dW = 0, dH = 0; +        const first = SelectionManager.SelectedDocuments()[0]; +        let thisPt = { thisX: e.clientX - this._offX, thisY: e.clientY - this._offY }; +        const fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0; +        if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles +            const project = (p: number[], a: number[], b: number[]) => { +                var atob = [b[0] - a[0], b[1] - a[1]]; +                var atop = [p[0] - a[0], p[1] - a[1]]; +                var len = atob[0] * atob[0] + atob[1] * atob[1]; +                var dot = atop[0] * atob[0] + atop[1] * atob[1]; +                var t = dot / len; +                dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); +                return [a[0] + atob[0] * t, a[1] + atob[1] * t]; +            } +            const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); +            const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]) +            thisPt = DragManager.snapDragAspect(drag, fixedAspect); +        } else { +            thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY); +        } + +        move[0] = thisPt.thisX - this._snapX; +        move[1] = thisPt.thisY - this._snapY; +        this._snapX = thisPt.thisX; +        this._snapY = thisPt.thisY; +        let dX = 0, dY = 0, dW = 0, dH = 0; +        const unfreeze = () => +            SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => +                (element.rootDoc.type === DocumentType.RTF && element.layoutDoc._nativeHeight) && element.toggleNativeDimensions()));          switch (this._resizeHdlId) {              case "": break;              case "documentDecorations-topLeftResizer": @@ -260,6 +304,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>                  dH = -move[1];                  break;              case "documentDecorations-topResizer": +                unfreeze();                  dY = -1;                  dH = -move[1];                  break; @@ -273,27 +318,33 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>                  dH = move[1];                  break;              case "documentDecorations-bottomResizer": +                unfreeze();                  dH = move[1];                  break;              case "documentDecorations-leftResizer": +                unfreeze();                  dX = -1;                  dW = -move[0];                  break;              case "documentDecorations-rightResizer": +                unfreeze();                  dW = move[0];                  break;          }          SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { +            if (e.ctrlKey && !element.props.Document._nativeHeight) element.toggleNativeDimensions();              if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { -                const doc = PositionDocument(element.props.Document); -                const layoutDoc = PositionDocument(Doc.Layout(element.props.Document)); -                let nwidth = layoutDoc._nativeWidth || 0; -                let nheight = layoutDoc._nativeHeight || 0; -                const width = (layoutDoc._width || 0); -                const height = (layoutDoc._height || (nheight / nwidth * width)); +                const doc = Document(element.rootDoc); +                let nwidth = doc._nativeWidth || 0; +                let nheight = doc._nativeHeight || 0; +                const width = (doc._width || 0); +                let height = (doc._height || (nheight / nwidth * width));                  const scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();                  if (nwidth && nheight) { +                    if (nwidth / nheight !== width / height) { +                        height = nheight / nwidth * width; +                    }                      if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;                      else dW = dH * nwidth / nheight;                  } @@ -303,8 +354,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>                  doc.y = (doc.y || 0) + dY * (actualdH - height);                  const fixedAspect = (nwidth && nheight);                  if (fixedAspect && (!nwidth || !nheight)) { -                    layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; -                    layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; +                    doc._nativeWidth = nwidth = doc._width || 0; +                    doc._nativeHeight = nheight = doc._height || 0;                  }                  const anno = Cast(doc.annotationOn, Doc, null);                  if (e.ctrlKey && anno) { @@ -320,24 +371,24 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>                  else if (nwidth > 0 && nheight > 0) {                      if (Math.abs(dW) > Math.abs(dH)) {                          if (!fixedAspect) { -                            layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0); +                            doc._nativeWidth = actualdW / (doc._width || 1) * (doc._nativeWidth || 0);                          } -                        layoutDoc._width = actualdW; -                        if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width; -                        else layoutDoc._height = actualdH; +                        doc._width = actualdW; +                        if (fixedAspect && !doc._fitWidth) doc._height = nheight / nwidth * doc._width; +                        else doc._height = actualdH;                      }                      else {                          if (!fixedAspect) { -                            layoutDoc._nativeHeight = actualdH / (layoutDoc._height || 1) * (doc._nativeHeight || 0); +                            doc._nativeHeight = actualdH / (doc._height || 1) * (doc._nativeHeight || 0);                          } -                        layoutDoc._height = actualdH; -                        if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height; -                        else layoutDoc._width = actualdW; +                        doc._height = actualdH; +                        if (fixedAspect && !doc._fitWidth) doc._width = nwidth / nheight * doc._height; +                        else doc._width = actualdW;                      }                  } else { -                    dW && (layoutDoc._width = actualdW); -                    dH && (layoutDoc._height = actualdH); -                    dH && layoutDoc._autoHeight && (layoutDoc._autoHeight = false); +                    dW && (doc._width = actualdW); +                    dH && (doc._height = actualdH); +                    dH && this._initialAutoHeight && (doc._autoHeight = this._initialAutoHeight = false);                  }              }          })); @@ -346,10 +397,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>      @action      onPointerUp = (e: PointerEvent): void => { +        SelectionManager.SelectedDocuments().map(dv => { +            if (NumCast(dv.layoutDoc._delayAutoHeight) < this._dragHeights.get(dv.layoutDoc)!) { +                dv.nativeWidth > 0 && Doc.toggleNativeDimensions(dv.layoutDoc, dv.props.ContentScaling(), dv.panelWidth(), dv.panelHeight()); +                dv.layoutDoc._autoHeight = true; +            } +            dv.layoutDoc._delayAutoHeight = undefined; +        });          this._resizeHdlId = "";          this.Interacting = false;          (e.button === 0) && this._resizeUndo?.end();          this._resizeUndo = undefined; +        SnappingManager.clearSnapLines();      }      @computed @@ -388,7 +447,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>          const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined;          const bounds = this.Bounds;          const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; -        if (SelectionManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { +        if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {              return (null);          }          const minimal = bounds.r - bounds.x < 100 ? true : false; @@ -473,16 +532,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>                      onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>                  <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"                      onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> -                {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : <div id="documentDecorations-levelSelector" className="documentDecorations-selector" title="tap to select containing document" -                    onPointerDown={this.onSelectorUp} onContextMenu={(e) => e.preventDefault()}> -                    <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" /> -                </div>} +                {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : +                    <div id="documentDecorations-levelSelector" className="documentDecorations-selector" +                        title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}> +                        <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" /> +                    </div>}                  <div id="documentDecorations-borderRadius" className="documentDecorations-radius"                      onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>              </div >              <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2, top: bounds.b + this._resizeBorderWidth / 2 }}> -                <DocumentButtonBar views={SelectionManager.SelectedDocuments()} /> +                <DocumentButtonBar views={SelectionManager.SelectedDocuments} />              </div>          </div >          ); | 
