fine_tune_panel.dart 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import 'dart:async';
  2. import 'package:fis_i18n/i18n.dart';
  3. import 'package:fis_measure/interfaces/process/items/item.dart';
  4. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  5. import 'package:fis_measure/process/items/top_item.dart';
  6. import 'package:fis_measure/process/workspace/application.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:flutter/services.dart';
  9. import 'package:get/get.dart';
  10. class FineTunePanel extends StatefulWidget {
  11. final Size panelSize;
  12. const FineTunePanel({
  13. Key? key,
  14. this.panelSize = const Size(150, 150),
  15. }) : super(key: key);
  16. @override
  17. State<StatefulWidget> createState() => _FineTunePanelState();
  18. }
  19. class _FineTunePanelState extends State<FineTunePanel> {
  20. Application application = Get.find<IApplication>() as Application;
  21. final GlobalKey _magnifierKey = GlobalKey();
  22. double arrowButtonSize = 50;
  23. Offset containerPos = const Offset(0, 0);
  24. Size containerSize = const Size(0, 0);
  25. bool ifShowPanel = false;
  26. bool ifSetTop = false; //是否置于顶部
  27. static const double fineTuneStep = 0.001;
  28. bool isCombined = false;
  29. Timer? _timer;
  30. IMeasureItem? get _currWorkingItem => isCombined
  31. ? (application.activeMeasureItem as TopMeasureItem).workingChild
  32. : application.activeMeasureItem;
  33. @override
  34. void initState() {
  35. arrowButtonSize = widget.panelSize.width / 2.5;
  36. application.mobileTouchEndEvent.addListener(_onMobileTouchEndEvent);
  37. application.mobileTouchEvent.addListener(_onMobileTouchEvent);
  38. application.activeMeasureItemChanged
  39. .addListener(_onActiveMeasureItemChanged);
  40. WidgetsBinding.instance.addPostFrameCallback((_) => {
  41. _initContainerParam(),
  42. });
  43. super.initState();
  44. }
  45. @override
  46. void dispose() {
  47. application.mobileTouchEndEvent.removeListener(_onMobileTouchEndEvent);
  48. application.mobileTouchEvent.removeListener(_onMobileTouchEvent);
  49. application.activeMeasureItemChanged
  50. .removeListener(_onActiveMeasureItemChanged);
  51. super.dispose();
  52. }
  53. @override
  54. Widget build(BuildContext context) {
  55. return Row(
  56. children: [
  57. Column(
  58. mainAxisAlignment:
  59. ifSetTop ? MainAxisAlignment.start : MainAxisAlignment.end,
  60. children: [
  61. if (ifShowPanel)
  62. Opacity(
  63. opacity: 0.7,
  64. child: Container(
  65. margin: EdgeInsets.only(
  66. left: 20,
  67. right: 20,
  68. top: ifSetTop ? 20 : 0,
  69. bottom: ifSetTop ? 0 : 20,
  70. ),
  71. decoration: BoxDecoration(
  72. border: Border.all(color: Colors.white, width: 2),
  73. borderRadius: BorderRadius.circular(10),
  74. color: Colors.black.withOpacity(0.8)),
  75. child: Column(
  76. children: [
  77. Container(
  78. decoration: BoxDecoration(
  79. color: const Color.fromARGB(255, 53, 53, 53)
  80. .withOpacity(0.8),
  81. borderRadius: const BorderRadius.only(
  82. topLeft: Radius.circular(8),
  83. topRight: Radius.circular(8),
  84. )),
  85. width: widget.panelSize.width,
  86. child: Padding(
  87. padding: const EdgeInsets.all(3.0),
  88. child: Text(
  89. i18nBook.measure.fineTuneStartPoint.t,
  90. style: const TextStyle(
  91. color: Colors.white, fontSize: 14),
  92. textAlign: TextAlign.center,
  93. ),
  94. ),
  95. ),
  96. Container(
  97. height: 2,
  98. width: widget.panelSize.width,
  99. color: Colors.white,
  100. ),
  101. SizedBox(
  102. key: _magnifierKey,
  103. width: widget.panelSize.width,
  104. height: widget.panelSize.height,
  105. child: Stack(
  106. children: [
  107. _buildFineTuneButton(
  108. Alignment.centerLeft,
  109. Icons.arrow_circle_left_outlined,
  110. const Offset(-1 * fineTuneStep, 0)),
  111. _buildFineTuneButton(
  112. Alignment.topCenter,
  113. Icons.arrow_circle_up_outlined,
  114. const Offset(0, -1 * fineTuneStep)),
  115. _buildFineTuneButton(
  116. Alignment.centerRight,
  117. Icons.arrow_circle_right_outlined,
  118. const Offset(fineTuneStep, 0)),
  119. _buildFineTuneButton(
  120. Alignment.bottomCenter,
  121. Icons.arrow_circle_down_outlined,
  122. const Offset(0, fineTuneStep)),
  123. ],
  124. ),
  125. ),
  126. ],
  127. ),
  128. ),
  129. ),
  130. ],
  131. ),
  132. ],
  133. );
  134. }
  135. Align _buildFineTuneButton(
  136. Alignment alignment, IconData icon, Offset offset) {
  137. return Align(
  138. alignment: alignment,
  139. child: GestureDetector(
  140. onTapDown: (e) {
  141. _onTapFineTuneButton(offset);
  142. },
  143. onLongPress: () {
  144. _startLongPressTimer(offset);
  145. },
  146. onLongPressUp: () {
  147. _stopLongPressTimer();
  148. },
  149. child: Icon(
  150. icon,
  151. color: Colors.white,
  152. size: arrowButtonSize,
  153. ),
  154. ),
  155. );
  156. }
  157. void _initContainerParam() {
  158. final containerLayer = context.findRenderObject() as RenderBox;
  159. containerPos = containerLayer.localToGlobal(Offset.zero);
  160. containerSize = containerLayer.size;
  161. }
  162. void _updateAlignment(Offset touchPos) {
  163. if (touchPos.dx < widget.panelSize.width) {
  164. if (ifSetTop && (touchPos.dy < containerSize.height / 2)) {
  165. setState(() {
  166. ifSetTop = false;
  167. });
  168. } else if (!ifSetTop && (touchPos.dy > containerSize.height / 2)) {
  169. setState(() {
  170. ifSetTop = true;
  171. });
  172. }
  173. } else if (ifSetTop) {
  174. setState(() {
  175. ifSetTop = false;
  176. });
  177. }
  178. }
  179. void _onMobileTouchEndEvent(_, Offset offset) {
  180. isCombined = false;
  181. if (_currWorkingItem?.feature != null) {
  182. int pointsNum = 0;
  183. pointsNum =
  184. _currWorkingItem?.feature!.innerPoints.toSet().toList().length ?? 0;
  185. if (pointsNum == 1) {
  186. setState(() {
  187. ifShowPanel = true;
  188. });
  189. Offset startPoint = application
  190. .activeMeasureItem!.feature!.innerPoints[0]
  191. .toOffset()
  192. .scale(containerSize.width, containerSize.height);
  193. _updateAlignment(startPoint);
  194. return;
  195. }
  196. if (application.activeMeasureItem is TopMeasureItem) {
  197. isCombined = true;
  198. if (_currWorkingItem?.feature != null) {
  199. pointsNum =
  200. _currWorkingItem!.feature!.innerPoints.toSet().toList().length;
  201. if (pointsNum == 1) {
  202. setState(() {
  203. ifShowPanel = true;
  204. });
  205. Offset startPoint = _currWorkingItem!.feature!.innerPoints[0]
  206. .toOffset()
  207. .scale(containerSize.width, containerSize.height);
  208. _updateAlignment(startPoint);
  209. return;
  210. }
  211. }
  212. }
  213. }
  214. setState(() {
  215. ifShowPanel = false;
  216. });
  217. }
  218. void _onMobileTouchEvent(_, Offset offset) {
  219. if (!ifShowPanel) return;
  220. setState(() {
  221. ifShowPanel = false;
  222. });
  223. }
  224. void _onActiveMeasureItemChanged(_, IMeasureItem? item) {
  225. if (!ifShowPanel) return;
  226. setState(() {
  227. ifShowPanel = false;
  228. });
  229. }
  230. void _onTapFineTuneButton(Offset offset) {
  231. if (_currWorkingItem?.feature != null) {
  232. HapticFeedback.lightImpact();
  233. _currWorkingItem?.update();
  234. int pointsNum = 0;
  235. pointsNum = _currWorkingItem?.feature!.innerPoints.length ?? 0;
  236. if (pointsNum > 0) {
  237. for (int i = 0; i < pointsNum; i++) {
  238. _currWorkingItem?.feature!.innerPoints[i]
  239. .addOffset(offset.dx, offset.dy);
  240. }
  241. }
  242. }
  243. }
  244. /// 开启长按定时器
  245. void _startLongPressTimer(Offset offset) {
  246. /// 随着触发次数的增多,触发的距离越来越长
  247. int fineTuneTimes = 0;
  248. _timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
  249. fineTuneTimes++;
  250. final scale = (1 + fineTuneTimes * 0.1).truncate().toDouble();
  251. _onTapFineTuneButton(offset.scale(scale, scale));
  252. });
  253. }
  254. void _stopLongPressTimer() {
  255. if (_timer != null) {
  256. _timer!.cancel();
  257. _timer = null;
  258. }
  259. }
  260. }