Browse Source

新增起始点微调功能

gavin.chen 2 years ago
parent
commit
78d52d3f30

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

@@ -58,6 +58,9 @@ abstract class IMeasureItem {
   /// 分配新序号
   int assignId();
 
+  /// 触发一次刷新
+  void update();
+
   /// 快照变化事件
   late final FEventHandler<IMeasureItemFeature?> featureUpdated;
 

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

@@ -158,6 +158,12 @@ abstract class MeasureItem<T extends MeasureItemFeature> extends IMeasureItem {
     doFeatureUpdate();
   }
 
+  @override
+  void update() {
+    if (feature == null) return;
+    doFeatureUpdate();
+  }
+
   @protected
   void onCancelingOnce() {}
 

+ 4 - 0
lib/process/workspace/application.dart

@@ -270,6 +270,7 @@ class Application implements IApplication {
   }
 
   final FEventHandler<Offset> mobileTouchEvent = FEventHandler();
+  final FEventHandler<Offset> mobileTouchEndEvent = FEventHandler();
 
   @override
   PointInfo createPointInfo(Offset offset, PointInfoType type) {
@@ -302,6 +303,9 @@ class Application implements IApplication {
       if (type == PointInfoType.touchMove) {
         mobileTouchEvent.emit(this, offset); // 传出移动事件
       }
+      if (type == PointInfoType.touchUp) {
+        mobileTouchEndEvent.emit(this, offset); // 传出触摸结束事件
+      }
     }
     return info;
   }

+ 9 - 0
lib/view/mobile_view/mobile_measure_main_view.dart

@@ -24,6 +24,7 @@ import 'package:fis_measure/view/mobile_view/controller/mobile_measure_view_stat
 import 'package:fis_measure/view/mobile_view/mobile_bottom_menu.dart';
 import 'package:fis_measure/view/mobile_view/mobile_right_panel.dart';
 import 'package:fis_measure/view/mobile_view/mobile_top_menu.dart';
+import 'package:fis_measure/view/mobile_view/widgets/fine_tune_panel.dart';
 import 'package:fis_measure/view/mobile_view/widgets/magnifier.dart';
 import 'package:fis_measure/view/outline/measure_area_indicator.dart';
 import 'package:fis_measure/view/paint/ai_patint.dart';
@@ -419,6 +420,10 @@ class _MobileMeasureMainViewState extends State<MobileMeasureMainView> {
                     //   id: _LayerLayoutIds.canvasMagnifier,
                     //   child: const CanvasMagnifier(),
                     // ),
+                    LayoutId(
+                      id: _LayerLayoutIds.fineTunePanel,
+                      child: const FineTunePanel(),
+                    ),
                   ],
                   if (isPlayerMode && canShowAI) ...[
                     LayoutId(
@@ -485,6 +490,7 @@ class _LayerLayoutDelegate extends MultiChildLayoutDelegate {
     layoutLayer(_LayerLayoutIds.standardLineCalibration, offset, renderSize);
     layoutLayer(_LayerLayoutIds.currActiveAreaIndicator, offset, renderSize);
     // layoutLayer(_LayerLayoutIds.canvasMagnifier, offset, renderSize);
+    layoutLayer(_LayerLayoutIds.fineTunePanel, offset, renderSize);
     layoutLayer(_LayerLayoutIds.gesture, offset, renderSize);
     layoutLayer(
       _LayerLayoutIds.result,
@@ -544,6 +550,9 @@ enum _LayerLayoutIds {
   /// 画布放大镜层
   // canvasMagnifier,
 
+  /// 微调按钮面板
+  fineTunePanel,
+
   /// 测量记录画板
   recordsCanvas,
 

+ 270 - 0
lib/view/mobile_view/widgets/fine_tune_panel.dart

@@ -0,0 +1,270 @@
+import 'dart:async';
+import 'package:fis_measure/interfaces/process/items/item.dart';
+import 'package:fis_measure/interfaces/process/workspace/application.dart';
+import 'package:fis_measure/process/items/top_item.dart';
+import 'package:fis_measure/process/workspace/application.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+
+class FineTunePanel extends StatefulWidget {
+  final Size panelSize;
+  const FineTunePanel({
+    Key? key,
+    this.panelSize = const Size(150, 150),
+  }) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() => _FineTunePanelState();
+}
+
+class _FineTunePanelState extends State<FineTunePanel> {
+  Application application = Get.find<IApplication>() as Application;
+  final GlobalKey _magnifierKey = GlobalKey();
+  double arrowButtonSize = 50;
+
+  Offset containerPos = const Offset(0, 0);
+  Size containerSize = const Size(0, 0);
+  bool ifShowPanel = false;
+  bool ifSetTop = false; //是否置于顶部
+
+  static const double fineTuneStep = 0.001;
+
+  bool isCombined = false;
+
+  Timer? _timer;
+
+  IMeasureItem? get _currWorkingItem => isCombined
+      ? (application.activeMeasureItem as TopMeasureItem).workingChild
+      : application.activeMeasureItem;
+
+  @override
+  void initState() {
+    arrowButtonSize = widget.panelSize.width / 2.5;
+    application.mobileTouchEndEvent.addListener(_onMobileTouchEndEvent);
+    application.mobileTouchEvent.addListener(_onMobileTouchEvent);
+    WidgetsBinding.instance.addPostFrameCallback((_) => {
+          _initContainerParam(),
+        });
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    application.mobileTouchEndEvent.removeListener(_onMobileTouchEndEvent);
+    application.mobileTouchEvent.removeListener(_onMobileTouchEvent);
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      children: [
+        Column(
+          mainAxisAlignment:
+              ifSetTop ? MainAxisAlignment.start : MainAxisAlignment.end,
+          children: [
+            if (ifShowPanel)
+              Opacity(
+                opacity: 0.7,
+                child: Container(
+                  margin: EdgeInsets.only(
+                    left: 20,
+                    right: 20,
+                    top: ifSetTop ? 20 : 0,
+                    bottom: ifSetTop ? 0 : 20,
+                  ),
+                  decoration: BoxDecoration(
+                      border: Border.all(color: Colors.white, width: 2),
+                      borderRadius: BorderRadius.circular(10),
+                      color: Colors.black.withOpacity(0.8)),
+                  child: Column(
+                    children: [
+                      Container(
+                        decoration: BoxDecoration(
+                            color: const Color.fromARGB(255, 53, 53, 53)
+                                .withOpacity(0.8),
+                            borderRadius: const BorderRadius.only(
+                              topLeft: Radius.circular(8),
+                              topRight: Radius.circular(8),
+                            )),
+                        width: widget.panelSize.width,
+                        child: const Padding(
+                          padding: EdgeInsets.all(3.0),
+                          child: Text(
+                            /// TODO:[Gavin] i18n
+                            "微调起始点",
+                            style: TextStyle(color: Colors.white, fontSize: 14),
+                            textAlign: TextAlign.center,
+                          ),
+                        ),
+                      ),
+                      Container(
+                        height: 2,
+                        width: widget.panelSize.width,
+                        color: Colors.white,
+                      ),
+                      SizedBox(
+                        key: _magnifierKey,
+                        width: widget.panelSize.width,
+                        height: widget.panelSize.height,
+                        child: Stack(
+                          children: [
+                            _buildFineTuneButton(
+                                Alignment.centerLeft,
+                                Icons.arrow_circle_left_outlined,
+                                const Offset(-1 * fineTuneStep, 0)),
+                            _buildFineTuneButton(
+                                Alignment.topCenter,
+                                Icons.arrow_circle_up_outlined,
+                                const Offset(0, -1 * fineTuneStep)),
+                            _buildFineTuneButton(
+                                Alignment.centerRight,
+                                Icons.arrow_circle_right_outlined,
+                                const Offset(fineTuneStep, 0)),
+                            _buildFineTuneButton(
+                                Alignment.bottomCenter,
+                                Icons.arrow_circle_down_outlined,
+                                const Offset(0, fineTuneStep)),
+                          ],
+                        ),
+                      ),
+                    ],
+                  ),
+                ),
+              ),
+          ],
+        ),
+      ],
+    );
+  }
+
+  Align _buildFineTuneButton(
+      Alignment alignment, IconData icon, Offset offset) {
+    return Align(
+      alignment: alignment,
+      child: GestureDetector(
+        onTapDown: (e) {
+          _onTapFineTuneButton(offset);
+        },
+        onLongPress: () {
+          _startLongPressTimer(offset);
+        },
+        onLongPressUp: () {
+          _stopLongPressTimer();
+        },
+        child: Icon(
+          icon,
+          color: Colors.white,
+          size: arrowButtonSize,
+        ),
+      ),
+    );
+  }
+
+  void _initContainerParam() {
+    final containerLayer = context.findRenderObject() as RenderBox;
+    containerPos = containerLayer.localToGlobal(Offset.zero);
+    containerSize = containerLayer.size;
+  }
+
+  void _updateAlignment(Offset touchPos) {
+    if (touchPos.dx < widget.panelSize.width) {
+      if (ifSetTop && (touchPos.dy < containerSize.height / 2)) {
+        setState(() {
+          ifSetTop = false;
+        });
+      } else if (!ifSetTop && (touchPos.dy > containerSize.height / 2)) {
+        setState(() {
+          ifSetTop = true;
+        });
+      }
+    } else if (ifSetTop) {
+      setState(() {
+        ifSetTop = false;
+      });
+    }
+  }
+
+  void _onMobileTouchEndEvent(_, Offset offset) {
+    isCombined = false;
+    if (_currWorkingItem?.feature != null) {
+      int pointsNum = 0;
+      pointsNum =
+          _currWorkingItem?.feature!.innerPoints.toSet().toList().length ?? 0;
+      // print("pointsNum: ${_currWorkingItem?.feature!.innerPoints.length}");
+      if (pointsNum == 1) {
+        setState(() {
+          ifShowPanel = true;
+        });
+        Offset startPoint = application
+            .activeMeasureItem!.feature!.innerPoints[0]
+            .toOffset()
+            .scale(containerSize.width, containerSize.height);
+        _updateAlignment(startPoint);
+        return;
+      }
+      if (application.activeMeasureItem is TopMeasureItem) {
+        isCombined = true;
+        if (_currWorkingItem?.feature == null) return;
+        pointsNum =
+            _currWorkingItem!.feature!.innerPoints.toSet().toList().length;
+        // print(
+        //     "组合测量项 pointsNum: ${_currWorkingItem?.feature!.innerPoints.toSet().toList().length}");
+        if (pointsNum == 1) {
+          setState(() {
+            ifShowPanel = true;
+          });
+          Offset startPoint = _currWorkingItem!.feature!.innerPoints[0]
+              .toOffset()
+              .scale(containerSize.width, containerSize.height);
+          _updateAlignment(startPoint);
+          return;
+        }
+      }
+    }
+    setState(() {
+      ifShowPanel = false;
+    });
+  }
+
+  void _onMobileTouchEvent(_, Offset offset) {
+    if (!ifShowPanel) return;
+    setState(() {
+      ifShowPanel = false;
+    });
+  }
+
+  void _onTapFineTuneButton(Offset offset) {
+    if (_currWorkingItem?.feature != null) {
+      HapticFeedback.lightImpact();
+      _currWorkingItem?.update();
+      int pointsNum = 0;
+      pointsNum = _currWorkingItem?.feature!.innerPoints.length ?? 0;
+      if (pointsNum > 0) {
+        for (int i = 0; i < pointsNum; i++) {
+          _currWorkingItem?.feature!.innerPoints[i]
+              .addOffset(offset.dx, offset.dy);
+        }
+      }
+    }
+  }
+
+  /// 开启长按定时器
+  void _startLongPressTimer(Offset offset) {
+    /// 随着触发次数的增多,触发的距离越来越长
+    int fineTuneTimes = 0;
+    _timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
+      fineTuneTimes++;
+      final scale = (1 + fineTuneTimes * 0.1).truncate().toDouble();
+      _onTapFineTuneButton(offset.scale(scale, scale));
+    });
+  }
+
+  void _stopLongPressTimer() {
+    if (_timer != null) {
+      _timer!.cancel();
+      _timer = null;
+    }
+  }
+}