@itwin/core-frontend
Version:
iTwin.js frontend components
171 lines • 9.13 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Tools
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrimitiveTool = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const IModelApp_1 = require("../IModelApp");
const NotificationManager_1 = require("../NotificationManager");
const AccuDrawTool_1 = require("./AccuDrawTool");
const Tool_1 = require("./Tool");
/** The PrimitiveTool class can be used to implement tools to create or modify geometric elements.
* @see [Writing a PrimitiveTool]($docs/learning/frontend/primitivetools.md)
* @public
* @extensions
*/
class PrimitiveTool extends Tool_1.InteractiveTool {
/** The viewport within which the tool operates.
* @note This property is only initialized if [[run]] returns `true`, causing the tool to be installed.
*/
targetView;
_targetModelId;
get targetModelId() { return this._targetModelId; }
set targetModelId(v) { this._targetModelId = v; }
targetIsLocked = false; // If target model is known, set this to true in constructor and override getTargetModel.
/** Get the iModel on which this tool operates.
* @note The iModel is obtained from [[targetView]], so should only be invoked if the tool installed successfully.
*/
get iModel() {
(0, core_bentley_1.assert)(undefined !== this.targetView);
return this.targetView.view.iModel;
}
/** Get the briefcase on which this tool operates, if the tool has successfully installed and the target [[iModel]] is a briefcase. */
get briefcase() {
const iModel = this.targetView?.view.iModel;
return iModel?.isBriefcaseConnection() ? iModel : undefined;
}
/**
* Establish this tool as the active PrimitiveTool.
* @return true if this tool was installed (though it may have exited too)
* @note If you override this method you **must** call `super.run` and return false if it returns false.
*/
async run(..._args) {
const { toolAdmin, viewManager } = IModelApp_1.IModelApp;
if (!this.isCompatibleViewport(viewManager.selectedView, false) || !await toolAdmin.onInstallTool(this))
return false;
await toolAdmin.startPrimitiveTool(this);
await toolAdmin.onPostInstallTool(this);
return true;
}
/** Determine whether the supplied Viewport is compatible with this tool.
* @param vp the Viewport to check
*/
isCompatibleViewport(vp, isSelectedViewChange) {
if (undefined === vp)
return false; // No views are open...
const view = vp.view;
const iModel = view.iModel;
if (this.requireWriteableTarget() && iModel.isReadonly)
return false; // this Tool can't be used when iModel is read only.
if (undefined === this.targetView || (!this.targetIsLocked && isSelectedViewChange))
this.targetView = vp; // Update target to new view if undefined or still free to change.
if (undefined === this.targetModelId && (!this.targetIsLocked || vp === this.targetView))
return true; // Accept if this view is current target or any type of model/view is still ok as target is still free to change.
if (iModel !== this.iModel)
return false; // Once a ViewState has been established, only accept viewport showing the same iModel.
if (this.targetModelId)
return view.viewsModel(this.targetModelId); // If a specific target model is specified, only allow view that shows it.
if (view.isSpatialView() && this.targetView.view.isSpatialView())
return true; // No specific target, two spatial views are considered compatible.
let allowView = false;
const targetView = this.targetView;
view.forEachModel((model) => {
if (!allowView && targetView.view.viewsModel(model.id))
allowView = true;
});
return allowView; // Accept if this view shares a model in common with target.
}
/**
* Checks that the adjusted point from the supplied button event is within the project extents for spatial views. The range of physical geometry
* should always be fully inside the project extents. Only checking the adjusted point won't absolutely guarantee that a tool doesn't create/move geometry
* outside the project extents, but it will be sufficient to handle most cases and provide good feedback to the user.
* @return true if ev is acceptable.
*/
isValidLocation(ev, isButtonEvent) {
const vp = ev.viewport;
if (undefined === vp)
return false;
if (isButtonEvent && Tool_1.BeButton.Data !== ev.button)
return true;
const view = vp.view;
if (!view.isSpatialView())
return true;
// NOTE: If points aren't being adjusted then the tool shouldn't be creating geometry currently (ex. locating elements) and we shouldn't filter point...
if (0 !== (IModelApp_1.IModelApp.toolAdmin.toolState.coordLockOvr & Tool_1.CoordinateLockOverrides.ACS))
return true;
// We know the tool isn't doing a locate, we don't know what it will do with this point. Minimize erroneous filtering by restricting the check to when AccuSnap is tool enable (not user enabled)...
if (!IModelApp_1.IModelApp.accuSnap.isSnapEnabled)
return true;
const extents = view.iModel.projectExtents;
if (extents.containsPoint(ev.point))
return true;
if (isButtonEvent && ev.isDown)
IModelApp_1.IModelApp.notifications.outputMessage(new NotificationManager_1.NotifyMessageDetails(NotificationManager_1.OutputMessagePriority.Error, Tool_1.CoreTools.translate("ElementSet.Error.ProjectExtents")));
return false;
}
/** Called on data button down event to lock the tool to its current target model. */
autoLockTarget() {
if (undefined === this.targetView)
return;
this.targetIsLocked = true;
}
/** Returns the prompt based on the tool's current state. */
getPrompt() { return ""; }
/** Called from isCompatibleViewport to check for a read only iModel, which is not a valid target for tools that create or modify elements. */
requireWriteableTarget() { return true; }
/**
* Called when active view changes. Tool may choose to restart or exit based on current view type.
* @param _previous The previously active view.
* @param current The new active view.
*/
async onSelectedViewportChanged(_previous, current) {
if (this.isCompatibleViewport(current, true))
return;
return this.onRestartTool();
}
/**
* Called to reset tool to initial state. PrimitiveTool implements this method to call onRestartTool.
*/
async onReinitialize() { return this.onRestartTool(); }
async exitTool() { return IModelApp_1.IModelApp.toolAdmin.startDefaultTool(); }
/**
* Called to reverse to a previous tool state (ex. undo last data button).
* @return false to instead reverse the most recent transaction.
*/
async onUndoPreviousStep() { return false; }
/** @internal */
async undoPreviousStep() {
if (!await this.onUndoPreviousStep())
return false;
AccuDrawTool_1.AccuDrawShortcuts.processPendingHints(); // Process pending hints from onUndoPreviousStep before calling updateDynamics...
IModelApp_1.IModelApp.viewManager.invalidateDecorationsAllViews();
IModelApp_1.IModelApp.toolAdmin.updateDynamics(undefined, undefined, true); // Don't wait for motion to update dynamics...
return true;
}
/**
* Called to reinstate to a previous tool state (ex. redo last data button).
* @return false to instead reinstate the most recent transaction.
*/
async onRedoPreviousStep() { return false; }
/** @internal */
async redoPreviousStep() {
if (!await this.onRedoPreviousStep())
return false;
AccuDrawTool_1.AccuDrawShortcuts.processPendingHints(); // Process pending hints from onRedoPreviousStep before calling updateDynamics...
IModelApp_1.IModelApp.viewManager.invalidateDecorationsAllViews();
IModelApp_1.IModelApp.toolAdmin.updateDynamics(undefined, undefined, true); // Don't wait for motion to update dynamics...
return true;
}
/** If this tool is editing a briefcase, commits any elements that the tool has changed, supplying the tool flyover for the undo description. */
async saveChanges() {
if (this.iModel.isBriefcaseConnection())
return this.iModel.saveChanges(this.flyover);
}
}
exports.PrimitiveTool = PrimitiveTool;
//# sourceMappingURL=PrimitiveTool.js.map