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'; import 'positioned_cursor.dart'; class MeasureMouseGesturePanel extends StatefulWidget { const MeasureMouseGesturePanel({Key? key}) : super(key: key); @override State createState() => _MeasureMouseGesturePanelState(); } class _MeasureMouseGesturePanelState extends State { Offset? mousePosition; Offset? cursorPosition; final mouseState = Get.find(); late final application = Get.find(); late final measureHandler = Get.find(); 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 Listener( onPointerDown: _rightClickListener, child: 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; 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(), ], ), ), ), ); } Widget _buildCursor() { switch (displayType) { case CursorDisplayType.none: return Container(); case CursorDisplayType.normal: return const PositionedCursor(); case CursorDisplayType.cross: return CrossIndicator( areaRegion: Rect.fromLTRB( tissueTMPixelRegion.left, tissueTMPixelRegion.top, tissueTMPixelRegion.right, tissueTMPixelRegion.bottom), ); } } /// 右键右上角角标 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(); /// 延迟进行同步,避免窗口手动缩放时引发 Error: Build scheduled during frame. Future.delayed(const Duration(milliseconds: 1000), () { _setScaleRatio(); }); } /// 同步缩放比 void _setScaleRatio() { mouseState.cursorScaleRatio = application.displayScaleRatio; } 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 .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, )); } } } } /// 鼠标右键监听 void _rightClickListener(PointerDownEvent event) { if (event.kind == PointerDeviceKind.mouse && event.buttons == kSecondaryMouseButton) { measureHandler.onRightClickFinishMeasure.emit(this, true); setState(() { isShowRightClickTip = false; }); } } } enum CursorDisplayType { none, cross, normal, }