LCOV - code coverage report
Current view: top level - lib/charts/painter - foreground_chart_painter.dart Coverage Total Hit
Test: lcov.info Lines: 62.6 % 107 67
Test Date: 2026-11-14 15:08:11 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2023 The terCAD team. All rights reserved.
       2              : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
       3              : 
       4              : import 'package:app_finance/_classes/herald/app_locale.dart';
       5              : import 'package:app_finance/charts/painter/abstract_painter.dart';
       6              : import 'package:material_ui/material_ui.dart';
       7              : import 'package:intl/intl.dart' as intl;
       8              : 
       9              : typedef Percentage = int;
      10              : 
      11              : class ForegroundChartPainter extends AbstractPainter {
      12              :   final Color color;
      13              :   final Color lineColor;
      14              :   final Color areaColor;
      15              :   final Color background;
      16              :   final List<double> yArea;
      17              :   final Type? xType;
      18              :   final Type? yType;
      19              :   final List<dynamic> yMap;
      20              :   late final double textArea;
      21              :   late final double shift;
      22              :   late final int yDiv;
      23              :   final int yDivider;
      24              :   late final int xDiv;
      25              :   final int xDivider;
      26              :   final dynamic xTpl;
      27              :   final dynamic yTpl;
      28              : 
      29              :   static const double coercion = 1.2;
      30              : 
      31            1 :   ForegroundChartPainter({
      32              :     required super.size,
      33              :     super.indent = 0.0,
      34              :     this.areaColor = Colors.green,
      35              :     this.background = Colors.grey,
      36              :     this.color = Colors.black,
      37              :     this.lineColor = Colors.black,
      38              :     this.xDivider = 12,
      39              :     super.xMin = 0.0,
      40              :     super.xMax = 1.0,
      41              :     this.xTpl,
      42              :     this.xType = double,
      43              :     this.yArea = const [],
      44              :     this.yMap = const [],
      45              :     this.yDivider = 12,
      46              :     super.yMin = 0.0,
      47              :     super.yMax = 1.0,
      48              :     this.yTpl,
      49              :     this.yType = double,
      50              :   }) {
      51            1 :     _setTextArea();
      52            3 :     shift = textArea * coercion;
      53            1 :     final tmp = size ?? const Size(0, 0);
      54            5 :     yDiv = yDivider * tmp.height ~/ 400;
      55            5 :     xDiv = xDivider * tmp.width ~/ 640;
      56              :   }
      57              : 
      58            1 :   void _setTextArea() {
      59            3 :     double tmp = (size?.width ?? 0) / 20;
      60            1 :     if (tmp > 32) {
      61            0 :       textArea = 32.0;
      62            1 :     } else if (tmp < 20) {
      63            1 :       textArea = 20.0;
      64              :     } else {
      65            0 :       textArea = tmp;
      66              :     }
      67              :   }
      68              : 
      69            1 :   @override
      70              :   void paint(Canvas canvas, Size size) {
      71            1 :     size = this.size ?? size;
      72            1 :     _paintAxisY(canvas, size);
      73            1 :     _paintAxisX(canvas, size);
      74            3 :     if (yArea.length == 2) {
      75            0 :       _plotAssert(canvas, size);
      76            0 :       _paintAssertLine(canvas, size, (yArea[0] + yArea[1]) / 2);
      77            3 :     } else if (yArea.length == 1) {
      78            0 :       _paintAssertLine(canvas, size, yArea[0]);
      79              :     }
      80              :   }
      81              : 
      82            0 :   void _paintAssertLine(Canvas canvas, Size size, double yPos) {
      83            0 :     final line = Paint()
      84            0 :       ..color = areaColor
      85            0 :       ..strokeWidth = 1;
      86            0 :     if (yMax == 0 || yDiv == 0) {
      87              :       return;
      88              :     }
      89            0 :     double y = _shiftStep(size.height, textArea, yDiv, (yPos - yMin) / (yMax / yDiv));
      90            0 :     canvas.drawLine(Offset(shift, y), Offset(size.width, y), line);
      91              :   }
      92              : 
      93            0 :   void _plotAssert(Canvas canvas, Size size) {
      94            0 :     final background = Paint()
      95            0 :       ..color = areaColor.withValues(alpha: 0.1)
      96            0 :       ..style = PaintingStyle.fill;
      97            0 :     if (yMax == 0 || yDiv == 0) {
      98              :       return;
      99              :     }
     100            0 :     canvas.drawRect(
     101            0 :       Rect.fromPoints(
     102            0 :         Offset(shift, _shiftStep(size.height, textArea, yDiv, ((yArea[0] - yMin) / (yMax / yDiv)).round())),
     103            0 :         Offset(size.width, _shiftStep(size.height, textArea, yDiv, ((yArea[1] - yMin) / (yMax / yDiv)).round())),
     104              :       ),
     105              :       background,
     106              :     );
     107              :   }
     108              : 
     109            1 :   void _paintText(Canvas canvas, double x, double y, String text) {
     110            1 :     final textPainter = TextPainter(
     111            1 :       text: TextSpan(
     112              :         text: text,
     113            1 :         style: TextStyle(
     114            1 :           color: color,
     115            2 :           fontSize: textArea / 2.2,
     116              :           fontFamily: 'Abel-Regular',
     117              :           fontFamilyFallback: const ['Arial', 'Helvetica', 'sans-serif'],
     118              :         ),
     119              :       ),
     120              :       textDirection: TextDirection.ltr,
     121              :     );
     122            1 :     textPainter.layout();
     123            4 :     textPainter.paint(canvas, Offset(x - textPainter.width, y));
     124              :   }
     125              : 
     126            0 :   void _paintIcon(Canvas canvas, double x, double y, IconData? icon) {
     127              :     if (icon == null) {
     128              :       return;
     129              :     }
     130            0 :     final textPainter = TextPainter(
     131            0 :       text: TextSpan(
     132            0 :         text: String.fromCharCode(icon.codePoint),
     133            0 :         style: TextStyle(
     134            0 :           color: color,
     135            0 :           fontSize: textArea,
     136            0 :           fontFamily: Icons.question_mark.fontFamily,
     137              :         ),
     138              :       ),
     139              :       textDirection: TextDirection.ltr,
     140              :     );
     141            0 :     textPainter.layout();
     142            0 :     textPainter.paint(canvas, Offset(x - textPainter.width, y));
     143              :   }
     144              : 
     145            1 :   void _paintAxisY(Canvas canvas, Size size) {
     146            1 :     final lineColor = Paint()
     147            3 :       ..color = this.lineColor.withValues(alpha: 0.4)
     148            1 :       ..strokeWidth = 0.5;
     149            5 :     double step = (yMax - yMin) / yDiv;
     150            3 :     for (int i = 0; i < yDiv; i++) {
     151            1 :       double delta = step * i;
     152            4 :       double y = _shiftStep(size.height, textArea, yDiv, i);
     153            1 :       if (yType != null || i == 0) {
     154            5 :         canvas.drawLine(Offset(shift, y), Offset(size.width, y), lineColor);
     155              :       }
     156            1 :       switch (yType) {
     157            1 :         case double:
     158            2 :           final formatter = yTpl ?? intl.NumberFormat.decimalPatternDigits(decimalDigits: 2, locale: AppLocale.code);
     159            8 :           _paintText(canvas, textArea, y - textArea / 3, formatter.format(yMin + delta));
     160              :           break;
     161            0 :         case IconData:
     162            0 :           final code = i >= yMap.length ? null : yMap[i];
     163            0 :           _paintIcon(canvas, textArea, y - textArea, code);
     164              :           break;
     165            0 :         case Percentage:
     166            0 :           final formatter = yTpl ?? intl.NumberFormat.decimalPatternDigits(decimalDigits: 0, locale: AppLocale.code);
     167            0 :           _paintText(canvas, textArea, y - textArea / 3, formatter.format(yMin + delta) + ' %');
     168              :           break;
     169              :       }
     170              :     }
     171              :   }
     172              : 
     173            1 :   double _shiftStep(total, shift, div, i) {
     174            4 :     return (total - shift) * (1 - i / div);
     175              :   }
     176              : 
     177            1 :   void _paintAxisX(Canvas canvas, Size size) {
     178            1 :     final lineColor = Paint()
     179            3 :       ..color = this.lineColor.withValues(alpha: 0.4)
     180            1 :       ..strokeWidth = 0.5;
     181            1 :     final background = Paint()
     182            3 :       ..color = this.background.withValues(alpha: 0.05)
     183            1 :       ..style = PaintingStyle.fill;
     184            1 :     double step = switch (xType) {
     185            7 :       DateTime => (xMax - xMin - 1) / xDiv,
     186            0 :       _ => (xMax - xMin) / xDiv,
     187              :     };
     188            3 :     double height = size.height - textArea;
     189            3 :     for (int i = 0; i <= xDiv; i++) {
     190            3 :       double delta = step * (xDiv - i);
     191            4 :       double x = _shiftStep(size.width, shift, xDiv, i);
     192            8 :       canvas.drawLine(Offset(x + shift, height), Offset(x + shift, height + 4), lineColor);
     193            2 :       if (i < xDiv) {
     194            2 :         dynamic value = xMin + delta;
     195            2 :         if (xType == DateTime) {
     196            1 :           final formatter = xTpl ?? intl.DateFormat('d');
     197            3 :           value = formatter.format(DateTime.fromMillisecondsSinceEpoch(value.toInt()));
     198              :         } else {
     199            0 :           final formatter = xTpl ?? intl.NumberFormat.decimalPatternDigits(decimalDigits: 1, locale: AppLocale.code);
     200            0 :           value = formatter.format(value as double);
     201              :         }
     202            1 :         if (xType != null) {
     203            6 :           _paintText(canvas, x + shift - 2, height + 1, value.toString());
     204              :         }
     205              :       }
     206            2 :       if (i % 2 != 0) {
     207            1 :         canvas.drawRect(
     208            1 :           Rect.fromPoints(
     209            3 :             Offset(x + shift, 0),
     210            8 :             Offset(_shiftStep(size.width, shift, xDiv, i - 1) + shift, height),
     211              :           ),
     212              :           background,
     213              :         );
     214              :       }
     215              :     }
     216              :   }
     217              : }
        

Generated by: LCOV version 2.0-1