6
6
import * as React from "react" ;
7
7
import type { XAndY } from "@itwin/core-geometry" ;
8
8
import { Point2d } from "@itwin/core-geometry" ;
9
- import { RelativePosition } from "@itwin/appui-abstract" ;
10
9
import type { ItemProps , ToolbarActionItem } from "@itwin/appui-react" ;
11
- import { ActionButtonItemDef , CursorInformation , CursorPopupManager , ToolbarItemUtilities } from "@itwin/appui-react" ;
10
+ import {
11
+ ActionButtonItemDef ,
12
+ CursorInformation ,
13
+ PopupManager ,
14
+ PositionPopup ,
15
+ Toolbar ,
16
+ ToolbarItemUtilities
17
+ } from "@itwin/appui-react" ;
18
+ import { IModelApp } from "@itwin/core-frontend" ;
19
+ import { Direction } from "@itwin/components-react" ;
20
+
12
21
import { FeatureTracking , MeasureToolsFeatures } from "../api/FeatureTracking.js" ;
13
22
import type { Measurement , MeasurementPickContext } from "../api/Measurement.js" ;
14
23
import { MeasurementManager } from "../api/MeasurementManager.js" ;
@@ -102,7 +111,6 @@ export class MeasurementActionDefinitions {
102
111
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.unlock" ) ,
103
112
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.unlock" ) ,
104
113
execute : ( args : Measurement [ ] ) => {
105
-
106
114
args . forEach ( ( m ) => {
107
115
if ( m . isLocked ) {
108
116
m . isLocked = false ;
@@ -124,7 +132,6 @@ export class MeasurementActionDefinitions {
124
132
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.lock" ) ,
125
133
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.lock" ) ,
126
134
execute : ( args : Measurement [ ] ) => {
127
-
128
135
args . forEach ( ( m ) => {
129
136
if ( ! m . isLocked ) {
130
137
m . isLocked = true ;
@@ -145,7 +152,6 @@ export class MeasurementActionDefinitions {
145
152
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.hideMeasurements" ) ,
146
153
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.hideMeaurements" ) ,
147
154
execute : ( args : Measurement [ ] ) => {
148
-
149
155
args . forEach ( ( m ) => {
150
156
if ( m . isVisible ) {
151
157
m . isVisible = false ;
@@ -165,7 +171,6 @@ export class MeasurementActionDefinitions {
165
171
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.displayMeasurements" ) ,
166
172
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.displayMeasurements" ) ,
167
173
execute : ( args : Measurement [ ] ) => {
168
-
169
174
args . forEach ( ( m ) => {
170
175
if ( ! m . isVisible ) {
171
176
m . isVisible = true ;
@@ -185,7 +190,6 @@ export class MeasurementActionDefinitions {
185
190
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.displayLabels" ) ,
186
191
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.displayLabels" ) ,
187
192
execute : ( args : Measurement [ ] ) => {
188
-
189
193
args . forEach ( ( m ) => {
190
194
if ( ! m . displayLabels ) {
191
195
m . displayLabels = true ;
@@ -205,7 +209,6 @@ export class MeasurementActionDefinitions {
205
209
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.hideLabels" ) ,
206
210
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.hideLabels" ) ,
207
211
execute : ( args : Measurement [ ] ) => {
208
-
209
212
args . forEach ( ( m ) => {
210
213
if ( m . displayLabels ) {
211
214
m . displayLabels = false ;
@@ -225,7 +228,6 @@ export class MeasurementActionDefinitions {
225
228
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.displayMeasurementAxes" ) ,
226
229
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.displayMeasurementAxes" ) ,
227
230
execute : ( args : Measurement [ ] ) => {
228
-
229
231
args . forEach ( ( m ) => {
230
232
if ( m instanceof DistanceMeasurement && ! m . showAxes ) {
231
233
m . showAxes = true ;
@@ -245,7 +247,6 @@ export class MeasurementActionDefinitions {
245
247
label : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.hideMeasurementAxes" ) ,
246
248
tooltip : ( ) => MeasureTools . localization . getLocalizedString ( "MeasureTools:Generic.hideMeasurementAxes" ) ,
247
249
execute : ( args : Measurement [ ] ) => {
248
-
249
250
args . forEach ( ( m ) => {
250
251
if ( m instanceof DistanceMeasurement && m . showAxes ) {
251
252
m . showAxes = false ;
@@ -312,33 +313,53 @@ export class MeasurementActionToolbar {
312
313
* @param measurements array of Measurement the actions are for. First one is always the one that initiated the event.
313
314
* @param screenPoint Where on the screen the toolbar is to be positioned (e.g. cursor point)
314
315
* @param offset Optional offset from the position. Default is (0,0)
315
- * @param relativePosition Optional direction the toolbar will open from. Default is Top (so will be above and centered from point + offset).
316
316
* @returns true if the toolbar was opened, false if otherwise (e.g. no action items to view).
317
317
*/
318
- public static openToolbar ( measurements : Measurement [ ] , screenPoint : XAndY , offset ?: XAndY , relativePosition ?: RelativePosition ) : boolean {
318
+ public static openToolbar ( measurements : Measurement [ ] , screenPoint : XAndY , offset ?: XAndY ) : boolean {
319
319
// Ensure a previous toolbar was closed out
320
320
this . closeToolbar ( false ) ;
321
321
322
322
const measurementsForActions = measurements . filter ( ( m ) => m . allowActions ) ;
323
- if ( measurementsForActions . length === 0 )
324
- return false ;
323
+ if ( measurementsForActions . length === 0 ) return false ;
325
324
326
- if ( this . _filterHandler && ! this . _filterHandler ( measurementsForActions ) )
327
- return false ;
325
+ if ( this . _filterHandler && ! this . _filterHandler ( measurementsForActions ) ) return false ;
328
326
329
327
// Query all action items...if have none, do not show the toolbar
330
328
const itemList = this . buildActionList ( measurementsForActions ) ;
331
- if ( itemList . length === 0 )
332
- return false ;
329
+ if ( itemList . length === 0 ) return false ;
333
330
334
331
// Build toolbar ID, making it unique so we can fade out a previous toolbar and not have that interfere with a new toolbar
335
332
this . _lastPopupId = `measurement-action-toolbar-${ this . _counter . toString ( ) } ` ;
336
333
this . _counter ++ ;
337
334
338
335
// Show toolbar
339
- const realOffset = ( offset !== undefined ) ? offset : Point2d . createZero ( ) ;
340
- const realRelPosition = ( relativePosition !== undefined ) ? relativePosition : RelativePosition . Top ;
341
- CursorPopupManager . open ( this . _lastPopupId , this . buildToolbar ( measurementsForActions , itemList ) , screenPoint , realOffset , realRelPosition ) ;
336
+ const realOffset = offset !== undefined ? offset : Point2d . createZero ( ) ;
337
+ const parentDocument = IModelApp . viewManager . selectedView ?. vpDiv . ownerDocument ;
338
+ if ( ! parentDocument ) {
339
+ return false ;
340
+ }
341
+
342
+ const toolItems : ToolbarActionItem [ ] = itemList . map ( ( itemDef : MeasurementActionItemDef , index ) => {
343
+ itemDef . measurements = measurements ;
344
+ return ToolbarItemUtilities . createActionItem ( itemDef . id , index * 10 , itemDef . iconSpec , itemDef . label , itemDef . execute ) ;
345
+ } ) ;
346
+ // Center the toolbar horizontally and position it to specified offset above the cursor point
347
+ const toolbarWidth = toolItems . length * 36 ; // Approximate width based on typical button size
348
+ const point = {
349
+ x : screenPoint . x - realOffset . x - toolbarWidth / 2 ,
350
+ y : screenPoint . y - realOffset . y ,
351
+ } ;
352
+ const component = (
353
+ < PositionPopup point = { point } >
354
+ < Toolbar expandsTo = { Direction . Top } items = { toolItems } />
355
+ </ PositionPopup >
356
+ ) ;
357
+ PopupManager . addOrUpdatePopup ( {
358
+ id : this . _lastPopupId ,
359
+ pt : point ,
360
+ component,
361
+ parentDocument,
362
+ } ) ;
342
363
343
364
FeatureTracking . notifyFeature ( MeasureToolsFeatures . MeasurementActionsToolbar_Open ) ;
344
365
@@ -351,17 +372,15 @@ export class MeasurementActionToolbar {
351
372
public static closeToolbar ( fadeOut : boolean = false ) : void {
352
373
// Forcibly close a fading toolbar if we get another close call otherwise they may interfere with each other
353
374
if ( this . _fadeoutPopId !== undefined ) {
354
- CursorPopupManager . close ( this . _fadeoutPopId , false , false ) ;
375
+ PopupManager . removePopup ( this . _fadeoutPopId ) ;
355
376
this . _fadeoutPopId = undefined ;
356
377
}
357
378
358
- if ( this . _lastPopupId === undefined )
359
- return ;
379
+ if ( this . _lastPopupId === undefined ) return ;
360
380
361
- if ( fadeOut )
362
- this . _fadeoutPopId = this . _lastPopupId ;
381
+ if ( fadeOut ) this . _fadeoutPopId = this . _lastPopupId ;
363
382
364
- CursorPopupManager . close ( this . _lastPopupId , false , fadeOut ) ;
383
+ PopupManager . removePopup ( this . _lastPopupId ) ;
365
384
this . _lastPopupId = undefined ;
366
385
}
367
386
@@ -373,7 +392,7 @@ export class MeasurementActionToolbar {
373
392
// Filter out duplicates
374
393
this . dropActionProvider ( provider ) ;
375
394
376
- const realPriority = ( providerPriority !== undefined ) ? providerPriority : 0 ;
395
+ const realPriority = providerPriority !== undefined ? providerPriority : 0 ;
377
396
this . _actionProviders . push ( { priority : realPriority , provider } ) ;
378
397
379
398
this . _actionProviders . sort ( ( a , b ) => b . priority - a . priority ) ;
@@ -384,8 +403,7 @@ export class MeasurementActionToolbar {
384
403
*/
385
404
public static dropActionProvider ( provider : MeasurementActionProvider ) : void {
386
405
for ( let i = 0 ; i < this . _actionProviders . length ; i ++ ) {
387
- if ( this . _actionProviders [ i ] . provider === provider )
388
- this . _actionProviders . splice ( i , 1 ) ;
406
+ if ( this . _actionProviders [ i ] . provider === provider ) this . _actionProviders . splice ( i , 1 ) ;
389
407
}
390
408
}
391
409
@@ -397,31 +415,31 @@ export class MeasurementActionToolbar {
397
415
/** Sets a default provider (which can be later cleared). This sets lock/unlock, delete, and open properties action buttons. */
398
416
public static setDefaultActionProvider ( ) {
399
417
MeasurementActionToolbar . addActionProvider ( ( measurements : Measurement [ ] , actionItemList : MeasurementActionItemDef [ ] ) => {
400
-
401
418
let allMeasurementsShowingLabels = true ;
402
419
let allMeasurementsLocked = true ;
403
420
let hasDistanceMeasurements = false ;
404
421
let allDistanceMeasurementsShowingAxes = true ;
405
422
406
423
for ( const measurement of measurements ) {
407
- if ( ! measurement . isLocked )
408
- allMeasurementsLocked = false ;
424
+ if ( ! measurement . isLocked ) allMeasurementsLocked = false ;
409
425
410
- if ( ! measurement . displayLabels )
411
- allMeasurementsShowingLabels = false ;
426
+ if ( ! measurement . displayLabels ) allMeasurementsShowingLabels = false ;
412
427
413
428
if ( measurement instanceof DistanceMeasurement ) {
414
429
hasDistanceMeasurements = true ;
415
- if ( ! measurement . showAxes )
416
- allDistanceMeasurementsShowingAxes = false ;
430
+ if ( ! measurement . showAxes ) allDistanceMeasurementsShowingAxes = false ;
417
431
}
418
432
}
419
433
420
- actionItemList . push ( ( allMeasurementsLocked ) ? MeasurementActionDefinitions . unlockAction : MeasurementActionDefinitions . lockAction ) ;
421
- actionItemList . push ( ( allMeasurementsShowingLabels ) ? MeasurementActionDefinitions . hideMeasurementLabels : MeasurementActionDefinitions . displayMeasurementLabels ) ;
434
+ actionItemList . push ( allMeasurementsLocked ? MeasurementActionDefinitions . unlockAction : MeasurementActionDefinitions . lockAction ) ;
435
+ actionItemList . push (
436
+ allMeasurementsShowingLabels ? MeasurementActionDefinitions . hideMeasurementLabels : MeasurementActionDefinitions . displayMeasurementLabels ,
437
+ ) ;
422
438
423
439
if ( hasDistanceMeasurements )
424
- actionItemList . push ( ( allDistanceMeasurementsShowingAxes ) ? MeasurementActionDefinitions . hideMeasurementAxes : MeasurementActionDefinitions . displayMeasurementAxes ) ;
440
+ actionItemList . push (
441
+ allDistanceMeasurementsShowingAxes ? MeasurementActionDefinitions . hideMeasurementAxes : MeasurementActionDefinitions . displayMeasurementAxes ,
442
+ ) ;
425
443
426
444
actionItemList . push ( MeasurementActionDefinitions . deleteAction ) ;
427
445
actionItemList . push ( MeasurementActionDefinitions . openPropertiesAction ) ;
@@ -431,8 +449,7 @@ export class MeasurementActionToolbar {
431
449
private static buildActionList ( measurements : Measurement [ ] ) : MeasurementActionItemDef [ ] {
432
450
const itemList = new Array < MeasurementActionItemDef > ( ) ;
433
451
434
- for ( const entry of this . _actionProviders )
435
- entry . provider ( measurements , itemList ) ;
452
+ for ( const entry of this . _actionProviders ) entry . provider ( measurements , itemList ) ;
436
453
437
454
return itemList ;
438
455
}
@@ -442,7 +459,7 @@ export class MeasurementActionToolbar {
442
459
itemDef . measurements = measurements ;
443
460
return ToolbarItemUtilities . createActionItem ( itemDef . id , index * 10 , itemDef . iconSpec , itemDef . label , itemDef . execute ) ;
444
461
} ) ;
445
- return < PopupToolbar items = { toolItems } onClose = { ( ) => MeasurementActionToolbar . closeToolbar ( true ) } /> ;
462
+ return < PopupToolbar items = { toolItems } onClose = { ( ) => MeasurementActionToolbar . closeToolbar ( true ) } /> ;
446
463
}
447
464
}
448
465
@@ -462,7 +479,7 @@ ShimFunctions.defaultButtonEventAction = (measurement: Measurement, pickContext:
462
479
measurements . unshift ( measurement ) ;
463
480
464
481
// Open toolbar for measurement
465
- MeasurementActionToolbar . openToolbar ( measurements , CursorInformation . cursorPosition , Point2d . create ( 0 , 10 ) ) ;
482
+ MeasurementActionToolbar . openToolbar ( measurements , CursorInformation . cursorPosition , Point2d . create ( 0 , 60 ) ) ;
466
483
467
484
// Notify global event that this measurement is responding to the button
468
485
MeasurementManager . instance . notifyMeasurementButtonEvent ( measurement , pickContext ) ;
0 commit comments