|
@@ -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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|