UNPKG

@itwin/core-frontend

Version:
171 lines 9.13 kB
"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