import 'dart:ui' as ui; import 'package:fis_measure/interfaces/process/items/item_feature.dart'; import 'package:fis_measure/interfaces/process/workspace/application.dart'; import 'package:fis_measure/process/workspace/measure_data_controller.dart'; import 'package:fis_measure/values/colors.dart'; import 'package:fis_ui/index.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'converter.dart'; class MeasureResultPanel extends StatefulWidget { const MeasureResultPanel({Key? key}) : super(key: key); @override State createState() => _MeasureResultPanelState(); } class _MeasureResultPanelState extends State { final _scrollController = ScrollController(); final application = Get.find(); final measureData = Get.find(); final List lines = []; bool get hasOutputs => lines.isNotEmpty; @override Widget build(BuildContext context) { if (!hasOutputs || !measureData.measureSystemSetting.showResultWindow) { return const SizedBox(); } return Align( alignment: _getAlignmentFromLocation( measureData.measureSystemSetting.showResultLocation), child: Container( margin: const EdgeInsets.only(left: 2, top: 2, right: 2, bottom: 2), constraints: const BoxConstraints( maxWidth: 600, minWidth: 150, maxHeight: 400, ), decoration: BoxDecoration( border: Border.all( color: MeasureColors.ResultBorder, width: 2, ), borderRadius: BorderRadius.circular(4), color: Colors.black.withOpacity(0.7), ), // padding: const EdgeInsets.only(left: 2), child: FThemeWidget( data: Theme.of(context).copyWith( scrollbarTheme: ScrollbarThemeData( thumbColor: MaterialStateProperty.all( MeasureColors.ResultScrollbar, ), crossAxisMargin: 1, ), ), child: FScrollbar( isAlwaysShown: false, controller: _scrollController, radius: ui.Radius.zero, child: FSingleChildScrollView( controller: _scrollController, child: FRepaintBoundary( child: FIntrinsicHeight( child: FIntrinsicWidth( child: FContainer( margin: const EdgeInsets.fromLTRB(5, 1, 5, 5), child: FColumn( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: _buildMainList(), ), ), ), ), ), ), ), ), ), ); } @override void initState() { _updateOutputs(); application.updateRenderReady.addListener(_onMeasureRerenderReady); measureData.measureSystemSettingChanged .addListener(_onMeasureSystemSettingChanged); super.initState(); } @override void dispose() { application.updateRenderReady.removeListener(_onMeasureRerenderReady); measureData.measureSystemSettingChanged .removeListener(_onMeasureSystemSettingChanged); super.dispose(); } /// 样式更新事件监听 void _onMeasureSystemSettingChanged(_, e) { _updateOutputs(); setState(() {}); } /// 面板对齐方式 Alignment _getAlignmentFromLocation(int locationNum) { switch (locationNum) { case 0: return Alignment.topLeft; case 1: return Alignment.bottomLeft; case 2: return Alignment.topRight; case 3: return Alignment.bottomRight; default: return Alignment.topLeft; } } List _buildMainList() { final list = [ _buildTitle(application.applicationName), _buildTitleDivider(), ]; for (var line in lines) { list.add(_buildLine(line)); } return list; } FWidget _buildLine(ResultLine result) { return FRow(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ FText( result.label, style: TextStyle( fontSize: measureData.measureSystemSetting.fontSize.toDouble(), color: MeasureColors.Primary, ), ), const FSizedBox(width: 8), FText( result.value ?? '', style: TextStyle( fontSize: measureData.measureSystemSetting.fontSize.toDouble(), color: MeasureColors.Primary, ), ), ]); } FWidget _buildTitle(String title) { return FText( title, style: TextStyle( fontSize: measureData.measureSystemSetting.fontSize.toDouble() - 2, color: MeasureColors.Primary, ), ); } FWidget _buildTitleDivider() { return const FDivider( color: Colors.white, height: 0.5, endIndent: 2, ); } void _onMeasureRerenderReady(Object sender, void e) { if (mounted) { setState(() { _updateOutputs(); _autoScrollToBottom(); }); } } void _updateOutputs() { lines.clear(); final features = []; for (var item in application.measureItems) { if (item.measuredFeatures.isNotEmpty) { features.addAll(item.measuredFeatures); } if (item.feature != null) { features.add(item.feature!); } } final idLength = features.length.toString().length; for (var feature in features) { final strList = FeatureValueDescConverter(feature).generate(idLength); lines.addAll(strList); } } void _autoScrollToBottom() { if (lines.isEmpty) return; Future.delayed(const Duration(milliseconds: 200), () { if (_scrollController.hasClients) { _scrollController.jumpTo(_scrollController.position.maxScrollExtent); } }); } } class ResultLine { ResultLine({required this.label, this.value}); String label; String? value; bool get isNotEmpty => value != null && value!.isNotEmpty && label.isNotEmpty; @override String toString() => '$label $value'; }