瀏覽代碼

update(measure): 新增【右键退出当前测量】功能 #0009381 Review by melon

gavin.chen 2 年之前
父節點
當前提交
767f62215e

+ 3 - 0
lib/interfaces/process/items/item.dart

@@ -60,6 +60,9 @@ abstract class IMeasureItem {
 
   /// 快照变化事件
   late final FEventHandler<IMeasureItemFeature?> featureUpdated;
+
+  /// 快照变化事件
+  late final FEventHandler<ItemStates?> onItemStatesChanged;
 }
 
 /// 组合(包含子项)测量项

+ 3 - 0
lib/interfaces/process/items/types.dart

@@ -5,6 +5,9 @@
 class MeasureTypes {
   MeasureTypes._();
 
+  /// 空类型,用于结束测量
+  static const Empty = 'Empty';
+
 /* Location [begin] */
   /// 深度
   static const Depth = "Depth";

+ 3 - 0
lib/process/items/factory.dart

@@ -14,6 +14,7 @@ import 'package:fis_measure/process/primitives/combos/two_ray.dart';
 import 'package:fis_measure/process/primitives/combos/two_straightline.dart';
 import 'package:fis_measure/process/primitives/detection.dart';
 import 'package:fis_measure/process/primitives/ellipse.dart';
+import 'package:fis_measure/process/primitives/empty.dart';
 import 'package:fis_measure/process/primitives/location.dart';
 import 'package:fis_measure/process/primitives/multi_method/multiple_trace.dart';
 import 'package:fis_measure/process/primitives/polyline.dart';
@@ -79,6 +80,8 @@ class MeasureItemFactory {
   }
 
   static void _registerItemCreators() {
+    // Empty
+    _singleton._register(MeasureTypes.Empty, Empty.createEmpty);
     // Locations
     _singleton._register(MeasureTypes.Depth, Location.createTissueDepth);
     _singleton._register(

+ 5 - 0
lib/process/items/item.dart

@@ -74,6 +74,7 @@ abstract class MeasureItem<T extends MeasureItemFeature> extends IMeasureItem {
   set state(ItemStates value) {
     if (value != _state) {
       _state = value;
+      onItemStatesChanged.emit(this, value);
     }
   }
 
@@ -89,6 +90,10 @@ abstract class MeasureItem<T extends MeasureItemFeature> extends IMeasureItem {
   @override
   late final FEventHandler<IMeasureItemFeature?> featureUpdated;
 
+  @override
+  final FEventHandler<ItemStates?> onItemStatesChanged =
+      FEventHandler<ItemStates?>();
+
   @override
   bool execute(PointInfo args) {
     bool hasFeatureBefore = feature != null;

+ 42 - 0
lib/process/primitives/empty.dart

@@ -0,0 +1,42 @@
+import 'dart:ui';
+import 'package:fis_measure/interfaces/date_types/point.dart';
+import 'package:fis_measure/interfaces/process/items/item.dart';
+import 'package:fis_measure/interfaces/process/items/item_metas.dart';
+import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
+import '../items/item.dart';
+import '../items/item_feature.dart';
+
+/// 空测量项,用于右键结束测量时的占位
+class Empty extends MeasureItem<EmptyFeature> {
+  Empty(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
+
+  @override
+  bool get finishAfterUnactive => true;
+
+  @override
+  bool onExecuteMouse(PointInfo args) {
+    return true;
+  }
+
+  @override
+  bool onExecuteTouch(PointInfo args) {
+    return true;
+  }
+
+  static Empty createEmpty(
+    ItemMeta meta, [
+    IMeasureItem? parent,
+  ]) {
+    Empty location = Empty(meta, parent);
+    return location;
+  }
+}
+
+class EmptyFeature extends MeasureItemFeature {
+  EmptyFeature(IMeasureItem refItem, DPoint point) : super(refItem) {
+    innerPoints.add(point);
+  }
+
+  @override
+  void paint(Canvas canvas, Size size) {}
+}

+ 30 - 0
lib/process/workspace/measure_handler.dart

@@ -72,6 +72,9 @@ abstract class IMeasureHandler {
   /// 拖拽状态发生改变
   late FEventHandler<bool> onDragStateChanged;
 
+  /// 鼠标右键取消测量
+  late FEventHandler<bool> onRightClickFinishMeasure;
+
   /// 图片加载loadding
   late FEventHandler<bool> onChangeImageLoaded;
 
@@ -83,6 +86,8 @@ abstract class IMeasureHandler {
 
   late FEventHandler<bool> onChangeFullScreenState;
 
+  late FEventHandler<bool> onToolPanelStateChanged;
+
   late FEventHandler<void> annotationsLoaded;
 
   /// 切换图像传递数据
@@ -112,6 +117,10 @@ abstract class IMeasureHandler {
   /// 全屏状态
   bool get fullScreenState;
   set fullScreenState(bool value);
+
+  /// 工具面板状态
+  bool get toolPanelState;
+  set toolPanelState(bool value);
 }
 
 class MeasureHandler implements IMeasureHandler {
@@ -128,6 +137,8 @@ class MeasureHandler implements IMeasureHandler {
 
   bool _fullScreenState = false;
 
+  bool _toolPanelState = true;
+
   AnnotationType _changedAnnotationType = AnnotationType.label;
   @override
   var onImageChanged = FEventHandler<ChangeImageInfo>();
@@ -144,6 +155,8 @@ class MeasureHandler implements IMeasureHandler {
   var onChangedAnnotationType = FEventHandler<AnnotationType>();
   @override
   var onDragStateChanged = FEventHandler<bool>();
+  @override
+  var onRightClickFinishMeasure = FEventHandler<bool>();
 
   @override
   FEventHandler<bool> onChangeImageLoaded = FEventHandler<bool>();
@@ -157,6 +170,9 @@ class MeasureHandler implements IMeasureHandler {
   @override
   FEventHandler<bool> onChangeFullScreenState = FEventHandler<bool>();
 
+  @override
+  FEventHandler<bool> onToolPanelStateChanged = FEventHandler<bool>();
+
   @override
   var annotationsLoaded = FEventHandler<void>();
 
@@ -188,6 +204,16 @@ class MeasureHandler implements IMeasureHandler {
     }
   }
 
+  @override
+  bool get toolPanelState => _toolPanelState;
+  @override
+  set toolPanelState(bool value) {
+    if (value != _toolPanelState) {
+      _toolPanelState = value;
+      _onToolPanelStateChanged();
+    }
+  }
+
   @override
   set changeImageLoaded(bool value) {
     if (value != _changeImageLoaded) {
@@ -260,6 +286,10 @@ class MeasureHandler implements IMeasureHandler {
     onChangeFullScreenState.emit(this, fullScreenState);
   }
 
+  void _onToolPanelStateChanged() {
+    onToolPanelStateChanged.emit(this, toolPanelState);
+  }
+
   void _onChangedAnnotation() {
     onChangedAnnotationType.emit(this, changedAnnotationType);
   }

+ 3 - 1
lib/process/workspace/recorder.dart

@@ -39,6 +39,7 @@ class MeasureRecorder implements IMeasureRecorder {
 
   /// 撤销一次操作
   bool undoOnce() {
+    if (_lastId == 0) return false;
     _finishLast();
     _cleanMeasureList();
 
@@ -80,8 +81,9 @@ class MeasureRecorder implements IMeasureRecorder {
 
   void _finishLast() {
     if (_application.currentOperateType == MeasureOperateType.measure) {
-      _application.activeAnnotationItem?.finishLast();
       _application.activeMeasureItem?.finishOnce();
+    } else {
+      _application.activeAnnotationItem?.finishLast();
     }
   }
 

+ 139 - 39
lib/view/gesture/mouse_gesture.dart

@@ -1,9 +1,16 @@
+import 'package:fis_i18n/i18n.dart';
 import 'package:fis_measure/interfaces/date_types/point.dart';
 import 'package:fis_measure/interfaces/date_types/rect.dart';
 import 'package:fis_measure/interfaces/date_types/rect_region.dart';
+import 'package:fis_measure/interfaces/enums/items.dart';
+import 'package:fis_measure/interfaces/process/items/item.dart';
 import 'package:fis_measure/interfaces/process/workspace/application.dart';
 import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/items/top_item.dart';
+import 'package:fis_measure/process/workspace/measure_handler.dart';
 import 'package:fis_measure/view/gesture/cross_position_indicator.dart';
+import 'package:fis_ui/index.dart';
+import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 import 'package:vid/us/vid_us_visual_area_type.dart';
@@ -23,69 +30,88 @@ class _MeasureMouseGesturePanelState extends State<MeasureMouseGesturePanel> {
 
   final mouseState = Get.find<IMouseState>();
   late final application = Get.find<IApplication>();
+  late final measureHandler = Get.find<MeasureHandler>();
   bool ifContainerTissueTM = false;
   CursorDisplayType displayType = CursorDisplayType.none;
   RectRegion tissueTMPixelRegion = RectRegion();
+  bool isShowRightClickTip = false;
+  late IMeasureItem? currMeasureItem;
 
   @override
   void initState() {
     _findMultiRegions();
     application.displaySizeChanged.addListener(_onResize);
+    application.activeMeasureItemChanged
+        .addListener(_onActiveMeasureItemChanged);
+    currMeasureItem = application.activeMeasureItem;
+    if (currMeasureItem != null) {
+      currMeasureItem!.onItemStatesChanged.addListener(_onItemStatesChanged);
+    }
     super.initState();
   }
 
   @override
   void dispose() {
     application.displaySizeChanged.removeListener(_onResize);
+    application.activeMeasureItemChanged
+        .removeListener(_onActiveMeasureItemChanged);
+    if (currMeasureItem != null) {
+      currMeasureItem!.onItemStatesChanged.removeListener(_onItemStatesChanged);
+    }
     super.dispose();
   }
 
   @override
   Widget build(BuildContext context) {
-    return GestureDetector(
-      onTapDown: (details) {
-        application.createPointInfo(
-          details.localPosition,
-          PointInfoType.mouseDown,
-        );
-      },
-      onPanDown: (details) {
-        // application.createPointInfo(
-        //   details.localPosition,
-        //   PointInfoType.mouseDown,
-        // );
-      },
-      onPanUpdate: (details) {
-        mouseState.mousePosition = details.localPosition;
-        application.createPointInfo(
-          details.localPosition,
-          PointInfoType.mouseMove,
-        );
-      },
-      child: MouseRegion(
-        cursor: SystemMouseCursors.none,
-        onHover: (event) {
-          mouseState.mousePosition = event.localPosition;
+    return Listener(
+      onPointerDown: _rightClickListener,
+      child: GestureDetector(
+        onTapDown: (details) {
           application.createPointInfo(
-            event.localPosition,
-            PointInfoType.mouseMove,
+            details.localPosition,
+            PointInfoType.mouseDown,
           );
-          _handleAreaChange(event.localPosition);
         },
-        onEnter: (e) {
-          setState(() {
-            displayType = CursorDisplayType.normal;
-          });
+        onPanDown: (details) {
+          // application.createPointInfo(
+          //   details.localPosition,
+          //   PointInfoType.mouseDown,
+          // );
         },
-        onExit: (e) {
-          setState(() {
-            displayType = CursorDisplayType.none;
-          });
+        onPanUpdate: (details) {
+          mouseState.mousePosition = details.localPosition;
+          application.createPointInfo(
+            details.localPosition,
+            PointInfoType.mouseMove,
+          );
         },
-        child: Stack(
-          children: [
-            _buildCursor(),
-          ],
+        child: MouseRegion(
+          cursor: SystemMouseCursors.none,
+          onHover: (event) {
+            mouseState.mousePosition = event.localPosition;
+            application.createPointInfo(
+              event.localPosition,
+              PointInfoType.mouseMove,
+            );
+            _handleAreaChange(event.localPosition);
+          },
+          onEnter: (e) {
+            setState(() {
+              displayType = CursorDisplayType.normal;
+            });
+          },
+          onExit: (e) {
+            setState(() {
+              displayType = CursorDisplayType.none;
+            });
+          },
+          child: Stack(
+            children: [
+              _buildCursor(),
+              if (isShowRightClickTip && displayType != CursorDisplayType.none)
+                _buildTip(),
+            ],
+          ),
         ),
       ),
     );
@@ -108,10 +134,73 @@ class _MeasureMouseGesturePanelState extends State<MeasureMouseGesturePanel> {
     }
   }
 
+  /// 右键右上角角标
+  FWidget _buildTip() {
+    return FPositioned(
+      top: 0,
+      right: 100,
+      child: FContainer(
+        height: 24,
+        margin: const EdgeInsets.all(5),
+        padding: const EdgeInsets.all(5),
+        decoration: const BoxDecoration(
+          color: Color.fromRGBO(71, 71, 71, 1),
+          borderRadius: BorderRadius.all(Radius.circular(5)),
+        ),
+        child: FRow(children: [
+          const FIcon(
+            Icons.error_outline,
+            color: Colors.white,
+            size: 14,
+          ),
+          const FSizedBox(width: 5),
+          FFittedBox(
+            fit: BoxFit.fitHeight,
+            child: FText(
+              i18nBook.measure.rightClickToCancel.t,
+              style: const TextStyle(
+                  height: 1,
+                  fontSize: 20,
+                  color: Color.fromRGBO(213, 213, 213, 1)),
+            ),
+          ),
+        ]),
+      ),
+    );
+  }
+
   void _onResize(_, e) {
     _findMultiRegions();
   }
 
+  void _onActiveMeasureItemChanged(_, IMeasureItem? e) {
+    if (e == null) return;
+    if (currMeasureItem != null) {
+      currMeasureItem!.onItemStatesChanged.removeListener(_onItemStatesChanged);
+    }
+
+    /// 组合测量项看成一个整体
+    if (e is TopMeasureItem) {
+      setState(() {
+        isShowRightClickTip = true;
+      });
+    }
+    currMeasureItem = e;
+    currMeasureItem!.onItemStatesChanged.addListener(_onItemStatesChanged);
+  }
+
+  void _onItemStatesChanged(_, ItemStates? e) {
+    if (e == ItemStates.running) {
+      setState(() {
+        isShowRightClickTip = true;
+      });
+    } else {
+      setState(() {
+        isShowRightClickTip = false;
+      });
+    }
+  }
+
   void _handleAreaChange(Offset pointerOffset) {
     if (ifContainerTissueTM) {
       if (tissueTMPixelRegion
@@ -152,6 +241,17 @@ class _MeasureMouseGesturePanelState extends State<MeasureMouseGesturePanel> {
       }
     }
   }
+
+  /// 鼠标右键监听
+  void _rightClickListener(PointerDownEvent event) {
+    if (event.kind == PointerDeviceKind.mouse &&
+        event.buttons == kSecondaryMouseButton) {
+      measureHandler.onRightClickFinishMeasure.emit(this, true);
+      setState(() {
+        isShowRightClickTip = false;
+      });
+    }
+  }
 }
 
 enum CursorDisplayType {

+ 5 - 5
lib/view/measure/measure_config/widgets/measure_configuration_style.dart

@@ -175,19 +175,19 @@ class _PatternBodyState extends FState<PatternBody> {
   /// [窗口位置] 枚举值
   static final List<MeasureSelectModel> C_PANEL_POSITION = [
     MeasureSelectModel(
-      name: '左侧顶部',
+      name: i18nBook.measure.topLeft.t,
       value: 0,
     ),
     MeasureSelectModel(
-      name: '左侧底部',
+      name: i18nBook.measure.bottomLeft.t,
       value: 1,
     ),
     MeasureSelectModel(
-      name: '右侧顶部',
+      name: i18nBook.measure.topRight.t,
       value: 2,
     ),
     MeasureSelectModel(
-      name: '右侧底部',
+      name: i18nBook.measure.bottomRight.t,
       value: 3,
     ),
   ];
@@ -298,7 +298,7 @@ class _PatternBodyState extends FState<PatternBody> {
               ),
               //窗口位置
               _PatternItem(
-                title: "窗口位置", //TODO:[Gavin] i18n
+                title: i18nBook.measure.panelPosition.t,
                 item: _PatternItemSelect(
                   itemList: C_PANEL_POSITION,
                   value:

+ 15 - 5
lib/view/measure/measure_main_view.dart

@@ -20,7 +20,7 @@ import 'package:fis_measure/process/workspace/third_part/calibration_controller.
 import 'package:fis_measure/utils/canvas.dart';
 import 'package:fis_measure/utils/prompt_box.dart';
 import 'package:fis_measure/values/colors.dart';
-import 'package:fis_measure/view/button_group/button_group.dart';
+import 'package:fis_measure/view/menu_button_group/menu_button_group.dart';
 import 'package:fis_measure/view/3d_view/carotid_player.dart';
 import 'package:fis_measure/view/gesture/annotation/annotation_gesture.dart';
 import 'package:fis_measure/view/measure/measure_result.dart';
@@ -63,6 +63,7 @@ class _MeasureMainViewState extends State<MeasureMainView> {
   final measureData = Get.find<MeasureDataController>();
 
   late bool canMeasure = application.canMeasure;
+  bool get isToolPanelOpen => measureHandler.toolPanelState;
   late final aiPatintController = Get.find<AiPatintController>();
 
   late bool enableCarotid2DMeasure = false;
@@ -121,6 +122,8 @@ class _MeasureMainViewState extends State<MeasureMainView> {
     });
     vidPlayerController.errorOccured.addListener(_onErrorOccured);
     playerController.frameLoadStateChanged.addListener(_onLoadStateChanged);
+    measureHandler.onToolPanelStateChanged
+        .addListener(_onToolPanelStateChanged);
   }
 
   @override
@@ -129,6 +132,8 @@ class _MeasureMainViewState extends State<MeasureMainView> {
     application.operateTypeChanged.removeListener(onOperateTypeChanged);
     measure3DViewController.updatePlayerMode.removeListener(_onModeChanged);
     playerController.frameLoadStateChanged.removeListener(_onLoadStateChanged);
+    measureHandler.onToolPanelStateChanged
+        .removeListener(_onToolPanelStateChanged);
     uninstallStandardLine();
     super.dispose();
   }
@@ -278,6 +283,10 @@ class _MeasureMainViewState extends State<MeasureMainView> {
     setState(() {});
   }
 
+  void _onToolPanelStateChanged(Object sender, bool e) {
+    setState(() {});
+  }
+
   void onStandardLineCalibrationStateChanged(
       Object sender, StandardLineCalibrationEditState e) {
     setState(() {
@@ -396,7 +405,7 @@ class _MeasureMainViewState extends State<MeasureMainView> {
                                     playerController as VidPlayerController,
                                   ),
                           ),
-                          if (canMeasure) ...[
+                          if (canMeasure && isToolPanelOpen) ...[
                             LayoutId(
                               id: _LayerLayoutIds.recordsCanvas,
                               child: const MeasureRecordsCanvasPanel(),
@@ -423,16 +432,17 @@ class _MeasureMainViewState extends State<MeasureMainView> {
                               id: _LayerLayoutIds.result,
                               child: const MeasureResultPanel(),
                             ),
+                          ],
+                          if (canMeasure)
                             LayoutId(
                               id: _LayerLayoutIds.buttonGroups,
                               child: FOffstage(
                                 offstage: isCaptureState,
-                                child: FButtonGroup(
+                                child: FMenuButtonGroup(
                                   capturePng: () => capturePng(),
                                 ),
                               ),
-                            )
-                          ],
+                            ),
                           if (canShowAI) ...[
                             LayoutId(
                               id: _LayerLayoutIds.paintAI,

+ 36 - 1
lib/view/measure/measure_tool.dart

@@ -3,6 +3,7 @@ import 'package:fis_i18n/i18n.dart';
 import 'package:fis_measure/interfaces/process/items/item.dart';
 import 'package:fis_measure/interfaces/process/items/item_metas.dart';
 import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/interfaces/process/items/types.dart';
 import 'package:fis_measure/interfaces/process/player/play_controller.dart';
 import 'package:fis_measure/interfaces/process/workspace/application.dart';
 import 'package:fis_measure/process/language/measure_language.dart';
@@ -41,6 +42,9 @@ class LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
   /// 当前选中的子测量项下标
   int activeChildItemIndex = 0;
 
+  /// 上一个测量项
+  int lastActiveItemIndex = 0;
+
   /// Styles
   late String fontFamily;
   static const Color buttonBackgroundColor = Color.fromRGBO(70, 70, 70, 1);
@@ -175,13 +179,42 @@ class LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
     if (measureData.curItemMetaList.isNotEmpty) {
       changeItem(measureData.curItemMetaList[0]);
     }
-    setState(() {});
+    setState(() {
+      activeItemIndex = 0;
+      activeChildItemIndex = 0;
+    });
+  }
+
+  /// 鼠标右键结束测量事件【即切换到空测量项】,此时缓存被结束的测量项,如果再次收到右击事件,恢复被结束的测量项
+  void _onRightClickFinishMeasure(_, e) {
+    if (activeItemIndex == -1) {
+      changeItem(measureData.curItemMetaList[lastActiveItemIndex]);
+      setState(() {
+        activeItemIndex = lastActiveItemIndex;
+        activeChildItemIndex = 0;
+      });
+    } else {
+      lastActiveItemIndex = activeItemIndex;
+      final emptyItem = ItemMeta(
+        "EmptyItem",
+        measureType: MeasureTypes.Empty,
+        description: "",
+        outputs: [],
+      );
+      changeItem(emptyItem);
+      setState(() {
+        activeItemIndex = -1;
+        activeChildItemIndex = 0;
+      });
+    }
   }
 
   @override
   void initState() {
     measureData.curItemMetaListChanged.addListener(_onCurItemMetaListChanged);
     measureData.itemMetaListChanged.addListener(_onItemMetaListChanged);
+    measureHandler.onRightClickFinishMeasure
+        .addListener(_onRightClickFinishMeasure);
     WidgetsBinding.instance.addPostFrameCallback((call) {
       _loadFirstItem();
       playerController.firstFrameLoaded.addListener(_onFirstFrameLoaded);
@@ -196,6 +229,8 @@ class LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
     measureData.curItemMetaListChanged
         .removeListener(_onCurItemMetaListChanged);
     measureData.itemMetaListChanged.removeListener(_onItemMetaListChanged);
+    measureHandler.onRightClickFinishMeasure
+        .removeListener(_onRightClickFinishMeasure);
     final item = application.activeMeasureItem;
     if (item != null && item is ITopMeasureItem) {
       item.workingChildChanged.removeListener(_onWorkingChildChanged);

+ 11 - 1
lib/view/measure/measure_view.dart

@@ -79,6 +79,11 @@ class _MeasureMainPageState extends State<MeasureMainPage> {
     setState(() {});
   }
 
+  /// 响应工具面板状态切换事件
+  void _onToolPanelStateChanged(sender, e) {
+    setState(() {});
+  }
+
   /// 在此处动态刷新测量窗口,而不是重置窗口
   void _onUpdateState(sender, Map<String, String> parameters) async {
     _curNeedRouterBack = false;
@@ -154,6 +159,8 @@ class _MeasureMainPageState extends State<MeasureMainPage> {
     measureHandler.onChangeImageLoaded.addListener(_onChangeImage);
     measureHandler.onChangeFullScreenState
         .addListener(_onChangeFullScreenState);
+    measureHandler.onToolPanelStateChanged
+        .addListener(_onToolPanelStateChanged);
     super.initState();
   }
 
@@ -163,6 +170,8 @@ class _MeasureMainPageState extends State<MeasureMainPage> {
     measureHandler.onChangeImageLoaded.removeListener(_onChangeImage);
     measureHandler.onChangeFullScreenState
         .removeListener(_onChangeFullScreenState);
+    measureHandler.onToolPanelStateChanged
+        .removeListener(_onToolPanelStateChanged);
     super.dispose();
   }
 
@@ -185,7 +194,8 @@ class _MeasureMainPageState extends State<MeasureMainPage> {
             children: [
               FOffstage(
                   child: const MeasureToolPanel(),
-                  offstage: measureHandler.fullScreenState),
+                  offstage: measureHandler.fullScreenState ||
+                      !measureHandler.toolPanelState),
               const FVerticalDivider(),
               FExpanded(
                 child: FColumn(

+ 14 - 4
lib/view/button_group/button_group.dart → lib/view/menu_button_group/menu_button_group.dart

@@ -18,18 +18,18 @@ import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 // import 'package:url_launcher/url_launcher.dart';
 
-class FButtonGroup extends FStatefulWidget {
+class FMenuButtonGroup extends FStatefulWidget {
   final VoidCallback capturePng;
-  const FButtonGroup({
+  const FMenuButtonGroup({
     Key? key,
     required this.capturePng,
   }) : super(key: key);
 
   @override
-  FState<FButtonGroup> createState() => _FButtonGroupState();
+  FState<FMenuButtonGroup> createState() => _FMenuButtonGroupState();
 }
 
-class _FButtonGroupState extends FState<FButtonGroup> {
+class _FMenuButtonGroupState extends FState<FMenuButtonGroup> {
   // 单个按钮高度
   static const double buttonHeight = 42;
   // 按钮外边距
@@ -130,6 +130,16 @@ class _FButtonGroupState extends FState<FButtonGroup> {
           setState(() {});
         },
       ),
+      _buildTitleButton(
+        measureHandler.toolPanelState
+            ? Icons.cancel_presentation
+            : Icons.present_to_all,
+        measureHandler.toolPanelState ? "关闭测量" : "打开测量",
+        () {
+          measureHandler.toolPanelState = !measureHandler.toolPanelState;
+          setState(() {});
+        },
+      ),
       _buildTitleButton(
         FIcons.fis_save,
         i18nBook.common.save.t,

+ 1 - 1
pubspec.yaml

@@ -96,7 +96,7 @@ dependency_overrides:
   fis_i18n:
     git:
       url: http://git.ius.plus:88/Project-Wing/fis_lib_i18n.git
-      ref: dfc63b7
+      ref: 9a7123b
   fis_jsonrpc:
     git:
       url: http://git.ius.plus:88/Project-Wing/fis_lib_jsonrpc.git