@itwin/measure-tools-react
Version:
Frontend framework and tools for measurements
242 lines • 9.75 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { BeDuration, BeUiEvent } from "@itwin/core-bentley";
import { EventHandled, IModelApp, NotifyMessageDetails, OutputMessageAlert, OutputMessagePriority, OutputMessageType, PrimitiveTool, ToolAssistance, ToolAssistanceInputMethod, } from "@itwin/core-frontend";
import { MeasureTools } from "../MeasureTools.js";
import { FeatureTracking } from "./FeatureTracking.js";
import { SheetMeasurementsHelper } from "./SheetMeasurementHelper.js";
/** Namespace for functions relating to the MeasurementTool interface. */
export var MeasurementTool;
(function (MeasurementTool) {
/** Event for when a tool creates a new measurement. */
MeasurementTool.onNewMeasurement = new BeUiEvent();
/** Event when the tool's dynamic measurement has changed. */
MeasurementTool.onDynamicMeasurementChanged = new BeUiEvent();
/** Gets the active measurement tool, if it exists.
* @returns the measurement tool, or undefined if the current tool is not one.
*/
function getActiveMeasurementTool() {
const tool = IModelApp.toolAdmin.currentTool;
if (tool === undefined ||
!("measurements" in tool) ||
!("persistMeasurements" in tool) ||
!("clearMeasurements" in tool))
return undefined;
return tool;
}
MeasurementTool.getActiveMeasurementTool = getActiveMeasurementTool;
})(MeasurementTool || (MeasurementTool = {}));
/**
* Helper class to hold the selection state during the operation of a tool. Call saveSelection on install and restoreSelection on cleanup.
*/
export class SelectionHolder {
constructor() {
this._selectedIds = new Set();
}
saveSelection(imodel, clearSelectionAfter) {
this._imodel = imodel;
this._selectedIds.clear();
imodel.selectionSet.elements.forEach((id) => this._selectedIds.add(id));
if (clearSelectionAfter)
imodel.selectionSet.emptyAll();
}
restoreSelection() {
if (!this._imodel)
return;
this._imodel.selectionSet.replace(this._selectedIds);
this._imodel = undefined;
this._selectedIds.clear();
}
}
/** Useful base class for tools */
export class PrimitiveToolBase extends PrimitiveTool {
get feature() {
return undefined;
}
async onPostInstall() {
await super.onPostInstall();
const feature = this.feature;
if (feature)
FeatureTracking.notifyFeature(feature);
}
// Recommended way to show feedback to the user in a consistant manor
showMessage(priority, briefMessage, detailedMessage) {
const msg = new NotifyMessageDetails(priority, briefMessage, detailedMessage, OutputMessageType.Toast, OutputMessageAlert.Balloon);
msg.displayTime = BeDuration.fromSeconds(5.0);
IModelApp.notifications.outputMessage(msg);
}
async showException(err) {
const priority = OutputMessagePriority.Error;
const msg = err.message || err.name || typeof err;
this.showMessage(priority, msg);
}
createMouseUndoInstruction(textOverride) {
const text = textOverride ||
MeasureTools.localization.getLocalizedString("MeasureTools:Generic.undoMeasurement");
return ToolAssistance.createKeyboardInstruction(ToolAssistance.createKeyboardInfo([ToolAssistance.ctrlKey, "Z"]), text, false, ToolAssistanceInputMethod.Mouse);
}
createMouseRedoInstruction(textOverride) {
const text = textOverride ||
MeasureTools.localization.getLocalizedString("MeasureTools:Generic.redoMeasurement");
return ToolAssistance.createKeyboardInstruction(ToolAssistance.createKeyboardInfo([ToolAssistance.ctrlKey, "Y"]), text, false, ToolAssistanceInputMethod.Mouse);
}
// For most of our cases we expect to draw our decorations when suspended also
decorateSuspended(context) {
this.decorate(context);
}
}
/** Useful base class for measurement tools which maintains a tool model that a subclass will instantiate as well as save the current selection/restore on exit.
* Generally a measurement tool model contains all the logic to create and manage measurements dynamically, while the tool is responsible
* for sending input events to the model.
*/
export class MeasurementToolBase extends PrimitiveToolBase {
get measurements() {
return this._toolModel.measurements;
}
get dynamicMeasurement() {
return this._toolModel.dynamicMeasurement;
}
get toolModel() {
return this._toolModel;
}
get selectionHolder() {
return this._selectionHolder;
}
get saveRestoreSelection() {
return true;
}
get allowedDrawingTypes() {
return [];
}
constructor(allowedViewportCallback = (() => true)) {
super();
this._allowedViewportCallback = allowedViewportCallback;
this._enableSheetMeasurements = false;
this._toolModel = this.createToolModel();
this._toolModel.synchMeasurementsWithSelectionSet = true; // Sync by default
this._selectionHolder = new SelectionHolder();
this.setupEvents();
}
requireWriteableTarget() {
return false;
}
isValidLocation(ev, _isButtonEvent) {
if (ev.viewport)
return this._allowedViewportCallback(ev.viewport);
return true;
}
async onPostInstall() {
await super.onPostInstall();
if (this.saveRestoreSelection)
this._selectionHolder.saveSelection(this.iModel, true);
this._toolModel.initialize();
this.updateAccuSnap();
this.updateToolAssistance();
}
async onCleanup() {
await super.onCleanup();
if (this.saveRestoreSelection)
this._selectionHolder.restoreSelection();
// Persist measurements when the tool exits
this._toolModel.persistMeasurements();
this._toolModel.cleanup();
}
async onReinitialize() {
this.toolModel.reset(false);
this.updateAccuSnap();
this.updateToolAssistance();
}
async onUndoPreviousStep() {
// If we have an active measurement, reset to initial state
if (undefined !== this._toolModel.dynamicMeasurement) {
await this.onReinitialize();
return true;
}
// Start undoing existing measurements
if (this._toolModel.canUndo) {
const result = this._toolModel.undoMeasurement();
this.updateToolAssistance();
return result;
}
const message = MeasureTools.localization.getLocalizedString("MeasureTools:Generic.nothingToUndo");
this.showMessage(OutputMessagePriority.Info, message);
return false;
}
async onRedoPreviousStep() {
// Probably not a good idea if there are ongoing measurements
if (undefined !== this._toolModel.dynamicMeasurement)
return false;
if (this._toolModel.canRedo) {
const result = this._toolModel.redoMeasurement();
this.updateToolAssistance();
return result;
}
const message = MeasureTools.localization.getLocalizedString("MeasureTools:Generic.nothingToRedo");
this.showMessage(OutputMessagePriority.Info, message);
return false;
}
async onResetButtonDown(_ev) {
if (undefined !== this.toolModel.dynamicMeasurement)
await this.onReinitialize();
else
await this.onRestartTool();
return EventHandled.Yes;
}
async onKeyTransition(wentDown, keyEvent) {
if (EventHandled.Yes === (await super.onKeyTransition(wentDown, keyEvent)))
return EventHandled.Yes;
if (wentDown && "escape" === keyEvent.key.toLowerCase()) {
await this.exitTool();
return EventHandled.Yes;
}
return EventHandled.No;
}
testDecorationHit(id) {
return this._toolModel.testDecorationHit(id);
}
getDecorationGeometry(hit) {
return this._toolModel.getDecorationGeometry(hit);
}
async getToolTip(hit) {
const defaultToolTip = async (hit) => {
const toolTip = await this._toolModel.getToolTip(hit);
if (toolTip === "")
return super.getToolTip(hit);
return toolTip;
};
if (!this._enableSheetMeasurements) {
return defaultToolTip(hit);
}
else {
return SheetMeasurementsHelper.getSheetToolTipText(hit, this.allowedDrawingTypes, defaultToolTip);
}
}
decorate(context) {
this._toolModel.decorate(context);
}
async onUnsuspend() {
this.updateAccuSnap();
this.updateToolAssistance();
}
persistMeasurements() {
return this._toolModel.persistMeasurements();
}
clearMeasurements(viewportType) {
this._toolModel.clearMeasurements(viewportType);
}
updateAccuSnap() {
IModelApp.accuSnap.enableSnap(true);
}
updateToolAssistance() { }
setupEvents() {
this._toolModel.onNewMeasurement.addListener((args) => {
MeasurementTool.onNewMeasurement.emit(args);
});
this._toolModel.onDynamicMeasurementChanged.addListener((args) => {
MeasurementTool.onDynamicMeasurementChanged.emit(args);
});
}
}
//# sourceMappingURL=MeasurementTool.js.map