瀏覽代碼

移动端支持切换M模式测量(深度)

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

+ 40 - 4
lib/process/primitives/location.dart

@@ -46,15 +46,51 @@ class Location extends MeasureItem<LocationFeature> {
     return true;
   }
 
+  String mapPointInfoType(PointInfoType type) {
+    switch (type) {
+      case PointInfoType.mouseUp:
+        return '⬆️';
+      case PointInfoType.mouseDown:
+        return '⬇️';
+      case PointInfoType.mouseMove:
+        return '⭐';
+      case PointInfoType.touchUp:
+        return '⬆️';
+      case PointInfoType.touchDown:
+        return '⬇️';
+      case PointInfoType.touchMove:
+        return '⭐';
+    }
+  }
+
   @override
   bool onExecuteTouch(PointInfo args) {
-    // TODO: implement onExecuteTouch
-    throw UnimplementedError();
+    // print(
+    //     'onExecuteTouch: ${mapPointInfoType(args.pointType)} ( ${(args.x * 100).ceil()} ${(args.y * 100).ceil()} )');
+    if (state == ItemStates.finished || state == ItemStates.waiting) {
+      if (args.pointType == PointInfoType.touchDown) {
+        handleMouseMoveWhileFinish(args);
+      }
+    }
+    if (state == ItemStates.running) {
+      if (args.pointType == PointInfoType.touchDown) {
+        feature?.point = args;
+        doCalculate();
+      }
+      if (args.pointType == PointInfoType.touchMove) {
+        feature?.point = args;
+        doCalculate();
+      }
+      if (args.pointType == PointInfoType.touchUp) {
+        doFeatureFinish();
+      }
+    }
+    return true;
   }
 
   void handleMouseMoveWhileFinish(PointInfo args) {
-    // TODO: 判断是否当前area
-    // 转换为Area逻辑位置
+    // TODO: 判断是否当前 area
+    // 转换为 Area 逻辑位置
     final point = args.toAreaLogicPoint();
     handleTissue(args, point);
     handleTissueTM(args.hostVisualArea!.mode.modeType, point);

+ 73 - 4
lib/view/gesture/cross_position_indicator.dart

@@ -1,5 +1,6 @@
 import 'package:fis_measure/values/colors.dart';
 import 'package:fis_measure/view/gesture/positioned_cursor.dart';
+import 'package:fis_measure/view/gesture/positioned_touch_cursor.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 import 'package:path_drawing/path_drawing.dart';
@@ -9,10 +10,10 @@ class CrossIndicator extends StatefulWidget {
   final Rect areaRegion;
 
   @override
-  State<StatefulWidget> createState() => _CrossIndicatorState();
+  State<StatefulWidget> createState() => CrossIndicatorState();
 }
 
-class _CrossIndicatorState extends State<CrossIndicator> {
+class CrossIndicatorState extends State<CrossIndicator> {
   final mouseState = Get.find<IMouseState>();
   CrossIndicatorStyle indicatorStyle = CrossIndicatorStyle.nomal;
 
@@ -66,10 +67,78 @@ class _CrossIndicatorState extends State<CrossIndicator> {
   }
 }
 
+class MobileCrossIndicator extends StatefulWidget {
+  const MobileCrossIndicator({Key? key, required this.areaRegion})
+      : super(key: key);
+  final Rect areaRegion;
+
+  @override
+  State<StatefulWidget> createState() => MobileCrossIndicatorState();
+}
+
+class MobileCrossIndicatorState extends State<MobileCrossIndicator> {
+  final touchState = Get.find<ITouchPointState>();
+  CrossIndicatorStyle indicatorStyle = CrossIndicatorStyle.nomal;
+
+  @override
+  void initState() {
+    touchState.mousePositionChanged.addListener(mousePositionChanged);
+    touchState.crossIndicatorStyleChanged
+        .addListener(_crossIndicatorStyleChanged);
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    touchState.mousePositionChanged.removeListener(mousePositionChanged);
+    touchState.crossIndicatorStyleChanged
+        .removeListener(_crossIndicatorStyleChanged);
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final position = touchState.mousePosition;
+    if (position == null) return Container();
+    return Positioned(
+      top: widget.areaRegion.top,
+      width: widget.areaRegion.width,
+      height: widget.areaRegion.height,
+      child: RepaintBoundary(
+        child: CustomPaint(
+            painter: _CrossIndicatorPainter(
+                strokeWidth: 1,
+                color: MeasureColors.ActiveCaliper,
+                centerOffset:
+                    Offset(position.dx, position.dy - widget.areaRegion.top),
+                style: indicatorStyle)),
+      ),
+    );
+  }
+
+  void mousePositionChanged(Object sender, dynamic e) {
+    onUpdate();
+  }
+
+  void _crossIndicatorStyleChanged(Object sender, dynamic e) {
+    setState(() {
+      indicatorStyle = e;
+    });
+  }
+
+  void onUpdate() {
+    setState(() {});
+  }
+}
+
 class _CrossIndicatorPainter extends CustomPainter {
   const _CrossIndicatorPainter(
-      {this.color, this.centerOffset, this.style = CrossIndicatorStyle.nomal});
+      {this.color,
+      this.strokeWidth,
+      this.centerOffset,
+      this.style = CrossIndicatorStyle.nomal});
   final Color? color;
+  final double? strokeWidth;
   final Offset? centerOffset;
   final CrossIndicatorStyle style;
 
@@ -77,7 +146,7 @@ class _CrossIndicatorPainter extends CustomPainter {
   void paint(Canvas canvas, Size size) {
     final paint = Paint()
       ..color = color ?? const Color.fromARGB(255, 255, 255, 0)
-      ..strokeWidth = 2
+      ..strokeWidth = strokeWidth ?? 2
       ..style = PaintingStyle.stroke
       ..isAntiAlias = false;
     final center = centerOffset ?? Offset(size.width / 2, size.height / 2);

+ 167 - 0
lib/view/gesture/positioned_touch_cursor.dart

@@ -0,0 +1,167 @@
+import 'package:fis_common/event/event_type.dart';
+import 'package:fis_measure/values/colors.dart';
+import 'package:fis_measure/view/cursor.dart';
+import 'package:fis_measure/view/gesture/cross_position_indicator.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+abstract class ITouchPointState {
+  Offset? get mousePosition;
+  set mousePosition(Offset? value);
+
+  Offset? get cursorPosition;
+
+  MeasureCursorType get cursorType;
+  set cursorType(MeasureCursorType value);
+
+  double get cursorSize;
+  set cursorSize(double value);
+
+  /// 卡尺缩放比
+  double get cursorScaleRatio;
+  set cursorScaleRatio(double value);
+
+  late FEventHandler<Offset?> mousePositionChanged;
+  late FEventHandler<MeasureCursorType> cursorTypeChanged;
+  late FEventHandler<double> cursorSizeChanged;
+  late FEventHandler<double> cursorScaleRatioChanged;
+  late FEventHandler<CrossIndicatorStyle> crossIndicatorStyleChanged;
+}
+
+class TouchPointState implements ITouchPointState {
+  Offset? _mousePosition;
+  double _cursorSize = 16;
+  double _cursorScaleRatio = 1;
+  MeasureCursorType _cursorType = MeasureCursorType.cursor01;
+
+  @override
+  var mousePositionChanged = FEventHandler<Offset?>();
+
+  @override
+  var cursorSizeChanged = FEventHandler<double>();
+  @override
+  var cursorScaleRatioChanged = FEventHandler<double>();
+
+  @override
+  var cursorTypeChanged = FEventHandler<MeasureCursorType>();
+
+  @override
+  var crossIndicatorStyleChanged = FEventHandler<CrossIndicatorStyle>();
+
+  @override
+  Offset? get mousePosition => _mousePosition;
+  @override
+  set mousePosition(Offset? value) {
+    if (value != _mousePosition) {
+      _mousePosition = value;
+      mousePositionChanged.emit(this, value);
+    }
+  }
+
+  @override
+  double get cursorSize => _cursorSize;
+  @override
+  set cursorSize(double value) {
+    if (value != _cursorSize) {
+      _cursorSize = value;
+      cursorSizeChanged.emit(this, value);
+    }
+  }
+
+  @override
+  double get cursorScaleRatio => _cursorScaleRatio;
+  @override
+  set cursorScaleRatio(double value) {
+    if (value != _cursorScaleRatio) {
+      _cursorScaleRatio = value;
+      cursorScaleRatioChanged.emit(this, value);
+    }
+  }
+
+  @override
+  MeasureCursorType get cursorType => _cursorType;
+  @override
+  set cursorType(MeasureCursorType value) {
+    if (value != _cursorType) {
+      _cursorType = value;
+      cursorTypeChanged.emit(this, value);
+    }
+  }
+
+  @override
+  Offset? get cursorPosition {
+    if (mousePosition == null) return null;
+
+    double dx = mousePosition!.dx, dy = mousePosition!.dy;
+    final offset = cursorScaleRatio * cursorSize / 2;
+    dx -= offset;
+    dy -= offset;
+    return Offset(dx, dy);
+  }
+}
+
+class PositionedTouchCursor extends StatefulWidget {
+  const PositionedTouchCursor({Key? key}) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() => _PositionedTouchCursorState();
+}
+
+class _PositionedTouchCursorState extends State<PositionedTouchCursor> {
+  final mouseState = Get.find<ITouchPointState>();
+
+  @override
+  void initState() {
+    mouseState.cursorSizeChanged.addListener(onCursorSizeChanged);
+    mouseState.cursorScaleRatioChanged.addListener(onCursorScaleRatioChanged);
+    mouseState.cursorTypeChanged.addListener(cursorTypeChanged);
+    mouseState.mousePositionChanged.addListener(mousePositionChanged);
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    mouseState.cursorSizeChanged.removeListener(onCursorSizeChanged);
+    mouseState.cursorScaleRatioChanged
+        .removeListener(onCursorScaleRatioChanged);
+    mouseState.cursorTypeChanged.removeListener(cursorTypeChanged);
+    mouseState.mousePositionChanged.removeListener(mousePositionChanged);
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final position = mouseState.cursorPosition;
+    if (position == null) return Container();
+
+    return Positioned(
+      left: position.dx,
+      top: position.dy,
+      child: MeasureCursor(
+        type: mouseState.cursorType,
+        size: mouseState.cursorSize * mouseState.cursorScaleRatio,
+        color: MeasureColors.Primary,
+      ),
+    );
+  }
+
+  void onCursorSizeChanged(Object sender, dynamic e) {
+    onUpdate();
+  }
+
+  void onCursorScaleRatioChanged(Object sender, dynamic e) {
+    onUpdate();
+  }
+
+  void cursorTypeChanged(Object sender, dynamic e) {
+    onUpdate();
+  }
+
+  void mousePositionChanged(Object sender, dynamic e) {
+    onUpdate();
+  }
+
+  void onUpdate() {
+    setState(() {});
+  }
+}

+ 125 - 26
lib/view/gesture/touch_gesture.dart

@@ -1,7 +1,14 @@
+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/process/workspace/application.dart';
 import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/view/gesture/cross_position_indicator.dart';
+import 'package:fis_measure/view/gesture/mouse_gesture.dart';
+import 'package:fis_measure/view/gesture/positioned_touch_cursor.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
+import 'package:vid/us/vid_us_visual_area_type.dart';
 
 class MeasureTouchGesturePanel extends StatefulWidget {
   const MeasureTouchGesturePanel({Key? key}) : super(key: key);
@@ -13,8 +20,16 @@ class MeasureTouchGesturePanel extends StatefulWidget {
 class _MeasureTouchGesturePanelState extends State<MeasureTouchGesturePanel> {
   late final application = Get.find<IApplication>();
 
+  final touchState = Get.find<ITouchPointState>();
+
+  CursorDisplayType displayType = CursorDisplayType.none;
+  RectRegion tissueTMPixelRegion = RectRegion();
+  bool ifContainerTissueTM = false;
+  Offset _lastPosition = Offset.zero;
+
   @override
   void initState() {
+    _findMultiRegions();
     super.initState();
   }
 
@@ -25,31 +40,115 @@ class _MeasureTouchGesturePanelState extends State<MeasureTouchGesturePanel> {
 
   @override
   Widget build(BuildContext context) {
-    return GestureDetector(onTapDown: (details) {
-      application.createPointInfo(
-        details.localPosition,
-        PointInfoType.touchDown,
-      );
-    }, onPanDown: (details) {
-      application.createPointInfo(
-        details.localPosition,
-        PointInfoType.touchDown,
-      );
-    }, onPanUpdate: (details) {
-      application.createPointInfo(
-        details.localPosition,
-        PointInfoType.touchMove,
-      );
-    }, onTapUp: (details) {
-      application.createPointInfo(
-        details.localPosition,
-        PointInfoType.touchUp,
-      );
-    }, onPanEnd: (details) {
-      application.createPointInfo(
-        Offset.zero,
-        PointInfoType.touchUp,
-      );
-    });
+    return GestureDetector(
+      behavior: HitTestBehavior.opaque,
+      onTapDown: (details) {
+        touchState.mousePosition = details.localPosition;
+        _handleAreaChange(details.localPosition);
+        application.createPointInfo(
+          details.localPosition,
+          PointInfoType.touchDown,
+        );
+      },
+      onPanDown: (details) {
+        touchState.mousePosition = details.localPosition;
+        _handleAreaChange(details.localPosition);
+        application.createPointInfo(
+          details.localPosition,
+          PointInfoType.touchDown,
+        );
+      },
+      onPanUpdate: (details) {
+        touchState.mousePosition = details.localPosition;
+        _handleAreaChange(details.localPosition);
+        _lastPosition = details.localPosition;
+        application.createPointInfo(
+          details.localPosition,
+          PointInfoType.touchMove,
+        );
+      },
+      onTapUp: (details) {
+        touchState.mousePosition = details.localPosition;
+        setState(() {
+          displayType = CursorDisplayType.normal;
+        });
+        application.createPointInfo(
+          details.localPosition,
+          PointInfoType.touchUp,
+        );
+      },
+      onPanEnd: (details) {
+        application.createPointInfo(
+          _lastPosition,
+          PointInfoType.touchUp,
+        );
+        setState(() {
+          displayType = CursorDisplayType.normal;
+        });
+      },
+      child: _buildCursor(),
+    );
+  }
+
+  Widget _buildCursor() {
+    switch (displayType) {
+      case CursorDisplayType.none:
+        return Container();
+      case CursorDisplayType.normal:
+        return Container();
+      case CursorDisplayType.cross:
+        return Stack(
+          children: [
+            MobileCrossIndicator(
+              areaRegion: Rect.fromLTRB(
+                  tissueTMPixelRegion.left,
+                  tissueTMPixelRegion.top,
+                  tissueTMPixelRegion.right,
+                  tissueTMPixelRegion.bottom),
+            ),
+          ],
+        );
+    }
+  }
+
+  void _handleAreaChange(Offset pointerOffset) {
+    if (ifContainerTissueTM) {
+      if (tissueTMPixelRegion
+          .containsPoint(DPoint.parseOffset(pointerOffset))) {
+        if (displayType != CursorDisplayType.cross) {
+          setState(() {
+            displayType = CursorDisplayType.cross;
+          });
+        }
+      } else {
+        if (displayType != CursorDisplayType.normal) {
+          setState(() {
+            displayType = CursorDisplayType.normal;
+          });
+        }
+      }
+    }
+  }
+
+  void _findMultiRegions() {
+    final displaySize = application.displaySize;
+    for (var visual in application.visuals) {
+      for (var area in visual.visualAreas) {
+        if (area.visualAreaType == VidUsVisualAreaType.TissueTimeMotion ||
+            area.visualAreaType == VidUsVisualAreaType.Doppler) {
+          ifContainerTissueTM = true;
+
+          ///TODO 由于布局问题 Left 定位给不准,这里放宽到边界
+          tissueTMPixelRegion = RectRegion.rect(DRect(
+            // area.displayRegion.left * displaySize.width,
+            0,
+            area.displayRegion.top * displaySize.height,
+            // area.displayRegion.width * displaySize.width,
+            displaySize.width,
+            area.displayRegion.height * displaySize.height,
+          ));
+        }
+      }
+    }
   }
 }

+ 1 - 1
lib/view/mobile_view/mobile_bottom_menu.dart

@@ -105,7 +105,7 @@ class _MobileBottomMenuState extends State<MobileBottomMenu> {
             children: [
               if (ifShowProgressBar) ...[
                 SinglePrevButton(),
-                SinglePlayButton(),
+                const SinglePlayButton(),
                 SingleNextButton(),
                 Expanded(
                   child: SizedBox(

+ 7 - 0
lib/view/mobile_view/mobile_measure_view.dart

@@ -6,6 +6,7 @@ import 'package:fis_measure/process/layout/configuration.dart';
 import 'package:fis_measure/process/workspace/measure_controller.dart';
 import 'package:fis_measure/process/workspace/measure_data_controller.dart';
 import 'package:fis_measure/process/workspace/measure_handler.dart';
+import 'package:fis_measure/view/gesture/positioned_touch_cursor.dart';
 import 'package:fis_measure/view/mobile_view/mobile_measure_main_view.dart';
 import 'package:fis_ui/index.dart';
 import 'package:flutter/material.dart';
@@ -86,8 +87,14 @@ class _MobileMeasureMainPageState extends State<MobileMeasureMainPage> {
     setState(() {});
   }
 
+  /// 初始化卡尺样式部分
+  Future<void> _initTouchModuel() async {
+    Get.put<ITouchPointState>(TouchPointState());
+  }
+
   @override
   void initState() {
+    _initTouchModuel();
     SystemChrome.setPreferredOrientations([
       DeviceOrientation.landscapeLeft, //全屏时旋转方向,左边
     ]);

+ 22 - 1
lib/view/mobile_view/mobile_right_panel/mobile_measure_tool.dart

@@ -198,6 +198,11 @@ class _MobileMeasureSelector extends FState<MobileMeasureSelector> {
         .addListener(_onActiveMeasureItemChanged);
     application.visualAreaChanged.addListener(_visualAreaChanged);
     _currentAreaType = application.currentVisualArea.visualAreaType;
+    _initMeasureItemsListAsync();
+  }
+
+  void _initMeasureItemsListAsync() async {
+    _availableModes = await _initMeasureItemsList();
   }
 
   @override
@@ -560,7 +565,13 @@ class _MobileMeasureSelector extends FState<MobileMeasureSelector> {
 
   /// 过滤出当前支持的所有测量项
   List<ItemMetaDTO> _currSupportedItemFilter(MeasureModeDTO mode) {
-    final supportedMeasureTypeName = ['Distance', 'AreaPerimeterTrace'];
+    final supportedMeasureTypeName = [
+      'Distance', // StraightLine
+      'AreaPerimeterTrace', // Trace
+      "Depth", // Location
+      "Velocity", // Location
+      "MDepth" // Location
+    ];
     List<ItemMetaDTO> _supportedItems = [];
     if (mode.availableGroups == null || mode.availableGroups!.isEmpty) {
       return _supportedItems;
@@ -574,8 +585,10 @@ class _MobileMeasureSelector extends FState<MobileMeasureSelector> {
           continue;
         }
         for (ItemMetaDTO items in folder.availableItems!) {
+          bool isSupported = false;
           if (supportedMeasureTypeName.contains(items.measureTypeName)) {
             _supportedItems.add(items);
+            isSupported = true;
           }
           if (items.multiMethodItems != null &&
               items.multiMethodItems!.isNotEmpty) {
@@ -583,6 +596,7 @@ class _MobileMeasureSelector extends FState<MobileMeasureSelector> {
               if (supportedMeasureTypeName.contains(item.measureTypeName)) {
                 if (item.isWorking) {
                   _supportedItems.add(items);
+                  isSupported = true;
                 }
               }
             }
@@ -599,8 +613,15 @@ class _MobileMeasureSelector extends FState<MobileMeasureSelector> {
             }
             if (isAdd) {
               _supportedItems.add(items);
+              isSupported = true;
             }
           }
+
+          if (isSupported) {
+            // print("✅ 支持的测量项:${items.name} | ${items.measureTypeName}}");
+          } else {
+            // print("❌ 不支持的测量项:${items.name} | ${items.measureTypeName}}");
+          }
         }
       }
     }

+ 1 - 0
lib/view/mobile_view/mobile_right_panel/mobile_more_measure_item_dialog.dart

@@ -176,6 +176,7 @@ class _MobileMoreMeasureItemDialogState
     );
   }
 
+  /// 构建每个分类
   FWidget _buildEachCategory(MeasureItemCategoryModel category) {
     final List<MeasureItemModel> measureItemList = category.items;
     final String categoryName = category.name;