Skip to content

Commit

Permalink
Add an option to use the virtual cursor to help with element locate w…
Browse files Browse the repository at this point in the history
…/touch input. (#1038)

* Add an option to use the virtual cursor to help with element locate w/touch input.

* Not sure what happened to ToolSettings.enableVirtualCursorForLocate...

* re-run extract-api

* Put back the beta tag...

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
bbastings and mergify[bot] authored Mar 25, 2021
1 parent ef34c10 commit 9beb4bf
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 11 deletions.
6 changes: 5 additions & 1 deletion common/api/imodeljs-frontend.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,9 @@ export class AccuSnap implements Decorator {
readonly toolState: AccuSnap.ToolState;
// @internal (undocumented)
touchCursor?: TouchCursor;
}
// @internal (undocumented)
get wantVirtualCursor(): boolean;
}

// @public (undocumented)
export namespace AccuSnap {
Expand Down Expand Up @@ -10852,6 +10854,8 @@ export class ToolSettings {
static doubleClickTimeout: BeDuration;
static doubleClickToleranceInches: number;
static doubleTapTimeout: BeDuration;
// @beta
static enableVirtualCursorForLocate: boolean;
static preserveWorldUp: boolean;
static scrollSpeed: number;
static startDragDelay: BeDuration;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@bentley/imodeljs-frontend",
"comment": "Add an option to use the virtual cursor to help with element locate w/touch input.",
"type": "none"
}
],
"packageName": "@bentley/imodeljs-frontend",
"email": "65233531+bbastings@users.noreply.github.com"
}
20 changes: 17 additions & 3 deletions core/frontend/src/AccuSnap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { IModelApp } from "./IModelApp";
import { CanvasDecoration } from "./render/CanvasDecoration";
import { IconSprites, Sprite, SpriteLocation } from "./Sprites";
import { BeButton, BeButtonEvent, BeTouchEvent, InputSource } from "./tools/Tool";
import { ToolSettings } from "./tools/ToolSettings";
import { DecorateContext } from "./ViewContext";
import { Decorator } from "./ViewManager";
import { ScreenViewport, Viewport } from "./Viewport";
Expand Down Expand Up @@ -125,6 +126,8 @@ export class TouchCursor implements CanvasDecoration {
}

public doTouchEnd(ev: BeTouchEvent): void {
if (this._isDragging && undefined !== ev.viewport)
IModelApp.toolAdmin.currentInputState.fromPoint(ev.viewport, this._offsetPosition, InputSource.Touch); // Current location should reflect virtual cursor offset...
this._isSelected = this._isDragging = false;
if (undefined !== ev.viewport)
ev.viewport.invalidateDecorations();
Expand Down Expand Up @@ -566,7 +569,7 @@ export class AccuSnap implements Decorator {
this.toolState.enabled = yesNo;
if (!yesNo) {
this.clear();
if (undefined !== this.touchCursor) {
if (undefined !== this.touchCursor && !this.wantVirtualCursor) {
this.touchCursor = undefined;
IModelApp.viewManager.invalidateDecorationsAllViews();
}
Expand Down Expand Up @@ -954,11 +957,16 @@ export class AccuSnap implements Decorator {
/** @internal */
public onTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): boolean { return (undefined !== this.touchCursor) ? this.touchCursor.doTouchMoveStart(ev, startEv) : false; }

/** @internal */
public get wantVirtualCursor(): boolean {
return this._doSnapping || (this.isLocateEnabled && ToolSettings.enableVirtualCursorForLocate);
}

/** @internal */
public async onTouchTap(ev: BeTouchEvent): Promise<boolean> {
if (undefined !== this.touchCursor)
return this.touchCursor.doTouchTap(ev);
if (!this._doSnapping)
if (!this.wantVirtualCursor)
return false;
this.touchCursor = TouchCursor.createFromTouchTap(ev);
if (undefined === this.touchCursor)
Expand Down Expand Up @@ -1018,7 +1026,13 @@ export class AccuSnap implements Decorator {
/** Enable locating elements.
* @public
*/
public enableLocate(yesNo: boolean) { this.toolState.locate = yesNo; }
public enableLocate(yesNo: boolean) {
this.toolState.locate = yesNo;
if (!yesNo && undefined !== this.touchCursor && !this.wantVirtualCursor) {
this.touchCursor = undefined;
IModelApp.viewManager.invalidateDecorationsAllViews();
}
}

/** Called whenever a new [[Tool]] is started.
* @internal
Expand Down
3 changes: 2 additions & 1 deletion core/frontend/src/tools/SelectTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ export class SelectionTool extends PrimitiveTool {
sections.push(ToolAssistance.createSection(mousePickInstructions, ToolAssistance.inputsLabel));

const touchPickInstructions: ToolAssistanceInstruction[] = [];
touchPickInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, CoreTools.translate("ElementSet.Inputs.AcceptElement"), false, ToolAssistanceInputMethod.Touch));
if (!ToolAssistance.createTouchCursorInstructions(touchPickInstructions))
touchPickInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, CoreTools.translate("ElementSet.Inputs.AcceptElement"), false, ToolAssistanceInputMethod.Touch));
sections.push(ToolAssistance.createSection(touchPickInstructions, ToolAssistance.inputsLabel));
break;
case SelectionMethod.Line:
Expand Down
10 changes: 9 additions & 1 deletion core/frontend/src/tools/ToolAdmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1573,7 +1573,15 @@ export class ToolAdmin {
viewport.drawLocateCursor(context, ev.viewPoint, viewport.pixelsFromInches(IModelApp.locateManager.apertureInches), this.isLocateCircleOn, hit);
}

public get isLocateCircleOn(): boolean { return this.toolState.locateCircleOn && this.currentInputState.inputSource === InputSource.Mouse && this._canvasDecoration === undefined; }
public get isLocateCircleOn(): boolean {
if (!this.toolState.locateCircleOn || undefined !== this._canvasDecoration)
return false;

if (InputSource.Mouse === this.currentInputState.inputSource)
return true;

return (InputSource.Touch === this.currentInputState.inputSource && undefined !== IModelApp.accuSnap.touchCursor);
}

/** @internal */
public beginDynamics(): void {
Expand Down
2 changes: 1 addition & 1 deletion core/frontend/src/tools/ToolAssistance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ export class ToolAssistance {
*/
public static createTouchCursorInstructions(instructions: ToolAssistanceInstruction[]): boolean {
const accuSnap = IModelApp.accuSnap;
if (accuSnap.isSnapEnabled && accuSnap.isSnapEnabledByUser && undefined === accuSnap.touchCursor) {
if (undefined === accuSnap.touchCursor && accuSnap.wantVirtualCursor) {
instructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, this.translateTouch("Activate"), false, ToolAssistanceInputMethod.Touch));
return true;
} else if (undefined !== accuSnap.touchCursor) {
Expand Down
2 changes: 2 additions & 0 deletions core/frontend/src/tools/ToolSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export class ToolSettings {
public static doubleClickTimeout = BeDuration.fromMilliseconds(500);
/** Number of screen inches of movement allowed between clicks to still qualify as a double-click. */
public static doubleClickToleranceInches = 0.05;
/** @beta Use virtual cursor to help with locating elements using touch input. By default it's only enabled for snapping. */
public static enableVirtualCursorForLocate = false;
/** If true, view rotation tool keeps the up vector (worldZ) aligned with screenY. */
public static preserveWorldUp = true;
/** Delay with a touch on the surface before a move operation begins. */
Expand Down
11 changes: 7 additions & 4 deletions test-apps/display-test-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ display-test-app has access to all key-ins defined in the imodeljs-frontend and
* `background=`: Preserve background color when drawing as a raster image.
* **dta aspect skew decorator** *apply=0|1* - Toggle a decorator that draws a simple bspline curve based on the project extents, for testing the effect of aspect ratio skew on the curve stroke tolerance. Use in conjunction with `fdt aspect skew` to adjust the skew. If `apply` is 0, then the skew will have no effect on the curve's level of detail; otherwise a higher aspect ratio skew should produce higher-resolution curve graphics.
* **dta classifyclip selected** *inside* - Color code elements from the current selection set based on their containment with the current view clip. Inside - Green, Outside - Red, Overlap - Blue. Specify optional inside arg to only determine inside or outside, not overlap. Disable clip in the view settings to select elements outside clip, use clip tool panel EDIT button to redisplay clip decoration after processing selection. Use key-in again without a clip or selection set to clear the color override.
* **dta grid settings** - Change the grid settings for the selected viewport.
* `spacing=number` Specify x and y grid reference line spacing in meters.
* `ratio=number` Specify y spacing as current x * ratio.
* `gridsPerRef=number` Specify number of grid lines to display per reference line.
* `orientation=0|1|2|3|4` Value for GridOrientationType.
* **dta model transform** - Apply a display transform to all models currently displayed in the selected viewport. Origin is specified like `x=1 y=2 z=3`; pitch and roll as `p=45 r=90` in degrees. Any argument can be omitted. Omitting all arguments clears the display transform. Snapping intentionally does not take the display transform into account.

## Editing
Expand All @@ -217,9 +222,7 @@ Using an editing session is optional, but outside of a session, the viewport's g

### Editing key-ins

display-test-app has access to all key-ins defined in the imodeljs-editor-frontend package. It also provides the following additional key-ins.

* `dta edit` - begin a new editing session, or end the current editing session. The title of the window or browser tab will update to reflect the current state: "[R/W]" indicating no current editing session, or "[EDIT]" indicating an active editing session.
* `dta delete elements` - delete all elements currently in the selection set.
* `dta move elements` - start moving elements. If no elements are currently in the selection set, you will be prompted to select one. First data point defines the start point; second defines the end point and moves the element(s) by the delta between the two points.
* `dta place line string` - start placing a line string. Each data point defines another point in the string; a reset (right mouse button) finishes. The element is placed into the first spatial model and spatial category in the viewport's model and category selectors.
* `dta undo` - undo the most recent change.
* `dta redo` - redo the most recently-undone change.
3 changes: 3 additions & 0 deletions test-apps/display-test-app/public/locales/en/SVTTools.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"keyin": "dta classifyclip selected"
}
},
"GridSettings": {
"keyin": "dta grid settings"
},
"Markup": {
"keyin": "dta markup",
"TestSelect": {
Expand Down
2 changes: 2 additions & 0 deletions test-apps/display-test-app/src/frontend/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DrawingAidTestTool } from "./DrawingAidTestTool";
import { EditingSessionTool, PlaceLineStringTool } from "./EditingTools";
import { FenceClassifySelectedTool } from "./Fence";
import { RecordFpsTool } from "./FpsMonitor";
import { ChangeGridSettingsTool } from "./Grid";
import { IncidentMarkerDemoTool } from "./IncidentMarkerDemo";
import { MarkupSelectTestTool } from "./MarkupSelectTestTool";
import { Notifications } from "./Notifications";
Expand Down Expand Up @@ -213,6 +214,7 @@ export class DisplayTestApp {
[
ApplyModelDisplayScaleTool,
ApplyModelTransformTool,
ChangeGridSettingsTool,
CloneViewportTool,
CloseIModelTool,
CloseWindowTool,
Expand Down
86 changes: 86 additions & 0 deletions test-apps/display-test-app/src/frontend/Grid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

import { parseArgs } from "@bentley/frontend-devtools";
import { GridOrientationType } from "@bentley/imodeljs-common";
import { IModelApp, Tool } from "@bentley/imodeljs-frontend";

/** Change grid settings for testing. */
export class ChangeGridSettingsTool extends Tool {
public static toolId = "GridSettings";
public static get minArgs() { return 0; }
public static get maxArgs() { return 4; }

public run(spacing?: number, ratio?: number, gridsPerRef?: number, orientation?: GridOrientationType): boolean {
const vp = IModelApp.viewManager.selectedView;
if (undefined === vp)
return false;

if (undefined !== spacing)
vp.view.details.gridSpacing = { x: spacing, y: spacing };

if (undefined !== ratio)
vp.view.details.gridSpacing = { x: vp.view.details.gridSpacing.x, y: vp.view.details.gridSpacing.x * ratio };

if (undefined !== gridsPerRef)
vp.view.details.gridsPerRef = gridsPerRef;

if (undefined !== orientation)
vp.view.details.gridOrientation = orientation;

vp.invalidateScene(); // Needed to clear cached grid decoration...
return true;
}

/** The keyin accepts the following arguments:
* - `spacing=number` Specify x and y grid reference line spacing in meters.
* - `ratio=number` Specify y spacing as current x * ratio.
* - `gridsPerRef=number` Specify number of grid lines to display per reference line.
* - `orientation=0|1|2|3|4` Value for GridOrientationType.
*/
public parseAndRun(...inputArgs: string[]): boolean {
let spacing;
let ratio;
let gridsPerRef;
let orientation;
const args = parseArgs(inputArgs);

const spacingArg = args.getFloat("s");
if (undefined !== spacingArg)
spacing = spacingArg;

const ratioArg = args.getFloat("r");
if (undefined !== ratioArg)
ratio = ratioArg;

const gridsPerRefArg = args.getInteger("g");
if (undefined !== gridsPerRefArg)
gridsPerRef = gridsPerRefArg;

const orientationArg = args.getInteger("o");
if (undefined !== orientationArg) {
switch (orientationArg) {
case 0:
orientation = GridOrientationType.View;
break;
case 1:
orientation = GridOrientationType.WorldXY;
break;
case 2:
orientation = GridOrientationType.WorldYZ;
break;
case 3:
orientation = GridOrientationType.WorldXZ;
break;
case 4:
orientation = GridOrientationType.AuxCoord;
break;
}
}

this.run(spacing, ratio, gridsPerRef, orientation);
return true;
}
}

0 comments on commit 9beb4bf

Please sign in to comment.