From a22c9f0923d77e1e292f13d6f2e17a0ec39249fc Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:34:10 +0800 Subject: [PATCH 01/29] sahani/migrate_flutter_to_version_3_10 (#225) - migrate flutter to version 3.10.0 --- example/pubspec.yaml | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 89fc5ca9a..dabcd6756 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: diff --git a/pubspec.yaml b/pubspec.yaml index 45e53f113..625197961 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.0.0 <4.0.0' flutter: ">=3.10.1" dependencies: From a4e0f4d81ce45633c3fefe39d7d5e4942a4b2074 Mon Sep 17 00:00:00 2001 From: Hamed Rezaee <57184669+hamed-deriv@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:15:19 +0800 Subject: [PATCH 02/29] hamed/update_dependencies (#227) - update dependencies --- example/pubspec.yaml | 4 ++-- pubspec.yaml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index dabcd6756..4e81f5a1a 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: @@ -26,7 +26,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: dev + ref: flutter-version-3 flutter_test: sdk: flutter intl_utils: ^2.8.2 diff --git a/pubspec.yaml b/pubspec.yaml index 625197961..16d0641ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.10.1" dependencies: @@ -16,7 +16,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_technical_analysis - ref: dev + ref: flutter-version-3 equatable: ^2.0.5 intl: ^0.18.0 @@ -31,7 +31,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: dev + ref: flutter-version-3 build_runner: ^2.3.3 intl_utils: ^2.8.2 From fe78002ff27842d2d5b0c6d1482a364b2d7239d8 Mon Sep 17 00:00:00 2001 From: Maryia <103177211+maryia-binary@users.noreply.github.com> Date: Mon, 31 Oct 2022 06:04:47 +0300 Subject: [PATCH 03/29] Maryia/78669/add drawing tools list (#195) - setup drawing tools config --- example/lib/main.dart | 2 +- lib/generated/intl/messages_en.dart | 2 + lib/generated/l10n.dart | 10 + lib/l10n/intl_en.arb | 5 +- lib/src/add_ons/add_on_config.dart | 21 ++ lib/src/add_ons/add_ons_repository.dart | 76 +++++++ .../drawing_tools_ui/drawing_tool_config.dart | 12 ++ .../drawing_tools_dialog.dart | 93 ++++++++ .../indicators_ui/indicator_config.dart | 26 +-- .../add_ons/indicators_ui/indicator_item.dart | 6 +- .../indicators_ui/indicator_repository.dart | 79 ------- .../indicators_ui/indicators_dialog.dart | 9 +- .../markers/marker_series.dart | 1 - lib/src/deriv_chart/deriv_chart.dart | 204 +++++++++++------- pubspec.yaml | 2 +- 15 files changed, 351 insertions(+), 197 deletions(-) create mode 100644 lib/src/add_ons/add_on_config.dart create mode 100644 lib/src/add_ons/add_ons_repository.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart delete mode 100644 lib/src/add_ons/indicators_ui/indicator_repository.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index b5132f261..418ee4299 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -310,7 +310,7 @@ class _FullscreenChartState extends State { _updateSampleSLAndTP(); - WidgetsBinding.instance.addPostFrameCallback( + WidgetsBinding.instance?.addPostFrameCallback( (Duration timeStamp) => _controller.scrollToLastTick(), ); } on BaseAPIException catch (e) { diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 83eae7405..f18e4d26c 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -83,6 +83,8 @@ class MessageLookup extends MessageLookupByLibrary { "labelType": MessageLookupByLibrary.simpleMessage("Type"), "warnCheckAssetSearchingText": MessageLookupByLibrary.simpleMessage( "Try checking your spelling or use a different term"), + "warnFailedLoadingDrawingTools": MessageLookupByLibrary.simpleMessage( + "Failed to load drawing tools."), "warnFailedLoadingIndicators": MessageLookupByLibrary.simpleMessage("Failed to load indicators.") }; diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 2df604f32..d6adf23d0 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -469,6 +469,16 @@ class ChartLocalization { args: [], ); } + + /// `Failed to load drawing tools.` + String get warnFailedLoadingDrawingTools { + return Intl.message( + 'Failed to load drawing tools.', + name: 'warnFailedLoadingDrawingTools', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 1ad4abccc..e7aae92ff 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -41,5 +41,6 @@ "labelShowLines": "Show Lines", "labelShowFractals": "Show Fractals", "labelIsSmooth": "Is Smooth", - "warnFailedLoadingIndicators": "Failed to load indicators." -} \ No newline at end of file + "warnFailedLoadingIndicators": "Failed to load indicators.", + "warnFailedLoadingDrawingTools": "Failed to load drawing tools." +} diff --git a/lib/src/add_ons/add_on_config.dart b/lib/src/add_ons/add_on_config.dart new file mode 100644 index 000000000..836dcb7b1 --- /dev/null +++ b/lib/src/add_ons/add_on_config.dart @@ -0,0 +1,21 @@ +import 'package:equatable/equatable.dart'; + +/// Config for add-ons such as indicators and drawing tools +abstract class AddOnConfig with EquatableMixin { + /// Initializes [AddOnConfig]. + const AddOnConfig({ + this.isOverlay = true, + }); + + /// Whether the add-on is an overlay on the main chart or displays on a + /// separate chart. Default is set to `true`. + final bool isOverlay; + + /// Serialization to JSON. Serves as value in key-value storage. + /// + /// Must specify add-on `name` with `nameKey`. + Map toJson(); + + @override + List get props => [toJson()]; +} diff --git a/lib/src/add_ons/add_ons_repository.dart b/lib/src/add_ons/add_ons_repository.dart new file mode 100644 index 000000000..3f291295f --- /dev/null +++ b/lib/src/add_ons/add_ons_repository.dart @@ -0,0 +1,76 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Storage key of saved indicators. +const String addOnsKey = 'addOns'; + +/// Holds indicators/drawing tools that were added to the Chart during runtime. +class AddOnsRepository extends ChangeNotifier { + /// Initializes + AddOnsRepository(this._addOnConfig) : _addOns = []; + + final dynamic _addOnConfig; + + final List _addOns; + SharedPreferences? _prefs; + + /// List of indicators or drawing tools. + List get addOns => _addOns; + + /// Loads user selected indicators or drawing tools from shared preferences. + void loadFromPrefs(SharedPreferences prefs) { + _prefs = prefs; + + if (!prefs.containsKey(addOnsKey)) { + // No saved indicators or drawing tools. + return; + } + + final List encodedAddOns = prefs.getStringList(addOnsKey)!; + _addOns.clear(); + + for (final String encodedAddOn in encodedAddOns) { + final T addOnConfig = _addOnConfig.fromJson(jsonDecode(encodedAddOn)); + _addOns.add(addOnConfig); + } + notifyListeners(); + } + + /// Adds a new indicator or drawing tool and updates storage. + void add(T addOnConfig) { + _addOns.add(addOnConfig); + _writeToPrefs(); + notifyListeners(); + } + + /// Updates indicator or drawing tool at [index] and updates storage. + void updateAt(int index, T addOnConfig) { + if (index < 0 || index >= _addOns.length) { + return; + } + _addOns[index] = addOnConfig; + _writeToPrefs(); + notifyListeners(); + } + + /// Removes indicator/drawing tool at [index] from repository and updates storage. + void removeAt(int index) { + if (index < 0 || index >= _addOns.length) { + return; + } + _addOns.removeAt(index); + _writeToPrefs(); + notifyListeners(); + } + + Future _writeToPrefs() async { + if (_prefs != null) { + await _prefs!.setStringList( + addOnsKey, + _addOns.map((T config) => jsonEncode(config)).toList(), + ); + } + } +} diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart new file mode 100644 index 000000000..e1d1160d7 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -0,0 +1,12 @@ +import 'package:deriv_chart/src/add_ons/add_on_config.dart'; +import 'package:flutter/material.dart'; + +/// Drawing tools config +@immutable +abstract class DrawingToolConfig extends AddOnConfig { + /// Initializes + const DrawingToolConfig({bool isOverlay = true}) + : super(isOverlay: isOverlay); + +// TODO(maryia-binary): Add config for drawing tools UI +} diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart new file mode 100644 index 000000000..3f1eeb0d7 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -0,0 +1,93 @@ +import 'package:deriv_chart/src/widgets/animated_popup.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; + +/// Drawing tools dialog with available drawing tools. +class DrawingToolsDialog extends StatefulWidget { + @override + _DrawingToolsDialogState createState() => _DrawingToolsDialogState(); +} + +class _DrawingToolsDialogState extends State { + String? _selectedDrawingTool; + + @override + Widget build(BuildContext context) { + final AddOnsRepository repo = + context.watch>(); + + return AnimatedPopupDialog( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DropdownButton( + value: _selectedDrawingTool, + hint: const Text('Select drawing tool'), + items: const >[ + DropdownMenuItem( + child: Text('Channel'), + value: 'Channel', + ), + DropdownMenuItem( + child: Text('Continuous'), + value: 'Continuous', + ), + DropdownMenuItem( + child: Text('Fib Fan'), + value: 'Fib Fan', + ), + DropdownMenuItem( + child: Text('Horizontal'), + value: 'Horizontal', + ), + DropdownMenuItem( + child: Text('Line'), + value: 'Line', + ), + DropdownMenuItem( + child: Text('Ray'), + value: 'Ray', + ), + DropdownMenuItem( + child: Text('Rectangle'), + value: 'Rectangle', + ), + DropdownMenuItem( + child: Text('Trend'), + value: 'Trend', + ), + DropdownMenuItem( + child: Text('Vertical'), + value: 'Vertical', + ), + // TODO(maryia-binary): add real drawing tools above + ], + onChanged: (String? config) { + setState(() { + _selectedDrawingTool = config; + }); + }, + ), + const SizedBox(width: 16), + ElevatedButton( + child: const Text('Add'), + onPressed: + _selectedDrawingTool is DrawingToolConfig ? () {} : null), + ], + ), + Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: repo.addOns.length, + itemBuilder: (BuildContext context, int index) => Container(), + ), + ), + ], + ), + ); + } +} diff --git a/lib/src/add_ons/indicators_ui/indicator_config.dart b/lib/src/add_ons/indicators_ui/indicator_config.dart index cefa7fb6c..450f41234 100644 --- a/lib/src/add_ons/indicators_ui/indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/indicator_config.dart @@ -1,5 +1,4 @@ -import 'dart:convert'; - +import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/roc/roc_indicator_config.dart'; @@ -32,11 +31,9 @@ import 'zigzag_indicator/zigzag_indicator_config.dart'; /// Indicator config @immutable -abstract class IndicatorConfig { +abstract class IndicatorConfig extends AddOnConfig { /// Initializes - const IndicatorConfig({ - this.isOverlay = true, - }); + const IndicatorConfig({bool isOverlay = true}) : super(isOverlay: isOverlay); /// Creates a concrete indicator config from JSON. factory IndicatorConfig.fromJson(Map json) { @@ -95,26 +92,9 @@ abstract class IndicatorConfig { } } - /// Whether the indicator is an overlay on the main chart or displays on a - /// separate chart. Default is set to `true`. - final bool isOverlay; - /// Key of indicator name property in JSON. static const String nameKey = 'name'; - /// Serialization to JSON. Serves as value in key-value storage. - /// - /// Must specify indicator `name` with `nameKey`. - Map toJson(); - - @override - bool operator ==(dynamic other) => - other is IndicatorConfig && - jsonEncode(other.toJson()) == jsonEncode(toJson()); - - @override - int get hashCode => jsonEncode(toJson()).hashCode; - /// Indicators supported field types static final Map supportedFieldTypes = { diff --git a/lib/src/add_ons/indicators_ui/indicator_item.dart b/lib/src/add_ons/indicators_ui/indicator_item.dart index 149fe3146..f6cfa8667 100644 --- a/lib/src/add_ons/indicators_ui/indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/indicator_item.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'callbacks.dart'; import 'indicator_config.dart'; -import 'indicator_repository.dart'; /// Representing and indicator item in indicators list dialog. abstract class IndicatorItem extends StatefulWidget { @@ -42,13 +42,13 @@ abstract class IndicatorItemState extends State { /// Indicators repository @protected - late IndicatorsRepository indicatorsRepo; + late AddOnsRepository indicatorsRepo; @override void didChangeDependencies() { super.didChangeDependencies(); - indicatorsRepo = Provider.of(context); + indicatorsRepo = Provider.of>(context); } @override diff --git a/lib/src/add_ons/indicators_ui/indicator_repository.dart b/lib/src/add_ons/indicators_ui/indicator_repository.dart deleted file mode 100644 index 9113a9bcb..000000000 --- a/lib/src/add_ons/indicators_ui/indicator_repository.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import 'indicator_config.dart'; - -/// Storage key of saved indicators. -const String indicatorsKey = 'indicators'; - -/// Holds indicators that were added to the Chart during runtime. -class IndicatorsRepository extends ChangeNotifier { - /// Initializes - IndicatorsRepository() : _indicators = []; - - final List _indicators; - SharedPreferences? _prefs; - - /// List of indicators. - List get indicators => _indicators; - - /// Loads user selected indicators from shared preferences. - void loadFromPrefs(SharedPreferences prefs) { - _prefs = prefs; - - if (!prefs.containsKey(indicatorsKey)) { - // No saved indicators. - return; - } - - final List strings = prefs.getStringList(indicatorsKey)!; - _indicators.clear(); - - for (final String string in strings) { - final IndicatorConfig indicatorConfig = - IndicatorConfig.fromJson(jsonDecode(string)); - _indicators.add(indicatorConfig); - } - notifyListeners(); - } - - /// Adds a new indicator and updates storage. - void add(IndicatorConfig indicatorConfig) { - _indicators.add(indicatorConfig); - _writeToPrefs(); - notifyListeners(); - } - - /// Updates indicator at [index] and updates storage. - void updateAt(int index, IndicatorConfig indicatorConfig) { - if (index < 0 || index >= _indicators.length) { - return; - } - _indicators[index] = indicatorConfig; - _writeToPrefs(); - notifyListeners(); - } - - /// Removes indicator at [index] from repository and updates storage. - void removeAt(int index) { - if (index < 0 || index >= _indicators.length) { - return; - } - _indicators.removeAt(index); - _writeToPrefs(); - notifyListeners(); - } - - Future _writeToPrefs() async { - if (_prefs != null) { - await _prefs!.setStringList( - indicatorsKey, - _indicators - .map((IndicatorConfig config) => jsonEncode(config)) - .toList(), - ); - } - } -} diff --git a/lib/src/add_ons/indicators_ui/indicators_dialog.dart b/lib/src/add_ons/indicators_ui/indicators_dialog.dart index 57d5f6638..f364857cf 100644 --- a/lib/src/add_ons/indicators_ui/indicators_dialog.dart +++ b/lib/src/add_ons/indicators_ui/indicators_dialog.dart @@ -6,6 +6,7 @@ import 'package:deriv_chart/src/add_ons/indicators_ui/dpo_indicator/dpo_indicato import 'package:deriv_chart/src/add_ons/indicators_ui/gator/gator_indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/smi/smi_indicator_config.dart'; +import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/widgets/animated_popup.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -18,7 +19,6 @@ import 'donchian_channel/donchian_channel_indicator_config.dart'; import 'fcb_indicator/fcb_indicator_config.dart'; import 'ichimoku_clouds/ichimoku_cloud_indicator_config.dart'; import 'indicator_config.dart'; -import 'indicator_repository.dart'; import 'ma_env_indicator/ma_env_indicator_config.dart'; import 'macd_indicator/macd_indicator_config.dart'; import 'parabolic_sar/parabolic_sar_indicator_config.dart'; @@ -38,7 +38,8 @@ class _IndicatorsDialogState extends State { @override Widget build(BuildContext context) { - final IndicatorsRepository repo = context.watch(); + final AddOnsRepository repo = + context.watch>(); return AnimatedPopupDialog( child: Column( @@ -161,9 +162,9 @@ class _IndicatorsDialogState extends State { Expanded( child: ListView.builder( shrinkWrap: true, - itemCount: repo.indicators.length, + itemCount: repo.addOns.length, itemBuilder: (BuildContext context, int index) => - repo.indicators[index].getItem( + repo.addOns[index].getItem( (IndicatorConfig updatedConfig) => repo.updateAt(index, updatedConfig), () { diff --git a/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart b/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart index 10dc8a3ed..d4e7ebde4 100644 --- a/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart @@ -5,7 +5,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/crosshair/find.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import '../chart_data.dart'; - import 'marker_painter.dart'; /// Marker series diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index dec9c1410..696beedd6 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -1,6 +1,8 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; -import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_repository.dart'; +import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicators_dialog.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/annotations/chart_annotation.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; @@ -88,100 +90,136 @@ class DerivChart extends StatefulWidget { } class _DerivChartState extends State { - final IndicatorsRepository _indicatorsRepo = IndicatorsRepository(); + final AddOnsRepository _indicatorsRepo = + AddOnsRepository(IndicatorConfig); + final AddOnsRepository _drawingToolsRepo = + AddOnsRepository(DrawingToolConfig); @override void initState() { super.initState(); - loadSavedIndicators(); + loadSavedIndicatorsAndDrawingTools(); } - Future loadSavedIndicators() async { + Future loadSavedIndicatorsAndDrawingTools() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - try { - _indicatorsRepo.loadFromPrefs(prefs); - } on Exception { - // ignore: unawaited_futures - showDialog( - context: context, - builder: (BuildContext context) => AnimatedPopupDialog( - child: Center( - child: Text( - ChartLocalization.of(context).warnFailedLoadingIndicators, + final List _stateRepos = [ + _indicatorsRepo, + _drawingToolsRepo + ]; + _stateRepos.asMap().forEach((int index, dynamic element) { + try { + element.loadFromPrefs(prefs); + } on Exception { + // ignore: unawaited_futures + showDialog( + context: context, + builder: (BuildContext context) => AnimatedPopupDialog( + child: Center( + child: element is AddOnsRepository + ? Text(ChartLocalization.of(context) + .warnFailedLoadingIndicators) + : Text(ChartLocalization.of(context) + .warnFailedLoadingDrawingTools), ), - ), - )); - } + )); + } + }); } @override - Widget build(BuildContext context) => - ChangeNotifierProvider.value( - value: _indicatorsRepo, - builder: (BuildContext context, _) => Stack( - children: [ - Chart( - mainSeries: widget.mainSeries, - pipSize: widget.pipSize, - granularity: widget.granularity, - controller: widget.controller, - overlaySeries: [ - ...context - .watch() - .indicators - .where((IndicatorConfig indicatorConfig) => - indicatorConfig.isOverlay) - .map((IndicatorConfig indicatorConfig) => - indicatorConfig.getSeries( - IndicatorInput( - widget.mainSeries.input, - widget.granularity, - ), - )) - ], - bottomSeries: [ - ...context - .watch() - .indicators - .where((IndicatorConfig indicatorConfig) => - !indicatorConfig.isOverlay) - .map((IndicatorConfig indicatorConfig) => - indicatorConfig.getSeries( - IndicatorInput( - widget.mainSeries.input, - widget.granularity, - ), - )) - ], - markerSeries: widget.markerSeries, - theme: widget.theme, - onCrosshairAppeared: widget.onCrosshairAppeared, - onVisibleAreaChanged: widget.onVisibleAreaChanged, - isLive: widget.isLive, - dataFitEnabled: widget.dataFitEnabled, - opacity: widget.opacity, - annotations: widget.annotations, - chartAxisConfig: widget.chartAxisConfig, - ), - Align( - alignment: Alignment.topLeft, - child: IconButton( - icon: const Icon(Icons.architecture), - onPressed: () { - showDialog( - context: context, - builder: ( - BuildContext context, - ) => - ChangeNotifierProvider.value( - value: _indicatorsRepo, - child: IndicatorsDialog(), - ), - ); - }, + Widget build(BuildContext context) => MultiProvider( + providers: >[ + ChangeNotifierProvider>.value( + value: _indicatorsRepo), + ChangeNotifierProvider>.value( + value: _drawingToolsRepo), + ], + child: Builder( + builder: (BuildContext context) => Stack( + children: [ + Chart( + mainSeries: widget.mainSeries, + pipSize: widget.pipSize, + granularity: widget.granularity, + controller: widget.controller, + overlaySeries: [ + ...context + .watch>() + .addOns + .where((IndicatorConfig indicatorConfig) => + indicatorConfig.isOverlay) + .map((IndicatorConfig indicatorConfig) => + indicatorConfig.getSeries( + IndicatorInput( + widget.mainSeries.input, + widget.granularity, + ), + )) + ], + bottomSeries: [ + ...context + .watch>() + .addOns + .where((IndicatorConfig indicatorConfig) => + !indicatorConfig.isOverlay) + .map((IndicatorConfig indicatorConfig) => + indicatorConfig.getSeries( + IndicatorInput( + widget.mainSeries.input, + widget.granularity, + ), + )) + ], + markerSeries: widget.markerSeries, + theme: widget.theme, + onCrosshairAppeared: widget.onCrosshairAppeared, + onVisibleAreaChanged: widget.onVisibleAreaChanged, + isLive: widget.isLive, + dataFitEnabled: widget.dataFitEnabled, + opacity: widget.opacity, + annotations: widget.annotations, + ), + Align( + alignment: Alignment.topLeft, + child: IconButton( + icon: const Icon(Icons.architecture), + onPressed: () { + showDialog( + context: context, + builder: ( + BuildContext context, + ) => + ChangeNotifierProvider< + AddOnsRepository>.value( + value: _indicatorsRepo, + child: IndicatorsDialog(), + ), + ); + }, + ), + ), + Align( + alignment: const FractionalOffset(0.1, 0), + child: IconButton( + icon: const Icon(Icons.drive_file_rename_outline_outlined), + onPressed: () { + showDialog( + context: context, + builder: ( + BuildContext context, + ) => + ChangeNotifierProvider< + AddOnsRepository>.value( + value: _drawingToolsRepo, + child: DrawingToolsDialog(), + ), + ); + }, + ), ), - ) - ], + ], + ), ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index 16d0641ef..b51cc27dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: flutter-version-3 + ref: dev build_runner: ^2.3.3 intl_utils: ^2.8.2 From c3b849e0af02aa79eed6998a7f138e94b8ca4a99 Mon Sep 17 00:00:00 2001 From: Bahar Date: Wed, 28 Dec 2022 13:02:20 +0800 Subject: [PATCH 04/29] bahar/82930/Add hollow chart type (#202) - Add hollow chart type --- example/lib/main.dart | 20 +- lib/deriv_chart.dart | 1 + .../ohlc_series/candle/candle_painter.dart | 42 ++-- .../ohlc_series/candle/candle_painting.dart | 30 --- .../hollow_candle/hollow_candle_painter.dart | 217 ++++++++++++++++++ .../hollow_candle/hollow_candle_series.dart | 28 +++ .../ohlc_series/ohlc_painting.dart | 30 +++ lib/src/models/chart_style.dart | 3 + lib/src/theme/chart_default_theme.dart | 2 +- .../theme/painting_styles/candle_style.dart | 12 +- 10 files changed, 322 insertions(+), 63 deletions(-) delete mode 100644 lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painting.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_series.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painting.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 418ee4299..8e45dc4b6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -394,9 +394,11 @@ class _FullscreenChartState extends State { children: [ ClipRect( child: DerivChart( - mainSeries: - style == ChartStyle.candles && ticks is List - ? CandleSeries(ticks as List) + mainSeries: style == ChartStyle.candles && + ticks is List + ? CandleSeries(ticks as List) + : style == ChartStyle.hollow && ticks is List + ? HollowCandleSeries(ticks as List) : LineSeries( ticks, style: const LineStyle(hasArea: true), @@ -701,16 +703,22 @@ class _FullscreenChartState extends State { IconButton _buildChartTypeButton() => IconButton( icon: Icon( - style == ChartStyle.line ? Icons.show_chart : Icons.insert_chart, + style == ChartStyle.line + ? Icons.show_chart + : style == ChartStyle.candles + ? Icons.insert_chart + : Icons.insert_chart_outlined_outlined, color: Colors.white, ), onPressed: () { Vibration.vibrate(duration: 50); setState(() { - if (style == ChartStyle.candles) { + if (style == ChartStyle.hollow) { style = ChartStyle.line; - } else { + } else if (style == ChartStyle.line) { style = ChartStyle.candles; + } else { + style = ChartStyle.hollow; } }); }, diff --git a/lib/deriv_chart.dart b/lib/deriv_chart.dart index 9e7994cae..32ad562a1 100644 --- a/lib/deriv_chart.dart +++ b/lib/deriv_chart.dart @@ -18,6 +18,7 @@ export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/line_series/line_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/series.dart'; export 'src/deriv_chart/chart/data_visualization/markers/active_marker.dart'; export 'src/deriv_chart/chart/data_visualization/markers/marker.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart index dc1634d8b..b154f84ba 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart @@ -9,7 +9,7 @@ import '../../../chart_data.dart'; import '../../data_painter.dart'; import '../../data_series.dart'; import '../../indexed_entry.dart'; -import 'candle_painting.dart'; +import '../ohlc_painting.dart'; /// A [DataPainter] for painting CandleStick data. class CandlePainter extends DataPainter> { @@ -35,7 +35,7 @@ class CandlePainter extends DataPainter> { final CandleStyle style = series.style as CandleStyle? ?? theme.candleStyle; _linePaint = Paint() - ..color = style.lineColor + ..color = style.neutralColor ..strokeWidth = 1.2; _positiveCandlePaint = Paint()..color = style.positiveColor; @@ -54,7 +54,7 @@ class CandlePainter extends DataPainter> { _paintCandle( canvas, - CandlePainting( + OhlcPainting( width: candleWidth, xCenter: epochToX(getEpochOf(candle, i)), yHigh: quoteToY(candle.high), @@ -69,7 +69,7 @@ class CandlePainter extends DataPainter> { final Candle lastCandle = series.entries!.last; final Candle lastVisibleCandle = series.visibleEntries.last; - CandlePainting lastCandlePainting; + OhlcPainting lastCandlePainting; if (lastCandle == lastVisibleCandle && series.prevLastEntry != null) { final IndexedEntry prevLastCandle = series.prevLastEntry!; @@ -86,7 +86,7 @@ class CandlePainter extends DataPainter> { animationInfo.currentTickPercent, )!; - lastCandlePainting = CandlePainting( + lastCandlePainting = OhlcPainting( xCenter: xCenter, yHigh: lastCandle.high > prevLastCandle.entry.high // In this case we don't update high-low line to avoid instant @@ -103,7 +103,7 @@ class CandlePainter extends DataPainter> { width: candleWidth, ); } else { - lastCandlePainting = CandlePainting( + lastCandlePainting = OhlcPainting( xCenter: epochToX( getEpochOf(lastVisibleCandle, series.visibleEntries.endIndex - 1)), yHigh: quoteToY(lastVisibleCandle.high), @@ -117,36 +117,36 @@ class CandlePainter extends DataPainter> { _paintCandle(canvas, lastCandlePainting); } - void _paintCandle(Canvas canvas, CandlePainting cp) { + void _paintCandle(Canvas canvas, OhlcPainting op) { canvas.drawLine( - Offset(cp.xCenter, cp.yHigh), - Offset(cp.xCenter, cp.yLow), + Offset(op.xCenter, op.yHigh), + Offset(op.xCenter, op.yLow), _linePaint, ); - if (cp.yOpen == cp.yClose) { + if (op.yOpen == op.yClose) { canvas.drawLine( - Offset(cp.xCenter - cp.width / 2, cp.yOpen), - Offset(cp.xCenter + cp.width / 2, cp.yOpen), + Offset(op.xCenter - op.width / 2, op.yOpen), + Offset(op.xCenter + op.width / 2, op.yOpen), _linePaint, ); - } else if (cp.yOpen > cp.yClose) { + } else if (op.yOpen > op.yClose) { canvas.drawRect( Rect.fromLTRB( - cp.xCenter - cp.width / 2, - cp.yClose, - cp.xCenter + cp.width / 2, - cp.yOpen, + op.xCenter - op.width / 2, + op.yClose, + op.xCenter + op.width / 2, + op.yOpen, ), _positiveCandlePaint, ); } else { canvas.drawRect( Rect.fromLTRB( - cp.xCenter - cp.width / 2, - cp.yOpen, - cp.xCenter + cp.width / 2, - cp.yClose, + op.xCenter - op.width / 2, + op.yOpen, + op.xCenter + op.width / 2, + op.yClose, ), _negativeCandlePaint, ); diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painting.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painting.dart deleted file mode 100644 index 97aeb3f1c..000000000 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painting.dart +++ /dev/null @@ -1,30 +0,0 @@ -/// The required painting properties of a candle. -class CandlePainting { - /// Initialzes the required painting properties of a candle. - const CandlePainting({ - required this.xCenter, - required this.yHigh, - required this.yLow, - required this.yOpen, - required this.yClose, - required this.width, - }); - - /// The center X position of the candle. - final double xCenter; - - /// Y position of the candle's high value. - final double yHigh; - - /// Y position of the candle's low value. - final double yLow; - - /// Y position of the candle's open value. - final double yOpen; - - /// Y position of the candle's close value. - final double yClose; - - /// The width of the candle. - final double width; -} diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart new file mode 100644 index 000000000..2a70ff70b --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart @@ -0,0 +1,217 @@ +import 'dart:ui' as ui; + +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/models/candle.dart'; +import 'package:deriv_chart/src/theme/painting_styles/candle_style.dart'; +import 'package:flutter/material.dart'; + +import '../../../chart_data.dart'; +import '../../data_painter.dart'; +import '../../data_series.dart'; +import '../../indexed_entry.dart'; +import '../ohlc_painting.dart'; + +// TODO(Bahar): Remove the shared code with candle_painter and move them to +//separate file. +/// A [DataPainter] for painting Hollow CandleStick data. +class HollowCandlePainter extends DataPainter> { + /// Initializes + HollowCandlePainter(DataSeries series) : super(series); + + late Color _positiveColor; + late Color _negativeColor; + late Color _neutralColor; + + @override + void onPaintData( + Canvas canvas, + Size size, + EpochToX epochToX, + QuoteToY quoteToY, + AnimationInfo animationInfo, + ) { + if (series.entries == null || series.visibleEntries.length < 2) { + return; + } + + final CandleStyle style = series.style as CandleStyle? ?? theme.candleStyle; + + _positiveColor = style.positiveColor; + _negativeColor = style.negativeColor; + _neutralColor = style.neutralColor; + + final double intervalWidth = + epochToX(chartConfig.granularity) - epochToX(0); + + final double candleWidth = intervalWidth * 0.6; + + // Painting visible candles except the last one that might be animated. + for (int i = series.visibleEntries.startIndex; + i < series.visibleEntries.endIndex - 1; + i++) { + final Candle candle = series.entries![i]; + final Candle prevCandle = + i != 0 ? series.entries![i - 1] : series.entries![0]; + + _paintCandle( + canvas, + OhlcPainting( + width: candleWidth, + xCenter: epochToX(getEpochOf(candle, i)), + yHigh: quoteToY(candle.high), + yLow: quoteToY(candle.low), + yOpen: quoteToY(candle.open), + yClose: quoteToY(candle.close), + ), + OhlcPainting( + width: candleWidth, + xCenter: epochToX(getEpochOf(prevCandle, i - 1)), + yHigh: quoteToY(prevCandle.high), + yLow: quoteToY(prevCandle.low), + yOpen: quoteToY(prevCandle.open), + yClose: quoteToY(prevCandle.close), + )); + } + + // Painting last visible candle + final Candle lastCandle = series.entries!.last; + final Candle lastVisibleCandle = series.visibleEntries.last; + final Candle prevLastCandle = series.entries![series.entries!.length - 2]; + + late OhlcPainting lastCandlePainting; + late OhlcPainting prevLastCandlePainting; + + if (lastCandle == lastVisibleCandle && series.prevLastEntry != null) { + final IndexedEntry prevLastCandle = series.prevLastEntry!; + + final double animatedYClose = quoteToY(ui.lerpDouble( + prevLastCandle.entry.close, + lastCandle.close, + animationInfo.currentTickPercent, + )!); + + final double xCenter = ui.lerpDouble( + epochToX(getEpochOf(prevLastCandle.entry, prevLastCandle.index)), + epochToX(getEpochOf(lastCandle, series.entries!.length - 1)), + animationInfo.currentTickPercent, + )!; + + lastCandlePainting = OhlcPainting( + xCenter: xCenter, + yHigh: lastCandle.high > prevLastCandle.entry.high + // In this case we don't update high-low line to avoid instant + // change of its height (ahead of animation). Candle close value + // animation will cover the line. + ? quoteToY(prevLastCandle.entry.high) + : quoteToY(lastCandle.high), + yLow: lastCandle.low < prevLastCandle.entry.low + // Same as comment above. + ? quoteToY(prevLastCandle.entry.low) + : quoteToY(lastCandle.low), + yOpen: quoteToY(lastCandle.open), + yClose: animatedYClose, + width: candleWidth, + ); + } else { + lastCandlePainting = OhlcPainting( + xCenter: epochToX( + getEpochOf(lastVisibleCandle, series.visibleEntries.endIndex - 1)), + yHigh: quoteToY(lastVisibleCandle.high), + yLow: quoteToY(lastVisibleCandle.low), + yOpen: quoteToY(lastVisibleCandle.open), + yClose: quoteToY(lastVisibleCandle.close), + width: candleWidth, + ); + } + + prevLastCandlePainting = OhlcPainting( + xCenter: epochToX( + getEpochOf(prevLastCandle, series.visibleEntries.endIndex - 1)), + yHigh: quoteToY(prevLastCandle.high), + yLow: quoteToY(prevLastCandle.low), + yOpen: quoteToY(prevLastCandle.open), + yClose: quoteToY(prevLastCandle.close), + width: candleWidth, + ); + + _paintCandle(canvas, lastCandlePainting, prevLastCandlePainting); + } + + void _drawWick(Canvas canvas, Color color, OhlcPainting currentPainting) { + canvas + ..drawLine( + Offset(currentPainting.xCenter, currentPainting.yHigh), + Offset(currentPainting.xCenter, currentPainting.yClose), + Paint() + ..color = color + ..strokeWidth = 1.2, + ) + ..drawLine( + Offset(currentPainting.xCenter, currentPainting.yLow), + Offset(currentPainting.xCenter, currentPainting.yOpen), + Paint() + ..color = color + ..strokeWidth = 1.2, + ); + } + + void _drawFilledRect( + Canvas canvas, Color color, OhlcPainting currentPainting) { + canvas.drawRect( + Rect.fromLTRB( + currentPainting.xCenter - currentPainting.width / 2, + currentPainting.yClose, + currentPainting.xCenter + currentPainting.width / 2, + currentPainting.yOpen, + ), + Paint()..color = color, + ); + } + + void _drawHollowRect( + Canvas canvas, Color color, OhlcPainting currentPainting) { + canvas.drawRect( + Rect.fromLTRB( + currentPainting.xCenter - currentPainting.width / 2, + currentPainting.yOpen, + currentPainting.xCenter + currentPainting.width / 2, + currentPainting.yClose, + ), + Paint() + ..color = color + ..strokeWidth = 1 + ..style = PaintingStyle.stroke, + ); + } + + void _drawLine(Canvas canvas, Color color, OhlcPainting currentPainting) { + canvas.drawLine( + Offset(currentPainting.xCenter - currentPainting.width / 2, + currentPainting.yOpen), + Offset(currentPainting.xCenter + currentPainting.width / 2, + currentPainting.yOpen), + Paint() + ..color = color + ..strokeWidth = 1.2, + ); + } + + void _paintCandle( + Canvas canvas, OhlcPainting currentPainting, OhlcPainting prevPainting) { + final Color _candleColor = currentPainting.yClose > prevPainting.yClose + ? _negativeColor + : currentPainting.yClose < prevPainting.yClose + ? _positiveColor + : _neutralColor; + + _drawWick(canvas, _candleColor, currentPainting); + + if (currentPainting.yOpen == currentPainting.yClose) { + _drawLine(canvas, _candleColor, currentPainting); + } else if (currentPainting.yOpen < currentPainting.yClose) { + _drawFilledRect(canvas, _candleColor, currentPainting); + } else { + _drawHollowRect(canvas, _candleColor, currentPainting); + } + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_series.dart new file mode 100644 index 000000000..4872aa561 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_series.dart @@ -0,0 +1,28 @@ +import 'package:deriv_chart/src/models/candle.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; +import 'package:deriv_chart/src/theme/painting_styles/candle_style.dart'; + +import '../../data_series.dart'; +import '../../series_painter.dart'; +import '../ohlc_type_series.dart'; +import 'hollow_candle_painter.dart'; + +/// Hollow CandleStick series +class HollowCandleSeries extends OHLCTypeSeries { + /// Initializes + HollowCandleSeries( + List entries, { + String? id, + CandleStyle? style, + HorizontalBarrierStyle? lastTickIndicatorStyle, + }) : super( + entries, + id ?? 'HollowCandleSeries', + style: style, + lastTickIndicatorStyle: lastTickIndicatorStyle, + ); + + @override + SeriesPainter> createPainter() => + HollowCandlePainter(this); +} diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painting.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painting.dart new file mode 100644 index 000000000..2abfa616e --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painting.dart @@ -0,0 +1,30 @@ +/// The required painting properties of a ohlc. +class OhlcPainting { + /// Initialzes the required painting properties of a ohlc. + const OhlcPainting({ + required this.xCenter, + required this.yHigh, + required this.yLow, + required this.yOpen, + required this.yClose, + required this.width, + }); + + /// The center X position of the ohlc. + final double xCenter; + + /// Y position of the ohlc's high value. + final double yHigh; + + /// Y position of the ohlc's low value. + final double yLow; + + /// Y position of the ohlc's open value. + final double yOpen; + + /// Y position of the ohlc's close value. + final double yClose; + + /// The width of the ohlc candle. + final double width; +} diff --git a/lib/src/models/chart_style.dart b/lib/src/models/chart_style.dart index f0ebf0d58..979723795 100644 --- a/lib/src/models/chart_style.dart +++ b/lib/src/models/chart_style.dart @@ -5,4 +5,7 @@ enum ChartStyle { /// Used to show the chart with candles. candles, + + /// Used to show the hollow candle chart. + hollow, } diff --git a/lib/src/theme/chart_default_theme.dart b/lib/src/theme/chart_default_theme.dart index 41a5d7893..19bfafcd2 100644 --- a/lib/src/theme/chart_default_theme.dart +++ b/lib/src/theme/chart_default_theme.dart @@ -95,7 +95,7 @@ abstract class ChartDefaultTheme implements ChartTheme { CandleStyle get candleStyle => CandleStyle( positiveColor: accentGreenColor, negativeColor: accentRedColor, - lineColor: base04Color, + neutralColor: base04Color, ); @override diff --git a/lib/src/theme/painting_styles/candle_style.dart b/lib/src/theme/painting_styles/candle_style.dart index 6fbd4e721..dbb34fa15 100644 --- a/lib/src/theme/painting_styles/candle_style.dart +++ b/lib/src/theme/painting_styles/candle_style.dart @@ -9,7 +9,7 @@ class CandleStyle extends DataSeriesStyle with EquatableMixin { const CandleStyle({ this.positiveColor = const Color(0xFF00A79E), this.negativeColor = const Color(0xFFCC2E3D), - this.lineColor = const Color(0xFF6E6E6E), + this.neutralColor = const Color(0xFF6E6E6E), }); /// Color of candles in which the price moved HIGHER during their period. @@ -18,13 +18,15 @@ class CandleStyle extends DataSeriesStyle with EquatableMixin { /// Color of candles in which the price moved LOWER during their period. final Color negativeColor; - /// The vertical line inside candle which represents high/low. - final Color lineColor; + /// Neutral color for candles in which the price remains same + /// or The vertical line inside candle which represents high/low. + final Color neutralColor; @override String toString() => - '${super.toString()}$positiveColor, $negativeColor, $lineColor'; + '${super.toString()}$positiveColor, $negativeColor, $neutralColor'; @override - List get props => [positiveColor, negativeColor, lineColor]; + List get props => + [positiveColor, negativeColor, neutralColor]; } From 8683d035d76f927ee5548f18ff2e8f979764a3b6 Mon Sep 17 00:00:00 2001 From: Bahar Date: Fri, 6 Jan 2023 11:09:04 +0800 Subject: [PATCH 05/29] bahar/82932/feat: add_ohlc_chart_type (#204) --- example/lib/main.dart | 49 +++--- lib/deriv_chart.dart | 1 + .../ohlc_series/candle/candle_painter.dart | 127 +++------------- .../hollow_candle/hollow_candle_painter.dart | 143 ++---------------- .../ohlc_candle/ohlc_candle_painter.dart | 74 +++++++++ .../ohlc_candle/ohlc_candle_series.dart | 27 ++++ .../ohlc_series/ohlc_painter.dart | 136 +++++++++++++++++ lib/src/models/chart_style.dart | 3 + 8 files changed, 310 insertions(+), 250 deletions(-) create mode 100644 lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_painter.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_series.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painter.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 8e45dc4b6..a2c7ec6b2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -369,6 +369,21 @@ class _FullscreenChartState extends State { }); } + DataSeries _getDataSeries(ChartStyle style) { + if (ticks is List) { + switch (style) { + case ChartStyle.candles: + return CandleSeries(ticks as List); + case ChartStyle.hollow: + return HollowCandleSeries(ticks as List); + case ChartStyle.ohlc: + return OhlcCandleSeries(ticks as List); + } + } + return LineSeries(ticks, style: const LineStyle(hasArea: true)) + as DataSeries; + } + @override Widget build(BuildContext context) => Material( color: const Color(0xFF0E0E0E), @@ -379,7 +394,6 @@ class _FullscreenChartState extends State { child: Row( children: [ Expanded( - // ignore: unnecessary_null_comparison child: _markets == null ? const SizedBox.shrink() : _buildMarketSelectorButton(), @@ -394,15 +408,7 @@ class _FullscreenChartState extends State { children: [ ClipRect( child: DerivChart( - mainSeries: style == ChartStyle.candles && - ticks is List - ? CandleSeries(ticks as List) - : style == ChartStyle.hollow && ticks is List - ? HollowCandleSeries(ticks as List) - : LineSeries( - ticks, - style: const LineStyle(hasArea: true), - ) as DataSeries, + mainSeries: _getDataSeries(style), markerSeries: MarkerSeries( _markers, activeMarker: _activeMarker, @@ -707,18 +713,27 @@ class _FullscreenChartState extends State { ? Icons.show_chart : style == ChartStyle.candles ? Icons.insert_chart - : Icons.insert_chart_outlined_outlined, + : style == ChartStyle.hollow + ? Icons.insert_chart_outlined_outlined + : Icons.add_chart, color: Colors.white, ), onPressed: () { Vibration.vibrate(duration: 50); setState(() { - if (style == ChartStyle.hollow) { - style = ChartStyle.line; - } else if (style == ChartStyle.line) { - style = ChartStyle.candles; - } else { - style = ChartStyle.hollow; + switch (style) { + case ChartStyle.ohlc: + style = ChartStyle.line; + return; + case ChartStyle.line: + style = ChartStyle.candles; + return; + case ChartStyle.candles: + style = ChartStyle.hollow; + return; + default: + style = ChartStyle.ohlc; + return; } }); }, diff --git a/lib/deriv_chart.dart b/lib/deriv_chart.dart index 32ad562a1..13fb8b497 100644 --- a/lib/deriv_chart.dart +++ b/lib/deriv_chart.dart @@ -19,6 +19,7 @@ export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ export 'src/deriv_chart/chart/data_visualization/chart_series/line_series/line_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/series.dart'; export 'src/deriv_chart/chart/data_visualization/markers/active_marker.dart'; export 'src/deriv_chart/chart/data_visualization/markers/marker.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart index b154f84ba..dd7e4375d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_painter.dart @@ -1,18 +1,14 @@ -import 'dart:ui' as ui; - -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/candle.dart'; import 'package:deriv_chart/src/theme/painting_styles/candle_style.dart'; import 'package:flutter/material.dart'; -import '../../../chart_data.dart'; import '../../data_painter.dart'; import '../../data_series.dart'; -import '../../indexed_entry.dart'; import '../ohlc_painting.dart'; +import '../ohlc_painter.dart'; /// A [DataPainter] for painting CandleStick data. -class CandlePainter extends DataPainter> { +class CandlePainter extends OhlcPainter { /// Initializes CandlePainter(DataSeries series) : super(series); @@ -21,17 +17,11 @@ class CandlePainter extends DataPainter> { late Paint _negativeCandlePaint; @override - void onPaintData( + void onPaintCandle( Canvas canvas, - Size size, - EpochToX epochToX, - QuoteToY quoteToY, - AnimationInfo animationInfo, + OhlcPainting currentPainting, + OhlcPainting prevPainting, ) { - if (series.entries == null || series.visibleEntries.length < 2) { - return; - } - final CandleStyle style = series.style as CandleStyle? ?? theme.candleStyle; _linePaint = Paint() @@ -41,112 +31,37 @@ class CandlePainter extends DataPainter> { _positiveCandlePaint = Paint()..color = style.positiveColor; _negativeCandlePaint = Paint()..color = style.negativeColor; - final double intervalWidth = - epochToX(chartConfig.granularity) - epochToX(0); - - final double candleWidth = intervalWidth * 0.6; - - // Painting visible candles except the last one that might be animated. - for (int i = series.visibleEntries.startIndex; - i < series.visibleEntries.endIndex - 1; - i++) { - final Candle candle = series.entries![i]; - - _paintCandle( - canvas, - OhlcPainting( - width: candleWidth, - xCenter: epochToX(getEpochOf(candle, i)), - yHigh: quoteToY(candle.high), - yLow: quoteToY(candle.low), - yOpen: quoteToY(candle.open), - yClose: quoteToY(candle.close), - ), - ); - } - - // Painting last visible candle - final Candle lastCandle = series.entries!.last; - final Candle lastVisibleCandle = series.visibleEntries.last; - - OhlcPainting lastCandlePainting; - - if (lastCandle == lastVisibleCandle && series.prevLastEntry != null) { - final IndexedEntry prevLastCandle = series.prevLastEntry!; - - final double animatedYClose = quoteToY(ui.lerpDouble( - prevLastCandle.entry.close, - lastCandle.close, - animationInfo.currentTickPercent, - )!); - - final double xCenter = ui.lerpDouble( - epochToX(getEpochOf(prevLastCandle.entry, prevLastCandle.index)), - epochToX(getEpochOf(lastCandle, series.entries!.length - 1)), - animationInfo.currentTickPercent, - )!; - - lastCandlePainting = OhlcPainting( - xCenter: xCenter, - yHigh: lastCandle.high > prevLastCandle.entry.high - // In this case we don't update high-low line to avoid instant - // change of its height (ahead of animation). Candle close value - // animation will cover the line. - ? quoteToY(prevLastCandle.entry.high) - : quoteToY(lastCandle.high), - yLow: lastCandle.low < prevLastCandle.entry.low - // Same as comment above. - ? quoteToY(prevLastCandle.entry.low) - : quoteToY(lastCandle.low), - yOpen: quoteToY(lastCandle.open), - yClose: animatedYClose, - width: candleWidth, - ); - } else { - lastCandlePainting = OhlcPainting( - xCenter: epochToX( - getEpochOf(lastVisibleCandle, series.visibleEntries.endIndex - 1)), - yHigh: quoteToY(lastVisibleCandle.high), - yLow: quoteToY(lastVisibleCandle.low), - yOpen: quoteToY(lastVisibleCandle.open), - yClose: quoteToY(lastVisibleCandle.close), - width: candleWidth, - ); - } - - _paintCandle(canvas, lastCandlePainting); - } - - void _paintCandle(Canvas canvas, OhlcPainting op) { canvas.drawLine( - Offset(op.xCenter, op.yHigh), - Offset(op.xCenter, op.yLow), + Offset(currentPainting.xCenter, currentPainting.yHigh), + Offset(currentPainting.xCenter, currentPainting.yLow), _linePaint, ); - if (op.yOpen == op.yClose) { + if (currentPainting.yOpen == currentPainting.yClose) { canvas.drawLine( - Offset(op.xCenter - op.width / 2, op.yOpen), - Offset(op.xCenter + op.width / 2, op.yOpen), + Offset(currentPainting.xCenter - currentPainting.width / 2, + currentPainting.yOpen), + Offset(currentPainting.xCenter + currentPainting.width / 2, + currentPainting.yOpen), _linePaint, ); - } else if (op.yOpen > op.yClose) { + } else if (currentPainting.yOpen > currentPainting.yClose) { canvas.drawRect( Rect.fromLTRB( - op.xCenter - op.width / 2, - op.yClose, - op.xCenter + op.width / 2, - op.yOpen, + currentPainting.xCenter - currentPainting.width / 2, + currentPainting.yClose, + currentPainting.xCenter + currentPainting.width / 2, + currentPainting.yOpen, ), _positiveCandlePaint, ); } else { canvas.drawRect( Rect.fromLTRB( - op.xCenter - op.width / 2, - op.yOpen, - op.xCenter + op.width / 2, - op.yClose, + currentPainting.xCenter - currentPainting.width / 2, + currentPainting.yOpen, + currentPainting.xCenter + currentPainting.width / 2, + currentPainting.yClose, ), _negativeCandlePaint, ); diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart index 2a70ff70b..8f84ceb3d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_painter.dart @@ -1,20 +1,14 @@ -import 'dart:ui' as ui; - -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/candle.dart'; import 'package:deriv_chart/src/theme/painting_styles/candle_style.dart'; import 'package:flutter/material.dart'; -import '../../../chart_data.dart'; import '../../data_painter.dart'; import '../../data_series.dart'; -import '../../indexed_entry.dart'; import '../ohlc_painting.dart'; +import '../ohlc_painter.dart'; -// TODO(Bahar): Remove the shared code with candle_painter and move them to -//separate file. /// A [DataPainter] for painting Hollow CandleStick data. -class HollowCandlePainter extends DataPainter> { +class HollowCandlePainter extends OhlcPainter { /// Initializes HollowCandlePainter(DataSeries series) : super(series); @@ -23,118 +17,32 @@ class HollowCandlePainter extends DataPainter> { late Color _neutralColor; @override - void onPaintData( + void onPaintCandle( Canvas canvas, - Size size, - EpochToX epochToX, - QuoteToY quoteToY, - AnimationInfo animationInfo, + OhlcPainting currentPainting, + OhlcPainting prevPainting, ) { - if (series.entries == null || series.visibleEntries.length < 2) { - return; - } - final CandleStyle style = series.style as CandleStyle? ?? theme.candleStyle; _positiveColor = style.positiveColor; _negativeColor = style.negativeColor; _neutralColor = style.neutralColor; - final double intervalWidth = - epochToX(chartConfig.granularity) - epochToX(0); - - final double candleWidth = intervalWidth * 0.6; - - // Painting visible candles except the last one that might be animated. - for (int i = series.visibleEntries.startIndex; - i < series.visibleEntries.endIndex - 1; - i++) { - final Candle candle = series.entries![i]; - final Candle prevCandle = - i != 0 ? series.entries![i - 1] : series.entries![0]; - - _paintCandle( - canvas, - OhlcPainting( - width: candleWidth, - xCenter: epochToX(getEpochOf(candle, i)), - yHigh: quoteToY(candle.high), - yLow: quoteToY(candle.low), - yOpen: quoteToY(candle.open), - yClose: quoteToY(candle.close), - ), - OhlcPainting( - width: candleWidth, - xCenter: epochToX(getEpochOf(prevCandle, i - 1)), - yHigh: quoteToY(prevCandle.high), - yLow: quoteToY(prevCandle.low), - yOpen: quoteToY(prevCandle.open), - yClose: quoteToY(prevCandle.close), - )); - } - - // Painting last visible candle - final Candle lastCandle = series.entries!.last; - final Candle lastVisibleCandle = series.visibleEntries.last; - final Candle prevLastCandle = series.entries![series.entries!.length - 2]; - - late OhlcPainting lastCandlePainting; - late OhlcPainting prevLastCandlePainting; - - if (lastCandle == lastVisibleCandle && series.prevLastEntry != null) { - final IndexedEntry prevLastCandle = series.prevLastEntry!; - - final double animatedYClose = quoteToY(ui.lerpDouble( - prevLastCandle.entry.close, - lastCandle.close, - animationInfo.currentTickPercent, - )!); + final Color _candleColor = currentPainting.yClose > prevPainting.yClose + ? _negativeColor + : currentPainting.yClose < prevPainting.yClose + ? _positiveColor + : _neutralColor; - final double xCenter = ui.lerpDouble( - epochToX(getEpochOf(prevLastCandle.entry, prevLastCandle.index)), - epochToX(getEpochOf(lastCandle, series.entries!.length - 1)), - animationInfo.currentTickPercent, - )!; + _drawWick(canvas, _candleColor, currentPainting); - lastCandlePainting = OhlcPainting( - xCenter: xCenter, - yHigh: lastCandle.high > prevLastCandle.entry.high - // In this case we don't update high-low line to avoid instant - // change of its height (ahead of animation). Candle close value - // animation will cover the line. - ? quoteToY(prevLastCandle.entry.high) - : quoteToY(lastCandle.high), - yLow: lastCandle.low < prevLastCandle.entry.low - // Same as comment above. - ? quoteToY(prevLastCandle.entry.low) - : quoteToY(lastCandle.low), - yOpen: quoteToY(lastCandle.open), - yClose: animatedYClose, - width: candleWidth, - ); + if (currentPainting.yOpen == currentPainting.yClose) { + _drawLine(canvas, _candleColor, currentPainting); + } else if (currentPainting.yOpen < currentPainting.yClose) { + _drawFilledRect(canvas, _candleColor, currentPainting); } else { - lastCandlePainting = OhlcPainting( - xCenter: epochToX( - getEpochOf(lastVisibleCandle, series.visibleEntries.endIndex - 1)), - yHigh: quoteToY(lastVisibleCandle.high), - yLow: quoteToY(lastVisibleCandle.low), - yOpen: quoteToY(lastVisibleCandle.open), - yClose: quoteToY(lastVisibleCandle.close), - width: candleWidth, - ); + _drawHollowRect(canvas, _candleColor, currentPainting); } - - prevLastCandlePainting = OhlcPainting( - xCenter: epochToX( - getEpochOf(prevLastCandle, series.visibleEntries.endIndex - 1)), - yHigh: quoteToY(prevLastCandle.high), - yLow: quoteToY(prevLastCandle.low), - yOpen: quoteToY(prevLastCandle.open), - yClose: quoteToY(prevLastCandle.close), - width: candleWidth, - ); - - _paintCandle(canvas, lastCandlePainting, prevLastCandlePainting); } void _drawWick(Canvas canvas, Color color, OhlcPainting currentPainting) { @@ -195,23 +103,4 @@ class HollowCandlePainter extends DataPainter> { ..strokeWidth = 1.2, ); } - - void _paintCandle( - Canvas canvas, OhlcPainting currentPainting, OhlcPainting prevPainting) { - final Color _candleColor = currentPainting.yClose > prevPainting.yClose - ? _negativeColor - : currentPainting.yClose < prevPainting.yClose - ? _positiveColor - : _neutralColor; - - _drawWick(canvas, _candleColor, currentPainting); - - if (currentPainting.yOpen == currentPainting.yClose) { - _drawLine(canvas, _candleColor, currentPainting); - } else if (currentPainting.yOpen < currentPainting.yClose) { - _drawFilledRect(canvas, _candleColor, currentPainting); - } else { - _drawHollowRect(canvas, _candleColor, currentPainting); - } - } } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_painter.dart new file mode 100644 index 000000000..e85181493 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_painter.dart @@ -0,0 +1,74 @@ +import 'package:deriv_chart/src/models/candle.dart'; +import 'package:deriv_chart/src/theme/painting_styles/candle_style.dart'; +import 'package:flutter/material.dart'; + +import '../../data_painter.dart'; +import '../../data_series.dart'; +import '../ohlc_painting.dart'; +import '../ohlc_painter.dart'; + +/// A [DataPainter] for painting Ohlc CandleStick data. +class OhlcCandlePainter extends OhlcPainter { + /// Initializes + OhlcCandlePainter(DataSeries series) : super(series); + + late Color _positiveColor; + late Color _negativeColor; + late Color _neutralColor; + + @override + void onPaintCandle( + Canvas canvas, + OhlcPainting currentPainting, + OhlcPainting prevPainting, + ) { + final CandleStyle style = series.style as CandleStyle? ?? theme.candleStyle; + + _positiveColor = style.positiveColor; + _negativeColor = style.negativeColor; + _neutralColor = style.neutralColor; + + final Color _candleColor = currentPainting.yClose > prevPainting.yClose + ? _negativeColor + : currentPainting.yClose < prevPainting.yClose + ? _positiveColor + : _neutralColor; + + _drawWick(canvas, _candleColor, currentPainting); + _drawOpenCloseLines(canvas, _candleColor, currentPainting); + } + + void _drawWick(Canvas canvas, Color color, OhlcPainting currentPainting) { + canvas.drawLine( + Offset(currentPainting.xCenter, currentPainting.yHigh), + Offset(currentPainting.xCenter, currentPainting.yLow), + Paint() + ..color = color + ..strokeWidth = 1.2, + ); + } + + void _drawOpenCloseLines( + Canvas canvas, Color color, OhlcPainting currentPainting) { + // Paint openning + canvas + ..drawLine( + Offset(currentPainting.xCenter - currentPainting.width / 2, + currentPainting.yOpen), + Offset(currentPainting.xCenter, currentPainting.yOpen), + Paint() + ..color = color + ..strokeWidth = 1.2, + ) + + // Paint closing + ..drawLine( + Offset(currentPainting.xCenter + currentPainting.width / 2, + currentPainting.yClose), + Offset(currentPainting.xCenter, currentPainting.yClose), + Paint() + ..color = color + ..strokeWidth = 1.2, + ); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_series.dart new file mode 100644 index 000000000..333b87a56 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_series.dart @@ -0,0 +1,27 @@ +import 'package:deriv_chart/src/models/candle.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; +import 'package:deriv_chart/src/theme/painting_styles/candle_style.dart'; + +import '../../data_series.dart'; +import '../../series_painter.dart'; +import '../ohlc_type_series.dart'; +import 'ohlc_candle_painter.dart'; + +/// Ohlc CandleStick series +class OhlcCandleSeries extends OHLCTypeSeries { + /// Initializes + OhlcCandleSeries( + List entries, { + String? id, + CandleStyle? style, + HorizontalBarrierStyle? lastTickIndicatorStyle, + }) : super( + entries, + id ?? 'OhlcCandleSeries', + style: style, + lastTickIndicatorStyle: lastTickIndicatorStyle, + ); + + @override + SeriesPainter> createPainter() => OhlcCandlePainter(this); +} diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painter.dart new file mode 100644 index 000000000..f26b5f966 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_painter.dart @@ -0,0 +1,136 @@ +import 'dart:ui' as ui; + +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/models/candle.dart'; +import 'package:flutter/material.dart'; + +import '../../chart_data.dart'; +import '../data_painter.dart'; +import '../data_series.dart'; +import '../indexed_entry.dart'; +import './ohlc_painting.dart'; + +/// A [DataPainter] for painting CandleStick data which handles the logic for +/// calculation of candle elements for all OHLC type charts +abstract class OhlcPainter extends DataPainter> { + /// Initializes + OhlcPainter(DataSeries series) : super(series); + + @override + void onPaintData( + Canvas canvas, + Size size, + EpochToX epochToX, + QuoteToY quoteToY, + AnimationInfo animationInfo, + ) { + if (series.entries == null || series.visibleEntries.length < 2) { + return; + } + + final double intervalWidth = + epochToX(chartConfig.granularity) - epochToX(0); + + final double candleWidth = intervalWidth * 0.6; + + // Painting visible candles except the last one that might be animated. + for (int i = series.visibleEntries.startIndex; + i < series.visibleEntries.endIndex - 1; + i++) { + final Candle candle = series.entries![i]; + final Candle prevCandle = + i != 0 ? series.entries![i - 1] : series.entries![0]; + + onPaintCandle( + canvas, + OhlcPainting( + width: candleWidth, + xCenter: epochToX(getEpochOf(candle, i)), + yHigh: quoteToY(candle.high), + yLow: quoteToY(candle.low), + yOpen: quoteToY(candle.open), + yClose: quoteToY(candle.close), + ), + OhlcPainting( + width: candleWidth, + xCenter: epochToX(getEpochOf(prevCandle, i - 1)), + yHigh: quoteToY(prevCandle.high), + yLow: quoteToY(prevCandle.low), + yOpen: quoteToY(prevCandle.open), + yClose: quoteToY(prevCandle.close), + )); + } + + // Painting last visible candle + final Candle lastCandle = series.entries!.last; + final Candle lastVisibleCandle = series.visibleEntries.last; + final Candle prevLastCandle = series.entries![series.entries!.length - 2]; + + late OhlcPainting lastCandlePainting; + late OhlcPainting prevLastCandlePainting; + + if (lastCandle == lastVisibleCandle && series.prevLastEntry != null) { + final IndexedEntry prevLastCandle = series.prevLastEntry!; + + final double animatedYClose = quoteToY(ui.lerpDouble( + prevLastCandle.entry.close, + lastCandle.close, + animationInfo.currentTickPercent, + )!); + + final double xCenter = ui.lerpDouble( + epochToX(getEpochOf(prevLastCandle.entry, prevLastCandle.index)), + epochToX(getEpochOf(lastCandle, series.entries!.length - 1)), + animationInfo.currentTickPercent, + )!; + + lastCandlePainting = OhlcPainting( + xCenter: xCenter, + yHigh: lastCandle.high > prevLastCandle.entry.high + // In this case we don't update high-low line to avoid instant + // change of its height (ahead of animation). Candle close value + // animation will cover the line. + ? quoteToY(prevLastCandle.entry.high) + : quoteToY(lastCandle.high), + yLow: lastCandle.low < prevLastCandle.entry.low + // Same as comment above. + ? quoteToY(prevLastCandle.entry.low) + : quoteToY(lastCandle.low), + yOpen: quoteToY(lastCandle.open), + yClose: animatedYClose, + width: candleWidth, + ); + } else { + lastCandlePainting = OhlcPainting( + xCenter: epochToX( + getEpochOf(lastVisibleCandle, series.visibleEntries.endIndex - 1)), + yHigh: quoteToY(lastVisibleCandle.high), + yLow: quoteToY(lastVisibleCandle.low), + yOpen: quoteToY(lastVisibleCandle.open), + yClose: quoteToY(lastVisibleCandle.close), + width: candleWidth, + ); + } + + prevLastCandlePainting = OhlcPainting( + xCenter: epochToX( + getEpochOf(prevLastCandle, series.visibleEntries.endIndex - 1)), + yHigh: quoteToY(prevLastCandle.high), + yLow: quoteToY(prevLastCandle.low), + yOpen: quoteToY(prevLastCandle.open), + yClose: quoteToY(prevLastCandle.close), + width: candleWidth, + ); + + onPaintCandle(canvas, lastCandlePainting, prevLastCandlePainting); + } + + /// Paints [DataSeries.visibleEntries]. + /// This method is for handling diffrent ways of painting candles for each + /// chart type + void onPaintCandle( + Canvas canvas, + OhlcPainting currentPainting, + OhlcPainting prevPainting, + ); +} diff --git a/lib/src/models/chart_style.dart b/lib/src/models/chart_style.dart index 979723795..e6269b6aa 100644 --- a/lib/src/models/chart_style.dart +++ b/lib/src/models/chart_style.dart @@ -8,4 +8,7 @@ enum ChartStyle { /// Used to show the hollow candle chart. hollow, + + /// Used to show the ohlc chart. + ohlc, } From 2a3b8dce40a88bc8f72c307eae4fcf887534a59a Mon Sep 17 00:00:00 2001 From: Bahar Date: Tue, 21 Feb 2023 16:04:35 +0800 Subject: [PATCH 06/29] maryia|bahar/78755/Add vertical and line drawing tool (#207) --- docs/how_chart_lib_works.md | 4 +- lib/generated/intl/messages_en.dart | 3 + lib/generated/l10n.dart | 20 +++ lib/l10n/intl_en.arb | 4 +- lib/src/add_ons/add_ons_repository.dart | 56 +++++--- .../add_ons/drawing_tools_ui/callbacks.dart | 4 + .../drawing_tools_ui/drawing_tool_config.dart | 31 ++++- .../drawing_tools_ui/drawing_tool_item.dart | 78 ++++++++++++ .../drawing_tools_dialog.dart | 95 +++++++------- .../line/line_drawing_tool_config.dart | 48 +++++++ .../line/line_drawing_tool_config.g.dart | 22 ++++ .../line/line_drawing_tool_item.dart | 73 +++++++++++ .../vertical_drawing_tool_config.dart | 48 +++++++ .../vertical_drawing_tool_config.g.dart | 22 ++++ .../vertical/vertical_drawing_tool_item.dart | 76 +++++++++++ lib/src/deriv_chart/chart/basic_chart.dart | 10 ++ lib/src/deriv_chart/chart/chart.dart | 20 ++- .../drawing_tools/data_model/vector.dart | 22 ++++ .../drawing_tools/drawing.dart | 38 ++++++ .../drawing_tools/drawing_creator.dart | 48 +++++++ .../drawing_tools/drawing_data.dart | 29 +++++ .../drawing_tools/drawing_painter.dart | 75 +++++++++++ .../drawing_tools/line/line_drawing.dart | 94 ++++++++++++++ .../line/line_drawing_creator.dart | 120 ++++++++++++++++++ .../vertical/vertical_drawing.dart | 54 ++++++++ .../vertical/vertical_drawing_creator.dart | 94 ++++++++++++++ .../chart/helpers/functions/conversion.dart | 25 ++++ lib/src/deriv_chart/chart/main_chart.dart | 31 ++++- lib/src/deriv_chart/deriv_chart.dart | 68 +++++++++- ...abstract_single_indicator_series_test.dart | 2 - 30 files changed, 1239 insertions(+), 75 deletions(-) create mode 100644 lib/src/add_ons/drawing_tools_ui/callbacks.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart diff --git a/docs/how_chart_lib_works.md b/docs/how_chart_lib_works.md index 70fc57090..12522b49b 100644 --- a/docs/how_chart_lib_works.md +++ b/docs/how_chart_lib_works.md @@ -178,9 +178,9 @@ Sometimes we need to show two charts on the screen, for example for showing bott **MainChart** and **BottomChart**s use the same **XAxis** (and it's provided in the root of the `Chart` widget to be accessible on the widgets at the bottom) but they have different YAxis. # DerivChart -**DerivChart** A wrapper around the **chart** widget which provides the UI to add/remove indicators and to manage saving/restoring selected ones on storage. +**DerivChart** A wrapper around the **chart** widget which provides the UI to add/remove addons (indicators and drawing tools) and to manage saving/restoring selected ones on storage. -*if you want to have indicators in the chart, you should use ***DerivChart** instead of **Chart**** +*if you want to have indicators and drawing tools in the chart, you should use ***DerivChart** instead of **Chart**** ![plot](deriv-chart.png) diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index f18e4d26c..2609cab09 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -30,6 +30,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Base Line Period"), "labelChannelFill": MessageLookupByLibrary.simpleMessage("Channel Fill"), + "labelColor": MessageLookupByLibrary.simpleMessage("Color"), "labelConversionLinePeriod": MessageLookupByLibrary.simpleMessage("Conversion Line Period"), "labelDistance": MessageLookupByLibrary.simpleMessage("Distance"), @@ -81,6 +82,8 @@ class MessageLookup extends MessageLookupByLibrary { "labelTeethPeriod": MessageLookupByLibrary.simpleMessage("Teeth Period"), "labelType": MessageLookupByLibrary.simpleMessage("Type"), + "selectDrawingTool": + MessageLookupByLibrary.simpleMessage("Select drawing tool"), "warnCheckAssetSearchingText": MessageLookupByLibrary.simpleMessage( "Try checking your spelling or use a different term"), "warnFailedLoadingDrawingTools": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index d6adf23d0..d454a7516 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -479,6 +479,26 @@ class ChartLocalization { args: [], ); } + + /// `Select drawing tool` + String get selectDrawingTool { + return Intl.message( + 'Select drawing tool', + name: 'selectDrawingTool', + desc: '', + args: [], + ); + } + + /// `Color` + String get labelColor { + return Intl.message( + 'Color', + name: 'labelColor', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index e7aae92ff..366202600 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -42,5 +42,7 @@ "labelShowFractals": "Show Fractals", "labelIsSmooth": "Is Smooth", "warnFailedLoadingIndicators": "Failed to load indicators.", - "warnFailedLoadingDrawingTools": "Failed to load drawing tools." + "warnFailedLoadingDrawingTools": "Failed to load drawing tools.", + "selectDrawingTool": "Select drawing tool", + "labelColor": "Color" } diff --git a/lib/src/add_ons/add_ons_repository.dart b/lib/src/add_ons/add_ons_repository.dart index 3f291295f..d767f8683 100644 --- a/lib/src/add_ons/add_ons_repository.dart +++ b/lib/src/add_ons/add_ons_repository.dart @@ -1,23 +1,28 @@ import 'dart:convert'; - +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; -/// Storage key of saved indicators. +/// Storage key of saved indicators/drawing tools. const String addOnsKey = 'addOns'; /// Holds indicators/drawing tools that were added to the Chart during runtime. -class AddOnsRepository extends ChangeNotifier { +class AddOnsRepository extends ChangeNotifier { /// Initializes - AddOnsRepository(this._addOnConfig) : _addOns = []; + AddOnsRepository(this._addOnConfig) : _addOns = []; final dynamic _addOnConfig; - final List _addOns; + /// List containing addOns + final List _addOns; SharedPreferences? _prefs; - /// List of indicators or drawing tools. - List get addOns => _addOns; + /// List of indicators. + List get addOns => getAddOns(); + + /// Getter for the list of addOns indicators or drawing tools. + List getAddOns() => _addOns; /// Loads user selected indicators or drawing tools from shared preferences. void loadFromPrefs(SharedPreferences prefs) { @@ -29,39 +34,50 @@ class AddOnsRepository extends ChangeNotifier { } final List encodedAddOns = prefs.getStringList(addOnsKey)!; - _addOns.clear(); + getAddOns().clear(); for (final String encodedAddOn in encodedAddOns) { - final T addOnConfig = _addOnConfig.fromJson(jsonDecode(encodedAddOn)); - _addOns.add(addOnConfig); + dynamic addOnConfig; + if (_addOnConfig is IndicatorConfig) { + addOnConfig = + IndicatorConfig.fromJson(jsonDecode(encodedAddOn)) as AddOnConfig; + } else if (_addOnConfig is DrawingToolConfig) { + addOnConfig = + DrawingToolConfig.fromJson(jsonDecode(encodedAddOn)) as AddOnConfig; + } + if (addOnConfig == null) { + continue; + } else { + getAddOns().add(addOnConfig); + } } notifyListeners(); } /// Adds a new indicator or drawing tool and updates storage. - void add(T addOnConfig) { - _addOns.add(addOnConfig); + void add(AddOnConfig addOnConfig) { + getAddOns().add(addOnConfig); _writeToPrefs(); notifyListeners(); } /// Updates indicator or drawing tool at [index] and updates storage. - void updateAt(int index, T addOnConfig) { - if (index < 0 || index >= _addOns.length) { + void updateAt(int index, AddOnConfig addOnConfig) { + if (index < 0 || index >= getAddOns().length) { return; } - _addOns[index] = addOnConfig; + getAddOns()[index] = addOnConfig; _writeToPrefs(); notifyListeners(); } - /// Removes indicator/drawing tool at [index] from repository and updates storage. + /// Removes indicator/drawing tool at [index] from repository and + /// updates storage. void removeAt(int index) { - if (index < 0 || index >= _addOns.length) { + if (index < 0 || index >= getAddOns().length) { return; } - _addOns.removeAt(index); - _writeToPrefs(); + getAddOns().removeAt(index); notifyListeners(); } @@ -69,7 +85,7 @@ class AddOnsRepository extends ChangeNotifier { if (_prefs != null) { await _prefs!.setStringList( addOnsKey, - _addOns.map((T config) => jsonEncode(config)).toList(), + getAddOns().map((AddOnConfig config) => jsonEncode(config)).toList(), ); } } diff --git a/lib/src/add_ons/drawing_tools_ui/callbacks.dart b/lib/src/add_ons/drawing_tools_ui/callbacks.dart new file mode 100644 index 000000000..80cc48834 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/callbacks.dart @@ -0,0 +1,4 @@ +import 'drawing_tool_config.dart'; + +/// Callback to update drawing tool with new [drawingToolConfig]. +typedef UpdateDrawingTool = Function(DrawingToolConfig drawingToolConfig); diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index e1d1160d7..b902a3933 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -1,5 +1,9 @@ import 'package:deriv_chart/src/add_ons/add_on_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:flutter/material.dart'; +import 'line/line_drawing_tool_config.dart'; +import 'vertical/vertical_drawing_tool_config.dart'; /// Drawing tools config @immutable @@ -8,5 +12,30 @@ abstract class DrawingToolConfig extends AddOnConfig { const DrawingToolConfig({bool isOverlay = true}) : super(isOverlay: isOverlay); -// TODO(maryia-binary): Add config for drawing tools UI + /// Creates a concrete drawing tool config from JSON. + factory DrawingToolConfig.fromJson(Map json) { + if (!json.containsKey(nameKey)) { + throw ArgumentError.value(json, 'json', 'Missing drawing tool name.'); + } + + switch (json[nameKey]) { + case LineDrawingToolConfig.name: + return LineDrawingToolConfig.fromJson(json); + case VerticalDrawingToolConfig.name: + return VerticalDrawingToolConfig.fromJson(json); + // Add new drawing tools here. + default: + throw ArgumentError.value( + json, 'json', 'Unidentified drawing tool name.'); + } + } + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'name'; + + /// Creates drawing tool. + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart new file mode 100644 index 000000000..975593e98 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; +import 'callbacks.dart'; +import 'drawing_tool_config.dart'; + +/// Represents a drawing tool item on the list of drawing tools in dialog. +abstract class DrawingToolItem extends StatefulWidget { + /// Initializes + const DrawingToolItem({ + required this.title, + required this.config, + required this.updateDrawingTool, + required this.deleteDrawingTool, + Key? key, + }) : super(key: key); + + /// Title + final String title; + + /// Contains drawing tool configuration. + final DrawingToolConfig config; + + /// Called when config values were updated. + final UpdateDrawingTool updateDrawingTool; + + /// Called when user removed drawing tool. + final VoidCallback deleteDrawingTool; + + @override + DrawingToolItemState createState() => + createIndicatorItemState(); + + /// Create state object for this widget + @protected + DrawingToolItemState createIndicatorItemState(); +} + +/// State class of [DrawingToolItem] +abstract class DrawingToolItemState + extends State { + /// Drawing tools repository + @protected + late AddOnsRepository drawingToolsRepo; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + drawingToolsRepo = + Provider.of>(context); + } + + @override + Widget build(BuildContext context) => ListTile( + contentPadding: const EdgeInsets.all(0), + leading: Text(widget.title, style: const TextStyle(fontSize: 16)), + title: getDrawingToolOptions(), + trailing: IconButton( + icon: const Icon(Icons.delete), + onPressed: removeDrawingTool, + ), + ); + + /// Updates drawing tool based on its current config values. + void updateDrawingTool() => + widget.updateDrawingTool.call(createDrawingToolConfig()); + + /// Removes this drawing tool. + void removeDrawingTool() => widget.deleteDrawingTool.call(); + + /// Returns the [DrawingToolConfig] which can be used to create the Series for + /// this drawing tool. + T createDrawingToolConfig(); + + /// Creates the menu options widget for this drawing tool. + Widget getDrawingToolOptions(); +} diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index 3f1eeb0d7..5f0a18d33 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -1,3 +1,6 @@ +import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/widgets/animated_popup.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -6,12 +9,30 @@ import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; /// Drawing tools dialog with available drawing tools. class DrawingToolsDialog extends StatefulWidget { + /// Creates drawing tools dialog. + const DrawingToolsDialog({ + required this.onDrawingToolSelection(DrawingToolConfig selectedDrawingTool), + required this.onDrawingToolRemoval(int index), + required this.onDrawingToolUpdate( + int index, DrawingToolConfig updatedConfig), + Key? key, + }) : super(key: key); + + /// Callback to inform parent about drawing tool removal. + final void Function(int) onDrawingToolRemoval; + + /// Callback to inform parent about drawing tool selection. + final void Function(DrawingToolConfig) onDrawingToolSelection; + + /// Callback to inform parent about drawing tool update. + final void Function(int, DrawingToolConfig) onDrawingToolUpdate; + @override _DrawingToolsDialogState createState() => _DrawingToolsDialogState(); } class _DrawingToolsDialogState extends State { - String? _selectedDrawingTool; + DrawingToolConfig? _selectedDrawingTool; @override Widget build(BuildContext context) { @@ -24,49 +45,21 @@ class _DrawingToolsDialogState extends State { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - DropdownButton( + DropdownButton( value: _selectedDrawingTool, - hint: const Text('Select drawing tool'), - items: const >[ - DropdownMenuItem( - child: Text('Channel'), - value: 'Channel', - ), - DropdownMenuItem( - child: Text('Continuous'), - value: 'Continuous', - ), - DropdownMenuItem( - child: Text('Fib Fan'), - value: 'Fib Fan', - ), - DropdownMenuItem( - child: Text('Horizontal'), - value: 'Horizontal', - ), - DropdownMenuItem( + hint: Text(ChartLocalization.of(context)!.selectDrawingTool), + items: const >[ + DropdownMenuItem( child: Text('Line'), - value: 'Line', + value: LineDrawingToolConfig(), ), - DropdownMenuItem( - child: Text('Ray'), - value: 'Ray', - ), - DropdownMenuItem( - child: Text('Rectangle'), - value: 'Rectangle', - ), - DropdownMenuItem( - child: Text('Trend'), - value: 'Trend', - ), - DropdownMenuItem( + DropdownMenuItem( child: Text('Vertical'), - value: 'Vertical', + value: VerticalDrawingToolConfig(), ), - // TODO(maryia-binary): add real drawing tools above + // TODO(maryia-binary): add the rest of drawing tools above ], - onChanged: (String? config) { + onChanged: (dynamic config) { setState(() { _selectedDrawingTool = config; }); @@ -74,16 +67,32 @@ class _DrawingToolsDialogState extends State { ), const SizedBox(width: 16), ElevatedButton( - child: const Text('Add'), - onPressed: - _selectedDrawingTool is DrawingToolConfig ? () {} : null), + child: const Text('Add'), + onPressed: _selectedDrawingTool != null && + _selectedDrawingTool is DrawingToolConfig + ? () { + widget.onDrawingToolSelection(_selectedDrawingTool!); + Navigator.of(context).pop(); + } + : null, + ), ], ), Expanded( child: ListView.builder( shrinkWrap: true, - itemCount: repo.addOns.length, - itemBuilder: (BuildContext context, int index) => Container(), + itemCount: repo.getAddOns().length, + itemBuilder: (BuildContext context, int index) => + repo.getAddOns()[index].getItem( + (DrawingToolConfig updatedConfig) { + widget.onDrawingToolUpdate(index, updatedConfig); + repo.updateAt(index, updatedConfig); + }, + () { + widget.onDrawingToolRemoval(index); + repo.removeAt(index); + }, + ), ), ), ], diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart new file mode 100644 index 000000000..cc634e709 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart @@ -0,0 +1,48 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'line_drawing_tool_item.dart'; + +part 'line_drawing_tool_config.g.dart'; + +/// Line drawing tool config +@JsonSerializable() +class LineDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const LineDrawingToolConfig({ + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = 'solid', + }) : super(); + + /// Initializes from JSON. + factory LineDrawingToolConfig.fromJson(Map json) => + _$LineDrawingToolConfigFromJson(json); + + /// Drawing tool name + static const String name = 'dt_line'; + + @override + Map toJson() => _$LineDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + // TODO(maryia-binary): implement 'dotted' and 'dashed' patterns + final String pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + LineDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart new file mode 100644 index 000000000..db9161488 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'line_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LineDrawingToolConfig _$LineDrawingToolConfigFromJson( + Map json) { + return LineDrawingToolConfig( + lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), + pattern: json['pattern'] as String, + ); +} + +Map _$LineDrawingToolConfigToJson( + LineDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'pattern': instance.pattern, + }; diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart new file mode 100644 index 000000000..75d4d8dfa --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart @@ -0,0 +1,73 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; +import 'line_drawing_tool_config.dart'; +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; + +/// Line drawing tool item in the list of drawing tools +class LineDrawingToolItem extends DrawingToolItem { + /// Initializes + const LineDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + LineDrawingToolConfig config = const LineDrawingToolConfig(), + }) : super( + key: key, + title: 'Line', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createIndicatorItemState() => + LineDrawingToolItemState(); +} + +/// LineDrawingToolItem State class +class LineDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _lineStyle; + String? _pattern; + + @override + LineDrawingToolConfig createDrawingToolConfig() => LineDrawingToolConfig( + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField(), + // TODO(maryia-binary): implement _buildPatternField() to set pattern + ], + ); + + Widget _buildColorField() => Row( + children: [ + Text( + ChartLocalization.of(context)!.labelColor, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: _currentLineStyle.color, + onColorChanged: (Color selectedColor) { + setState(() { + _lineStyle = _currentLineStyle.copyWith(color: selectedColor); + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as LineDrawingToolConfig).lineStyle; + + String get _currentPattern => + _pattern ?? (widget.config as LineDrawingToolConfig).pattern; +} diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart new file mode 100644 index 000000000..3087fb65c --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart @@ -0,0 +1,48 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../callbacks.dart'; + +part 'vertical_drawing_tool_config.g.dart'; + +/// Vertical drawing tool configurations. +@JsonSerializable() +class VerticalDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const VerticalDrawingToolConfig({ + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = 'solid', + }) : super(); + + /// Initializes from JSON. + factory VerticalDrawingToolConfig.fromJson(Map json) => + _$VerticalDrawingToolConfigFromJson(json); + + /// Unique name for this drawing tool. + static const String name = 'dt_vertical'; + + @override + Map toJson() => _$VerticalDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + final String pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + VerticalDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart new file mode 100644 index 000000000..b711df85e --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'vertical_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +VerticalDrawingToolConfig _$VerticalDrawingToolConfigFromJson( + Map json) { + return VerticalDrawingToolConfig( + lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), + pattern: json['pattern'] as String, + ); +} + +Map _$VerticalDrawingToolConfigToJson( + VerticalDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'pattern': instance.pattern, + }; diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart new file mode 100644 index 000000000..40f1c5497 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart @@ -0,0 +1,76 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; + +import 'package:flutter/material.dart'; + +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; +import '../drawing_tool_item.dart'; + +/// Vertical drawing tool item in the list of drawing tool which provide this +/// drawing tools options menu. +class VerticalDrawingToolItem extends DrawingToolItem { + /// Initializes + const VerticalDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + VerticalDrawingToolConfig config = const VerticalDrawingToolConfig(), + }) : super( + key: key, + title: 'Vertical', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createIndicatorItemState() => + VerticalDrawingToolItemState(); +} + +/// Vertival drawing tool Item State class +class VerticalDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _lineStyle; + String? _pattern; + + @override + VerticalDrawingToolConfig createDrawingToolConfig() => + VerticalDrawingToolConfig( + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField(), + ], + ); + + Widget _buildColorField() => Row( + children: [ + Text( + ChartLocalization.of(context)!.labelColor, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: _currentLineStyle.color, + onColorChanged: (Color selectedColor) { + setState(() { + _lineStyle = _currentLineStyle.copyWith(color: selectedColor); + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as VerticalDrawingToolConfig).lineStyle; + + String get _currentPattern => + _pattern ?? (widget.config as VerticalDrawingToolConfig).pattern; +} diff --git a/lib/src/deriv_chart/chart/basic_chart.dart b/lib/src/deriv_chart/chart/basic_chart.dart index 9977a786e..85c21920f 100644 --- a/lib/src/deriv_chart/chart/basic_chart.dart +++ b/lib/src/deriv_chart/chart/basic_chart.dart @@ -266,6 +266,16 @@ class BasicChartState extends State bottomPadding: _bottomPadding, ); + /// Returns quote based on the y-coordinate. + double chartQuoteFromCanvasY(double quote) => quoteFromCanvasY( + y: quote, + topBoundQuote: _topBoundQuote, + bottomBoundQuote: _bottomBoundQuote, + canvasHeight: canvasSize?.height ?? 200, + topPadding: _topPadding, + bottomPadding: _bottomPadding, + ); + @override Widget build(BuildContext context) => LayoutBuilder( key: _key, diff --git a/lib/src/deriv_chart/chart/chart.dart b/lib/src/deriv_chart/chart/chart.dart index 57f10da6c..f81b3629a 100644 --- a/lib/src/deriv_chart/chart/chart.dart +++ b/lib/src/deriv_chart/chart/chart.dart @@ -1,4 +1,6 @@ import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis.dart'; import 'package:deriv_chart/src/misc/callbacks.dart'; @@ -6,7 +8,7 @@ import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; - +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'bottom_chart.dart'; import 'data_visualization/chart_data.dart'; import 'main_chart.dart'; @@ -17,6 +19,9 @@ class Chart extends StatefulWidget { const Chart({ required this.mainSeries, required this.granularity, + required this.onAddDrawing, + this.drawings, + this.selectedDrawingTool, this.pipSize = 4, this.controller, this.overlaySeries, @@ -46,6 +51,16 @@ class Chart extends StatefulWidget { /// Open position marker series. final MarkerSeries? markerSeries; + /// Existing drawings. + final List? drawings; + + /// Callback to pass new drawing to the parent. + final void Function(Map> addedDrawing, + {bool isDrawingFinished}) onAddDrawing; + + /// Selected drawing tool. + final DrawingToolConfig? selectedDrawingTool; + /// Chart's controller final ChartController? controller; @@ -153,6 +168,9 @@ class _ChartState extends State with WidgetsBindingObserver { Expanded( flex: 3, child: MainChart( + drawings: widget.drawings, + onAddDrawing: widget.onAddDrawing, + selectedDrawingTool: widget.selectedDrawingTool, controller: _controller, mainSeries: widget.mainSeries, overlaySeries: widget.overlaySeries, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart new file mode 100644 index 000000000..ba4abd8a9 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart @@ -0,0 +1,22 @@ +///A class that holds vector data +class Vector { + /// Initializes + const Vector({ + required this.x0, + required this.y0, + required this.x1, + required this.y1, + }); + + ///Related x for starting point of the vector + final double? x0; + + ///Related y for starting point of the vector + final double? y0; + + ///Related x for ending point of the vector + final double? x1; + + ///Related y for ending point of the vector + final double? y1; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart new file mode 100644 index 000000000..3c370c171 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -0,0 +1,38 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; + +/// Base class to draw a particular drawing +class Drawing { + /// Paint + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + ) {} + + /// Calculates y intersection based on vector points. + /// Based on yIntersection() from SmartCharts + double? getYIntersection(Vector vector, double x) { + final double x1 = vector.x0!, x2 = vector.x1!, x3 = x, x4 = x; + final double y1 = vector.y0!, y2 = vector.y1!, y3 = 0, y4 = 10000; + final double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + final double numerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); + + double mua = numerator / denominator; + if (denominator == 0) { + if (numerator == 0) { + mua = 1; + } else { + return null; + } + } + + final double y = y1 + mua * (y2 - y1); + return y; + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart new file mode 100644 index 000000000..4859b8ecf --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart @@ -0,0 +1,48 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; + +/// Creates a drawing piece by piece collected on every gesture +/// exists in a widget tree starting from selecting a selectedDrawingTool and +/// until drawing is finished +class DrawingCreator extends StatelessWidget { + /// Initializes drawing creator area. + const DrawingCreator({ + required this.onAddDrawing, + required this.selectedDrawingTool, + required this.quoteFromCanvasY, + Key? key, + }) : super(key: key); + + /// Selected drawing tool. + final DrawingToolConfig selectedDrawingTool; + + /// Callback to pass a newly created drawing to the parent. + final void Function(Map> addedDrawing, + {bool isDrawingFinished}) onAddDrawing; + + /// Conversion function for converting quote to chart's canvas' Y position. + final double Function(double) quoteFromCanvasY; + + @override + Widget build(BuildContext context) { + final String drawingToolType = selectedDrawingTool.toJson()['name']; + switch (drawingToolType) { + case 'dt_line': + return LineDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + case 'dt_vertical': + return VerticalDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + // TODO(maryia-binary): add the rest of drawing tools here + default: + return Container(); + } + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart new file mode 100644 index 000000000..c254dc8e5 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart @@ -0,0 +1,29 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; + +/// A class that hold drawing data. +class DrawingData { + /// Initializes + const DrawingData({ + required this.id, + required this.config, + required this.drawings, + }); + + /// Unique id of the current drawing. + final String id; + + /// Configuration of the current drawing. + final DrawingToolConfig? config; + + ///Drawing list. + final List drawings; + + /// Updates configuration. + DrawingData updateConfig(DrawingToolConfig config) => + DrawingData(id: id, config: config, drawings: drawings); + + /// Updates drawing list. + DrawingData updateDrawingList(List drawings) => + DrawingData(id: id, config: config, drawings: drawings); +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart new file mode 100644 index 000000000..b774e3ee5 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -0,0 +1,75 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Paints every existing drawing. +class DrawingPainter extends StatefulWidget { + /// Initializes + const DrawingPainter({ + required this.drawingData, + required this.quoteToCanvasY, + Key? key, + }) : super(key: key); + + /// Contains each drawing data + final DrawingData? drawingData; + + /// Conversion function for converting quote to chart's canvas' Y position. + final double Function(double) quoteToCanvasY; + + @override + _DrawingPainterState createState() => _DrawingPainterState(); +} + +class _DrawingPainterState extends State { + @override + Widget build(BuildContext context) { + final XAxisModel xAxis = context.watch(); + + return Stack(children: [ + widget.drawingData != null + ? CustomPaint( + child: Container(), + painter: _DrawingPainter( + drawingData: widget.drawingData!, + theme: context.watch(), + epochToX: xAxis.xFromEpoch, + quoteToY: widget.quoteToCanvasY, + ), + ) + : Container() + ]); + } +} + +class _DrawingPainter extends CustomPainter { + _DrawingPainter({ + required this.drawingData, + required this.theme, + required this.epochToX, + required this.quoteToY, + }); + + final DrawingData drawingData; + final ChartTheme theme; + double Function(int x) epochToX; + double Function(double y) quoteToY; + + @override + void paint(Canvas canvas, Size size) { + for (final Drawing drawing in drawingData.drawings) { + drawing.onPaint( + canvas, size, theme, epochToX, quoteToY, drawingData.config!); + } + } + + @override + bool shouldRepaint(_DrawingPainter oldDelegate) => true; + + @override + bool shouldRebuildSemantics(_DrawingPainter oldDelegate) => false; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart new file mode 100644 index 000000000..beb357507 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -0,0 +1,94 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; +import '../drawing.dart'; + +/// Line drawing tool. A line is a vector defined by two points that is +/// infinite in both directions. +class LineDrawing extends Drawing { + /// Initializes + LineDrawing({ + required this.drawingPart, + this.startEpoch = 0, + this.startYCoord = 0, + this.endEpoch = 0, + this.endYCoord = 0, + }); + + /// Part of a drawing: 'marker' or 'line' + final String drawingPart; + + /// Starting epoch. + final int startEpoch; + + /// Starting Y coordinates. + final double startYCoord; + + /// Ending epoch. + final int endEpoch; + + /// Ending Y coordinates. + final double endYCoord; + + /// Marker radius. + final double markerRadius = 4; + + /// Paint + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config) { + final LineStyle lineStyle = config.toJson()['lineStyle']; + final String pattern = config.toJson()['pattern']; + final double startQuoteToY = quoteToY(startYCoord); + final double endQuoteToY = quoteToY(endYCoord); + + if (drawingPart == 'marker') { + final double startXCoord = epochToX(startEpoch); + + canvas.drawCircle(Offset(startXCoord, startQuoteToY), markerRadius, + Paint()..color = lineStyle.color); + } else if (drawingPart == 'line') { + final double startXCoord = epochToX(startEpoch); + final double endXCoord = epochToX(endEpoch); + + /// Based on calculateOuterSet() from SmartCharts + Vector vec = Vector( + x0: startXCoord, + y0: startQuoteToY, + x1: endXCoord, + y1: endQuoteToY, + ); + if (vec.x0! > vec.x1!) { + vec = Vector( + x0: endXCoord, + y0: endQuoteToY, + x1: startXCoord, + y1: startQuoteToY, + ); + } + + final double earlier = vec.x0! - 1000; + final double later = vec.x1! + 1000; + + final double startY = getYIntersection(vec, earlier) ?? 0, + endingY = getYIntersection(vec, later) ?? 0, + startX = earlier, + endingX = later; + + if (pattern == 'solid') { + canvas.drawLine( + Offset(startX, startY), + Offset(endingX, endingY), + Paint() + ..color = lineStyle.color + ..strokeWidth = lineStyle.thickness); + } + } + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart new file mode 100644 index 000000000..b7a83d8a7 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart @@ -0,0 +1,120 @@ +import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import './line_drawing.dart'; + +/// Creates a Line drawing piece by piece collected on every gesture +/// exists in a widget tree starting from selecting a line drawing tool and +/// until drawing is finished +class LineDrawingCreator extends StatefulWidget { + /// Initializes the line drawing creator. + const LineDrawingCreator({ + required this.onAddDrawing, + required this.quoteFromCanvasY, + Key? key, + }) : super(key: key); + + /// Callback to pass a newly created line drawing to the parent. + final void Function(Map> addedDrawing, + {bool isDrawingFinished}) onAddDrawing; + + /// Conversion function for converting quote from chart's canvas' Y position. + final double Function(double) quoteFromCanvasY; + + @override + _LineDrawingCreatorState createState() => _LineDrawingCreatorState(); +} + +class _LineDrawingCreatorState extends State { + late GestureManagerState gestureManager; + + /// Parts of a particular line drawing, e.g. marker, line + final List _drawingParts = []; + + /// Tapped position. + Offset? position; + + /// Saved starting epoch. + int? _startingEpoch; + + /// Saved starting Y coordinates. + double? _startingYPoint; + + /// If drawing has been started. + bool _isPenDown = false; + + /// Unique drawing id. + String _drawingId = ''; + + /// If drawing has been finished. + bool _isDrawingFinished = false; + + /// Get epoch from x. + int Function(double x)? epochFromX; + + @override + void initState() { + super.initState(); + gestureManager = context.read() + ..registerCallback(_onTap); + } + + @override + void dispose() { + gestureManager.removeCallback(_onTap); + super.dispose(); + } + + void _onTap(TapUpDetails details) { + if (_isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + if (!_isPenDown) { + _startingEpoch = epochFromX!(position!.dx); + _startingYPoint = widget.quoteFromCanvasY(position!.dy); + _isPenDown = true; + _drawingId = 'line_$_startingEpoch'; + + _drawingParts.add(LineDrawing( + drawingPart: 'marker', + startEpoch: _startingEpoch!, + startYCoord: _startingYPoint!, + )); + } else if (!_isDrawingFinished) { + _isPenDown = false; + _isDrawingFinished = true; + final int endEpoch = epochFromX!(position!.dx); + final double endYPoint = widget.quoteFromCanvasY(position!.dy); + + _drawingParts.addAll([ + LineDrawing( + drawingPart: 'marker', + startEpoch: endEpoch, + startYCoord: endYPoint, + ), + LineDrawing( + drawingPart: 'line', + startEpoch: _startingEpoch!, + startYCoord: _startingYPoint!, + endEpoch: endEpoch, + endYCoord: endYPoint, + ) + ]); + } + widget.onAddDrawing( + >{_drawingId: _drawingParts}, + isDrawingFinished: _isDrawingFinished); + }); + } + + @override + Widget build(BuildContext context) { + final XAxisModel xAxis = context.watch(); + epochFromX = xAxis.epochFromX; + + return Container(); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart new file mode 100644 index 000000000..d7924c5ae --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -0,0 +1,54 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; +import '../drawing.dart'; + +/// Vertical drawing tool. A vertical is a vertical line defined by one point +/// that is infinite in both directions. +class VerticalDrawing extends Drawing { + /// Initializes + VerticalDrawing({ + required this.drawingPart, + this.epoch = 0, + this.yCoord = 0, + }); + + /// Part of a drawing: 'vertical' + final String drawingPart; + + /// Starting epoch. + final int epoch; + + /// Starting Y coordinates. + final double yCoord; + + /// Paint + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config) { + final LineStyle lineStyle = config.toJson()['lineStyle']; + final String pattern = config.toJson()['pattern']; + final double startQuoteToY = quoteToY(yCoord); + + if (drawingPart == 'vertical') { + final double xCoord = epochToX(epoch); + + final double startY = startQuoteToY - 1000, + endingY = startQuoteToY + 1000; + + if (pattern == 'solid') { + canvas.drawLine( + Offset(xCoord, startY), + Offset(xCoord, endingY), + Paint() + ..color = lineStyle.color + ..strokeWidth = lineStyle.thickness); + } + } + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart new file mode 100644 index 000000000..005d413a9 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart @@ -0,0 +1,94 @@ +import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import './vertical_drawing.dart'; + +/// Creates a Vertical line drawing +class VerticalDrawingCreator extends StatefulWidget { + /// Initializes the vertical drawing creator. + const VerticalDrawingCreator({ + required this.onAddDrawing, + required this.quoteFromCanvasY, + Key? key, + }) : super(key: key); + + /// Callback to pass a newly created vertical drawing to the parent. + final void Function(Map> addedDrawing, + {bool isDrawingFinished}) onAddDrawing; + + /// Conversion function for converting quote from chart's canvas' Y position. + final double Function(double) quoteFromCanvasY; + + @override + _VerticalDrawingCreatorState createState() => _VerticalDrawingCreatorState(); +} + +class _VerticalDrawingCreatorState extends State { + late GestureManagerState gestureManager; + + /// Parts of a particular vertical drawing, e.g. marker, line + final List _drawingParts = []; + + /// Tapped position. + Offset? position; + + /// Saved starting epoch. + int? _startingEpoch; + + /// Saved starting Y coordinates. + double? _startingYPoint; + + /// Unique drawing id. + String _drawingId = ''; + + /// If drawing has been finished. + bool _isDrawingFinished = false; + + /// Get epoch from x. + int Function(double x)? epochFromX; + + @override + void initState() { + super.initState(); + gestureManager = context.read() + ..registerCallback(_onTap); + } + + @override + void dispose() { + gestureManager.removeCallback(_onTap); + super.dispose(); + } + + void _onTap(TapUpDetails details) { + if (_isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + _startingEpoch = epochFromX!(position!.dx); + _startingYPoint = widget.quoteFromCanvasY(position!.dy); + _drawingId = 'vertical_$_startingEpoch'; + _isDrawingFinished = true; + + _drawingParts.add(VerticalDrawing( + drawingPart: 'vertical', + epoch: _startingEpoch!, + yCoord: _startingYPoint!, + )); + + widget.onAddDrawing( + >{_drawingId: _drawingParts}, + isDrawingFinished: _isDrawingFinished); + }); + } + + @override + Widget build(BuildContext context) { + final XAxisModel xAxis = context.watch(); + epochFromX = xAxis.epochFromX; + + return Container(); + } +} diff --git a/lib/src/deriv_chart/chart/helpers/functions/conversion.dart b/lib/src/deriv_chart/chart/helpers/functions/conversion.dart index 3f6235fd5..f9cc2392f 100644 --- a/lib/src/deriv_chart/chart/helpers/functions/conversion.dart +++ b/lib/src/deriv_chart/chart/helpers/functions/conversion.dart @@ -94,3 +94,28 @@ double quoteToCanvasY({ return topPadding + pxFromTopBound; } + +/// Returns quote based on the y-coordinate. +double quoteFromCanvasY({ + required double y, + required double topBoundQuote, + required double bottomBoundQuote, + required double canvasHeight, + required double topPadding, + required double bottomPadding, +}) { + final double drawingRange = canvasHeight - topPadding - bottomPadding; + final double quoteRange = topBoundQuote - bottomBoundQuote; + + final double yTopBound = topPadding; + + if (quoteRange == 0) { + return topBoundQuote; + } + + final double yToTopBoundFraction = (y - yTopBound) / drawingRange; + + final double quoteDiffFromTopBound = yToTopBoundFraction * quoteRange; + + return topBoundQuote - quoteDiffFromTopBound; +} diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index c54d14bec..beb2c95c2 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -3,13 +3,17 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/crosshair/crosshair_area.dart'; import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_data_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_area.dart'; import 'package:deriv_chart/src/deriv_chart/chart/loading_animation.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; - +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'basic_chart.dart'; import 'data_visualization/chart_data.dart'; import 'data_visualization/models/animation_info.dart'; @@ -21,6 +25,9 @@ class MainChart extends BasicChart { /// Initializes the main chart to display in the chart widget. MainChart({ required DataSeries mainSeries, + required this.onAddDrawing, + this.drawings, + this.selectedDrawingTool, this.isLive = false, int pipSize = 4, Key? key, @@ -57,6 +64,16 @@ class MainChart extends BasicChart { /// The series that hold the list markers. final MarkerSeries? markerSeries; + /// Existing drawings. + final List? drawings; + + /// Callback to pass new drawing to the parent. + final void Function(Map> addedDrawing, + {bool isDrawingFinished}) onAddDrawing; + + /// Selected drawing tool. + final DrawingToolConfig? selectedDrawingTool; + /// The function that gets called on crosshair appearance. final VoidCallback? onCrosshairAppeared; @@ -267,6 +284,18 @@ class _ChartImplementationState extends BasicChartState { quoteToCanvasY: chartQuoteToCanvasY, ), ), + if (widget.drawings != null) + ...widget.drawings! + .map((DrawingData drawingData) => DrawingPainter( + drawingData: drawingData, + quoteToCanvasY: chartQuoteToCanvasY, + )), + if (widget.selectedDrawingTool != null) + DrawingCreator( + onAddDrawing: widget.onAddDrawing, + selectedDrawingTool: widget.selectedDrawingTool!, + quoteFromCanvasY: chartQuoteFromCanvasY, + ), _buildCrosshairArea(), if (_isScrollToLastTickAvailable) Positioned( diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index 696beedd6..86fc56583 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -1,13 +1,19 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:collection/collection.dart'; +import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicators_dialog.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/annotations/chart_annotation.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/chart_object.dart'; import 'package:deriv_chart/src/misc/callbacks.dart'; import 'package:deriv_chart/src/misc/chart_controller.dart'; import 'package:deriv_chart/src/models/chart_axis_config.dart'; @@ -19,9 +25,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'chart/chart.dart'; -import 'chart/data_visualization/models/chart_object.dart'; - /// A wrapper around the [Chart] which handles adding indicators to the chart. class DerivChart extends StatefulWidget { /// Initializes @@ -95,9 +98,16 @@ class _DerivChartState extends State { final AddOnsRepository _drawingToolsRepo = AddOnsRepository(DrawingToolConfig); + /// Selected drawing tool. + DrawingToolConfig? _selectedDrawingTool; + + /// Existing drawings. + final List _drawings = []; + @override void initState() { super.initState(); + loadSavedIndicatorsAndDrawingTools(); } @@ -171,6 +181,9 @@ class _DerivChartState extends State { ), )) ], + drawings: _drawings, + onAddDrawing: _onAddDrawing, + selectedDrawingTool: _selectedDrawingTool, markerSeries: widget.markerSeries, theme: widget.theme, onCrosshairAppeared: widget.onCrosshairAppeared, @@ -212,7 +225,26 @@ class _DerivChartState extends State { ChangeNotifierProvider< AddOnsRepository>.value( value: _drawingToolsRepo, - child: DrawingToolsDialog(), + child: DrawingToolsDialog( + onDrawingToolRemoval: (int index) { + setState(() { + _drawings.removeAt(index); + }); + }, + onDrawingToolSelection: + (DrawingToolConfig selectedDrawingTool) { + setState(() { + _selectedDrawingTool = selectedDrawingTool; + }); + }, + onDrawingToolUpdate: + (int index, DrawingToolConfig updatedConfig) { + setState(() { + _drawings[index] = + _drawings[index].updateConfig(updatedConfig); + }); + }, + ), ), ); }, @@ -222,4 +254,32 @@ class _DerivChartState extends State { ), ), ); + + void _onAddDrawing( + Map> addedDrawing, { + bool isDrawingFinished = false, + }) { + setState(() { + final String drawingId = addedDrawing.keys.first; + + final DrawingData? existingDrawing = _drawings.firstWhereOrNull( + (DrawingData drawing) => drawing.id == drawingId, + ); + + if (existingDrawing == null) { + _drawings.add(DrawingData( + id: drawingId, + config: _selectedDrawingTool!, + drawings: addedDrawing.values.first, + )); + } else { + existingDrawing.updateDrawingList(addedDrawing.values.first); + } + + if (isDrawingFinished) { + _drawingToolsRepo.add(_selectedDrawingTool!); + _selectedDrawingTool = null; + } + }); + } } diff --git a/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart b/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart index d8aad0a69..1c2ffa21a 100644 --- a/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart +++ b/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart @@ -1,11 +1,9 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/abstract_single_indicator_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/indicator_options.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; -import 'package:deriv_technical_analysis/src/indicators/cached_indicator.dart'; import 'package:flutter_test/flutter_test.dart'; class MockOptions extends IndicatorOptions { From 1389826b84880901ada7ae26c1476f94a17f653b Mon Sep 17 00:00:00 2001 From: Bahar Date: Fri, 9 Jun 2023 11:07:24 +0800 Subject: [PATCH 07/29] bahar/82785/Make drawings draggable (#224) * feat: make_drawings_draggable * line_changes_added_before_cleanup * cleanup * extract_drawing_tool_chart * fixed_crosshair_issue * fix_long_press_issue * make_drawing_class_abstract * fix_review_issues * added_enum_for_darwing_parts * fix_review_comments * fix_review_pointed_out_issues * fix_comments * add initial value for draggable edge point and remove extra inputs * add vector.zero * drawing_selection_added * review_comments * handle unfinished drawings * fix_review_comments * fix_json_autogenerated_files_issue --- .../android/app/src/main/AndroidManifest.xml | 1 + .../line/line_drawing_tool_config.dart | 5 +- .../line/line_drawing_tool_config.g.dart | 22 +- .../line/line_drawing_tool_item.dart | 5 +- .../vertical_drawing_tool_config.dart | 5 +- .../vertical_drawing_tool_config.g.dart | 22 +- .../vertical/vertical_drawing_tool_item.dart | 5 +- lib/src/deriv_chart/chart/chart.dart | 5 + .../data_model/draggable_edge_point.dart | 51 +++++ .../data_model/drawing_paint_style.dart | 24 ++ .../data_model/drawing_parts.dart | 8 + .../data_model/drawing_pattern.dart | 11 + .../drawing_tools/data_model/point.dart | 31 +++ .../drawing_tools/data_model/vector.dart | 21 +- .../drawing_tools/drawing.dart | 26 ++- .../drawing_tools/drawing_creator.dart | 13 ++ .../drawing_tools/drawing_data.dart | 30 ++- .../drawing_tools/drawing_painter.dart | 145 +++++++++++- .../drawing_tools/line/line_drawing.dart | 213 ++++++++++++++---- .../line/line_drawing_creator.dart | 70 ++++-- .../vertical/vertical_drawing.dart | 84 +++++-- .../vertical/vertical_drawing_creator.dart | 8 +- lib/src/deriv_chart/chart/main_chart.dart | 38 ++-- lib/src/deriv_chart/deriv_chart.dart | 30 ++- .../drawing_tool_chart.dart | 84 +++++++ 25 files changed, 789 insertions(+), 168 deletions(-) create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart create mode 100644 lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 5d77f4100..2ee70cb26 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -44,4 +44,5 @@ android:name="flutterEmbedding" android:value="2" /> + diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart index cc634e709..ede15ed14 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'line_drawing_tool_item.dart'; @@ -14,7 +15,7 @@ class LineDrawingToolConfig extends DrawingToolConfig { /// Initializes const LineDrawingToolConfig({ this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), - this.pattern = 'solid', + this.pattern = DrawingPatterns.solid, }) : super(); /// Initializes from JSON. @@ -33,7 +34,7 @@ class LineDrawingToolConfig extends DrawingToolConfig { /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' // TODO(maryia-binary): implement 'dotted' and 'dashed' patterns - final String pattern; + final DrawingPatterns pattern; @override DrawingToolItem getItem( diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart index db9161488..2d57e2366 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart @@ -7,16 +7,24 @@ part of 'line_drawing_tool_config.dart'; // ************************************************************************** LineDrawingToolConfig _$LineDrawingToolConfigFromJson( - Map json) { - return LineDrawingToolConfig( - lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), - pattern: json['pattern'] as String, - ); -} + Map json) => + LineDrawingToolConfig( + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); Map _$LineDrawingToolConfigToJson( LineDrawingToolConfig instance) => { 'lineStyle': instance.lineStyle, - 'pattern': instance.pattern, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart index 75d4d8dfa..48501e464 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; import 'line_drawing_tool_config.dart'; @@ -31,7 +32,7 @@ class LineDrawingToolItem extends DrawingToolItem { class LineDrawingToolItemState extends DrawingToolItemState { LineStyle? _lineStyle; - String? _pattern; + DrawingPatterns? _pattern; @override LineDrawingToolConfig createDrawingToolConfig() => LineDrawingToolConfig( @@ -68,6 +69,6 @@ class LineDrawingToolItemState LineStyle get _currentLineStyle => _lineStyle ?? (widget.config as LineDrawingToolConfig).lineStyle; - String get _currentPattern => + DrawingPatterns get _currentPattern => _pattern ?? (widget.config as LineDrawingToolConfig).pattern; } diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart index 3087fb65c..83c62c354 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -15,7 +16,7 @@ class VerticalDrawingToolConfig extends DrawingToolConfig { /// Initializes const VerticalDrawingToolConfig({ this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), - this.pattern = 'solid', + this.pattern = DrawingPatterns.solid, }) : super(); /// Initializes from JSON. @@ -33,7 +34,7 @@ class VerticalDrawingToolConfig extends DrawingToolConfig { final LineStyle lineStyle; /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' - final String pattern; + final DrawingPatterns pattern; @override DrawingToolItem getItem( diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart index b711df85e..10933b01c 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart @@ -7,16 +7,24 @@ part of 'vertical_drawing_tool_config.dart'; // ************************************************************************** VerticalDrawingToolConfig _$VerticalDrawingToolConfigFromJson( - Map json) { - return VerticalDrawingToolConfig( - lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), - pattern: json['pattern'] as String, - ); -} + Map json) => + VerticalDrawingToolConfig( + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); Map _$VerticalDrawingToolConfigToJson( VerticalDrawingToolConfig instance) => { 'lineStyle': instance.lineStyle, - 'pattern': instance.pattern, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart index 40f1c5497..5b23ac662 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; import 'package:flutter/material.dart'; @@ -34,7 +35,7 @@ class VerticalDrawingToolItem extends DrawingToolItem { class VerticalDrawingToolItemState extends DrawingToolItemState { LineStyle? _lineStyle; - String? _pattern; + DrawingPatterns? _pattern; @override VerticalDrawingToolConfig createDrawingToolConfig() => @@ -71,6 +72,6 @@ class VerticalDrawingToolItemState LineStyle get _currentLineStyle => _lineStyle ?? (widget.config as VerticalDrawingToolConfig).lineStyle; - String get _currentPattern => + DrawingPatterns get _currentPattern => _pattern ?? (widget.config as VerticalDrawingToolConfig).pattern; } diff --git a/lib/src/deriv_chart/chart/chart.dart b/lib/src/deriv_chart/chart/chart.dart index f81b3629a..bd35eff37 100644 --- a/lib/src/deriv_chart/chart/chart.dart +++ b/lib/src/deriv_chart/chart/chart.dart @@ -20,6 +20,7 @@ class Chart extends StatefulWidget { required this.mainSeries, required this.granularity, required this.onAddDrawing, + required this.clearDrawingToolSelection, this.drawings, this.selectedDrawingTool, this.pipSize = 4, @@ -61,6 +62,9 @@ class Chart extends StatefulWidget { /// Selected drawing tool. final DrawingToolConfig? selectedDrawingTool; + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + /// Chart's controller final ChartController? controller; @@ -171,6 +175,7 @@ class _ChartState extends State with WidgetsBindingObserver { drawings: widget.drawings, onAddDrawing: widget.onAddDrawing, selectedDrawingTool: widget.selectedDrawingTool, + clearDrawingToolSelection: widget.clearDrawingToolSelection, controller: _controller, mainSeries: widget.mainSeries, overlaySeries: widget.overlaySeries, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart new file mode 100644 index 000000000..0d9ec38f7 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart @@ -0,0 +1,51 @@ +import 'dart:ui'; + +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; + +/// A class that holds draggable edge point data. +/// Draggable edge points are part of the drawings which added by user clicks +/// And we want to hanle difftent types of drag events on them. +/// For example with dots are draggable edge points for the line +/// ⎯⎯⚪️⎯⎯⎯⚪️⎯⎯ +class DraggableEdgePoint { + /// Represents whether the whole drawing is currently being dragged or not + bool isDrawingDragged = false; + + /// Represents whether the edge point is currently being dragged or not + bool isDragged = false; + + /// Holds the current position of the edge point when it is being dragged. + Offset draggedPosition = Offset.zero; + + /// A callback method that takes the relative x and y positions as parameter, + /// sets the draggedPosition field to its value and return epoch and quote + /// values. + Point updatePosition(int epoch, double yCoord, + double Function(int x) epochToX, double Function(double y) quoteToY) { + final Offset oldPosition = Offset(epoch.toDouble(), yCoord); + draggedPosition = isDrawingDragged ? draggedPosition : oldPosition; + + final double xCoord = epochToX(draggedPosition.dx.toInt()); + final double quoteY = quoteToY(draggedPosition.dy); + + return Point(x: xCoord, y: quoteY); + } + + /// A method that takes the gesture delta Offset object as parameter + /// and sets the draggedPosition field to its value. + void updatePositionWithLocalPositions( + Offset delta, + XAxisModel xAxis, + double Function(double) quoteFromCanvasY, + double Function(double y) quoteToY, + {required bool isOtherEndDragged}) { + final Offset localPosition = Offset( + xAxis.xFromEpoch(draggedPosition.dx.toInt()), + quoteToY(draggedPosition.dy)) + + (isOtherEndDragged ? Offset.zero : delta); + + draggedPosition = Offset(xAxis.epochFromX(localPosition.dx).toDouble(), + quoteFromCanvasY(localPosition.dy)); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart new file mode 100644 index 000000000..94538edad --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +/// A class that holds the paint style of the drawings +class DrawingPaintStyle { + /// Returns the glowy paint style of the line + Paint glowyLinePaintStyle(Color color, double thickness) => Paint() + ..color = color + ..strokeWidth = thickness + 3 + ..strokeCap = StrokeCap.round + ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 10); + + /// Returns the paint style of the the line + Paint linePaintStyle(Color color, double thickness) => Paint() + ..color = color + ..strokeWidth = thickness; + + /// Returns the paint style of the circle marker + Paint glowyCirclePaintStyle(Color color) => Paint() + ..color = color + ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 10); + + /// Returns the paint style of the circle marker + Paint transparentCirclePaintStyle() => Paint()..color = Colors.transparent; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart new file mode 100644 index 000000000..9729dca23 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart @@ -0,0 +1,8 @@ +/// Differnet types of drawing parts. +enum DrawingParts { + /// Used to show the marker. + marker, + + /// Used to show the line. + line, +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart new file mode 100644 index 000000000..2f4a8530d --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart @@ -0,0 +1,11 @@ +/// Differnet types of drawing patterns. +enum DrawingPatterns { + /// Used for solid line. + solid, + + /// Used for dotted line. + dotted, + + /// Used for dashed line. + dashed, +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart new file mode 100644 index 000000000..12c18f4b1 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart @@ -0,0 +1,31 @@ +import 'dart:math'; +import 'dart:ui'; + +///A class that holds point data +class Point { + /// Initializes + const Point({ + required this.x, + required this.y, + }); + + /// Related x for the point + final double x; + + /// Related y for the point + final double y; + + /// Checks whether the point has been "clicked" by a user at a certain + /// position on the screen, within a given "affected area" radius. + /// + /// The [position] parameter is the location on the screen where the user + /// clicked, specified as an [Offset] object. + /// + /// The [affectedArea] parameter is the radius of the affected area around + /// the point. + /// + /// Returns `true` if the distance between the [position] and the point is + /// less than the [affectedArea], indicating that the point has been "clicked" + bool isClicked(Offset position, double affectedArea) => + pow(x - position.dx, 2) + pow(y - position.dy, 2) < pow(affectedArea, 2); +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart index ba4abd8a9..eeb9ee2e7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart @@ -1,4 +1,4 @@ -///A class that holds vector data +/// A class that holds vector data class Vector { /// Initializes const Vector({ @@ -8,15 +8,18 @@ class Vector { required this.y1, }); - ///Related x for starting point of the vector - final double? x0; + /// Vector with zero coordinate. + const Vector.zero() : this(x0: 0, y0: 0, x1: 0, y1: 0); - ///Related y for starting point of the vector - final double? y0; + /// Related x for starting point of the vector + final double x0; - ///Related x for ending point of the vector - final double? x1; + /// Related y for starting point of the vector + final double y0; - ///Related y for ending point of the vector - final double? y1; + /// Related x for ending point of the vector + final double x1; + + /// Related y for ending point of the vector + final double y1; } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index 3c370c171..a5ec37dcb 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -1,10 +1,12 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; /// Base class to draw a particular drawing -class Drawing { +abstract class Drawing { /// Paint void onPaint( Canvas canvas, @@ -12,14 +14,15 @@ class Drawing { ChartTheme theme, double Function(int x) epochToX, double Function(double y) quoteToY, - DrawingToolConfig config, - ) {} + DrawingData drawingData, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }); /// Calculates y intersection based on vector points. - /// Based on yIntersection() from SmartCharts double? getYIntersection(Vector vector, double x) { - final double x1 = vector.x0!, x2 = vector.x1!, x3 = x, x4 = x; - final double y1 = vector.y0!, y2 = vector.y1!, y3 = 0, y4 = 10000; + final double x1 = vector.x0, x2 = vector.x1, x3 = x, x4 = x; + final double y1 = vector.y0, y2 = vector.y1, y3 = 0, y4 = 10000; final double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); final double numerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); @@ -35,4 +38,15 @@ class Drawing { final double y = y1 + mua * (y2 - y1); return y; } + + /// Calculates whether a user's touch or click intersects + /// with any of the painted areas on the screen + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart index 4859b8ecf..d0a09e5be 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart @@ -13,6 +13,8 @@ class DrawingCreator extends StatelessWidget { required this.onAddDrawing, required this.selectedDrawingTool, required this.quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, Key? key, }) : super(key: key); @@ -26,14 +28,25 @@ class DrawingCreator extends StatelessWidget { /// Conversion function for converting quote to chart's canvas' Y position. final double Function(double) quoteFromCanvasY; + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + @override Widget build(BuildContext context) { + // TODO(bahar-deriv): Deligate the creation of drawing to the specific + // drawing tool config final String drawingToolType = selectedDrawingTool.toJson()['name']; + switch (drawingToolType) { case 'dt_line': return LineDrawingCreator( onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, ); case 'dt_vertical': return VerticalDrawingCreator( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart index c254dc8e5..5661940b9 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart @@ -4,26 +4,38 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too /// A class that hold drawing data. class DrawingData { /// Initializes - const DrawingData({ + DrawingData({ required this.id, required this.config, - required this.drawings, + required this.drawingParts, + this.isDrawingFinished = false, + this.isSelected = true, }); /// Unique id of the current drawing. final String id; /// Configuration of the current drawing. - final DrawingToolConfig? config; + final DrawingToolConfig config; - ///Drawing list. - final List drawings; + /// Drawing list. + final List drawingParts; + + /// If drawing is finished. + bool isDrawingFinished; + + /// If the drawing is selected by the user. + bool isSelected; /// Updates configuration. - DrawingData updateConfig(DrawingToolConfig config) => - DrawingData(id: id, config: config, drawings: drawings); + DrawingData updateConfig(DrawingToolConfig config) => DrawingData( + id: id, + config: config, + drawingParts: drawingParts, + isDrawingFinished: isDrawingFinished, + ); /// Updates drawing list. - DrawingData updateDrawingList(List drawings) => - DrawingData(id: id, config: config, drawings: drawings); + DrawingData updateDrawingPartList(List drawingParts) => + DrawingData(id: id, config: config, drawingParts: drawingParts); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index b774e3ee5..0909bdcb9 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -1,7 +1,9 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:deriv_chart/deriv_chart.dart'; @@ -12,6 +14,9 @@ class DrawingPainter extends StatefulWidget { const DrawingPainter({ required this.drawingData, required this.quoteToCanvasY, + required this.quoteFromCanvasY, + required this.onMoveDrawing, + required this.setIsDrawingSelected, Key? key, }) : super(key: key); @@ -23,26 +28,112 @@ class DrawingPainter extends StatefulWidget { @override _DrawingPainterState createState() => _DrawingPainterState(); + + /// Conversion function for converting quote to chart's canvas' Y position. + final double Function(double) quoteFromCanvasY; + + /// Callback to check if any single part of a single drawing is moved + /// regardless of knowing type of the drawing. + final void Function({bool isDrawingMoved}) onMoveDrawing; + + /// Callback to set if drawing is selected (tapped). + final void Function(DrawingData drawing) setIsDrawingSelected; } class _DrawingPainterState extends State { + bool _isDrawingDragged = false; + final DraggableEdgePoint _draggableStartPoint = DraggableEdgePoint(); + final DraggableEdgePoint _draggableEndPoint = DraggableEdgePoint(); + Offset? _previousPosition; + @override Widget build(BuildContext context) { final XAxisModel xAxis = context.watch(); - return Stack(children: [ - widget.drawingData != null - ? CustomPaint( - child: Container(), - painter: _DrawingPainter( + void _onPanUpdate(DragUpdateDetails details) { + if (widget.drawingData!.isSelected && + widget.drawingData!.isDrawingFinished) { + setState(() { + _isDrawingDragged = details.delta != Offset.zero; + _draggableStartPoint + ..isDrawingDragged = _isDrawingDragged + ..updatePositionWithLocalPositions( + details.delta, + xAxis, + widget.quoteFromCanvasY, + widget.quoteToCanvasY, + isOtherEndDragged: _draggableEndPoint.isDragged, + ); + _draggableEndPoint + ..isDrawingDragged = _isDrawingDragged + ..updatePositionWithLocalPositions( + details.delta, + xAxis, + widget.quoteFromCanvasY, + widget.quoteToCanvasY, + isOtherEndDragged: _draggableStartPoint.isDragged, + ); + }); + } + } + + DragUpdateDetails convertLongPressToDrag( + LongPressMoveUpdateDetails longPressDetails, Offset? previousPosition) { + final Offset delta = longPressDetails.localPosition - previousPosition!; + return DragUpdateDetails( + delta: delta, + globalPosition: longPressDetails.globalPosition, + localPosition: longPressDetails.localPosition, + ); + } + + return widget.drawingData != null + ? GestureDetector( + onTapUp: (TapUpDetails details) { + widget.setIsDrawingSelected(widget.drawingData!); + }, + onLongPressDown: (LongPressDownDetails details) { + widget.onMoveDrawing(isDrawingMoved: true); + _previousPosition = details.localPosition; + }, + onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { + final DragUpdateDetails dragDetails = + convertLongPressToDrag(details, _previousPosition); + _previousPosition = details.localPosition; + + _onPanUpdate(dragDetails); + }, + onLongPressUp: () { + widget.onMoveDrawing(isDrawingMoved: false); + _draggableStartPoint.isDragged = false; + _draggableEndPoint.isDragged = false; + }, + onPanStart: (DragStartDetails details) { + widget.onMoveDrawing(isDrawingMoved: true); + }, + onPanUpdate: (DragUpdateDetails details) { + _onPanUpdate(details); + }, + onPanEnd: (DragEndDetails details) { + setState(() { + _draggableStartPoint.isDragged = false; + _draggableEndPoint.isDragged = false; + }); + widget.onMoveDrawing(isDrawingMoved: false); + }, + child: CustomPaint( + foregroundPainter: _DrawingPainter( drawingData: widget.drawingData!, theme: context.watch(), epochToX: xAxis.xFromEpoch, quoteToY: widget.quoteToCanvasY, + draggableStartPoint: _draggableStartPoint, + draggableEndPoint: _draggableEndPoint, ), - ) - : Container() - ]); + size: const Size(double.infinity, double.infinity), + ), + ) + : const SizedBox(); } } @@ -52,18 +143,30 @@ class _DrawingPainter extends CustomPainter { required this.theme, required this.epochToX, required this.quoteToY, + required this.draggableStartPoint, + this.draggableEndPoint, }); final DrawingData drawingData; final ChartTheme theme; double Function(int x) epochToX; double Function(double y) quoteToY; + DraggableEdgePoint draggableStartPoint; + DraggableEdgePoint? draggableEndPoint; @override void paint(Canvas canvas, Size size) { - for (final Drawing drawing in drawingData.drawings) { - drawing.onPaint( - canvas, size, theme, epochToX, quoteToY, drawingData.config!); + for (final Drawing drawingPart in drawingData.drawingParts) { + drawingPart.onPaint( + canvas, + size, + theme, + epochToX, + quoteToY, + drawingData, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + ); } } @@ -72,4 +175,24 @@ class _DrawingPainter extends CustomPainter { @override bool shouldRebuildSemantics(_DrawingPainter oldDelegate) => false; + + @override + bool hitTest(Offset position) { + for (final Drawing drawingPart in drawingData.drawingParts) { + if (drawingPart.hitTest( + position, + epochToX, + quoteToY, + drawingData.config, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + )) { + return true; + } + } + + /// For deselecting the drawing when tapping outside of the drawing. + drawingData.isSelected = false; + return false; + } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index beb357507..3bcbb0f52 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -1,8 +1,17 @@ +import 'dart:math'; + import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; -import '../drawing.dart'; /// Line drawing tool. A line is a vector defined by two points that is /// infinite in both directions. @@ -17,7 +26,7 @@ class LineDrawing extends Drawing { }); /// Part of a drawing: 'marker' or 'line' - final String drawingPart; + final DrawingParts drawingPart; /// Starting epoch. final int startEpoch; @@ -32,63 +41,167 @@ class LineDrawing extends Drawing { final double endYCoord; /// Marker radius. - final double markerRadius = 4; + final double markerRadius = 10; + + Vector _vector = const Vector.zero(); + + /// Keeps the latest position of the start and end point of drawing + Point? _startPoint, _endPoint; + + /// Vector of the line + Vector getLineVector( + double startXCoord, + double startQuoteToY, + double endXCoord, + double endQuoteToY, + ) { + Vector vec = Vector( + x0: startXCoord, + y0: startQuoteToY, + x1: endXCoord, + y1: endQuoteToY, + ); + if (vec.x0 > vec.x1) { + vec = Vector( + x0: endXCoord, + y0: endQuoteToY, + x1: startXCoord, + y1: startQuoteToY, + ); + } + + final double earlier = vec.x0 - 1000; + final double later = vec.x1 + 1000; + + final double startY = getYIntersection(vec, earlier) ?? 0, + endingY = getYIntersection(vec, later) ?? 0, + startX = earlier, + endingX = later; - /// Paint + return Vector( + x0: startX, + y0: startY, + x1: endingX, + y1: endingY, + ); + } + + /// Paint the line @override void onPaint( - Canvas canvas, - Size size, - ChartTheme theme, - double Function(int x) epochToX, - double Function(double y) quoteToY, - DrawingToolConfig config) { - final LineStyle lineStyle = config.toJson()['lineStyle']; - final String pattern = config.toJson()['pattern']; - final double startQuoteToY = quoteToY(startYCoord); - final double endQuoteToY = quoteToY(endYCoord); - - if (drawingPart == 'marker') { - final double startXCoord = epochToX(startEpoch); - - canvas.drawCircle(Offset(startXCoord, startQuoteToY), markerRadius, - Paint()..color = lineStyle.color); - } else if (drawingPart == 'line') { - final double startXCoord = epochToX(startEpoch); - final double endXCoord = epochToX(endEpoch); - - /// Based on calculateOuterSet() from SmartCharts - Vector vec = Vector( - x0: startXCoord, - y0: startQuoteToY, - x1: endXCoord, - y1: endQuoteToY, + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final DrawingPaintStyle paint = DrawingPaintStyle(); + final LineDrawingToolConfig config = + drawingData.config as LineDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; + + _startPoint = draggableStartPoint.updatePosition( + startEpoch, + startYCoord, + epochToX, + quoteToY, + ); + _endPoint = draggableEndPoint!.updatePosition( + endEpoch, + endYCoord, + epochToX, + quoteToY, + ); + + final double startXCoord = _startPoint!.x; + final double startQuoteToY = _startPoint!.y; + + final double endXCoord = _endPoint!.x; + final double endQuoteToY = _endPoint!.y; + + if (drawingPart == DrawingParts.marker) { + if (endEpoch != 0 && endQuoteToY != 0) { + /// Draw first point + canvas.drawCircle( + Offset(endXCoord, endQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } else if (startEpoch != 0 && startQuoteToY != 0) { + /// Draw second point + canvas.drawCircle( + Offset(startXCoord, startQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } + } else if (drawingPart == DrawingParts.line) { + _vector = getLineVector( + startXCoord, + startQuoteToY, + endXCoord, + endQuoteToY, ); - if (vec.x0! > vec.x1!) { - vec = Vector( - x0: endXCoord, - y0: endQuoteToY, - x1: startXCoord, - y1: startQuoteToY, + + if (pattern == DrawingPatterns.solid) { + canvas.drawLine( + Offset(_vector.x0, _vector.y0), + Offset(_vector.x1, _vector.y1), + drawingData.isSelected + ? paint.glowyLinePaintStyle(lineStyle.color, lineStyle.thickness) + : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), ); } + } + } - final double earlier = vec.x0! - 1000; - final double later = vec.x1! + 1000; + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen, for any of the edge points + /// it will call "setIsEdgeDragged" callback function to determine which + /// point is clicked + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final LineStyle lineStyle = config.toJson()['lineStyle']; - final double startY = getYIntersection(vec, earlier) ?? 0, - endingY = getYIntersection(vec, later) ?? 0, - startX = earlier, - endingX = later; + final double startXCoord = _startPoint!.x; + final double startQuoteToY = _startPoint!.y; - if (pattern == 'solid') { - canvas.drawLine( - Offset(startX, startY), - Offset(endingX, endingY), - Paint() - ..color = lineStyle.color - ..strokeWidth = lineStyle.thickness); - } + final double endXCoord = _endPoint!.x; + final double endQuoteToY = _endPoint!.y; + + /// Check if start point clicked + if (_startPoint!.isClicked(position, markerRadius)) { + draggableStartPoint.isDragged = true; } + + /// Check if end point clicked + if (_endPoint!.isClicked(position, markerRadius)) { + draggableEndPoint!.isDragged = true; + } + + /// Computes the distance between a point and a line which should be less + /// than the line thickness + 6 to make sure the user can easily click on + final double distance = ((endQuoteToY - startQuoteToY) * position.dx - + (endXCoord - startXCoord) * position.dy + + endXCoord * startQuoteToY - + endQuoteToY * startXCoord) / + sqrt(pow(endQuoteToY - startQuoteToY, 2) + + pow(endXCoord - startXCoord, 2)); + + return distance.abs() <= lineStyle.thickness + 6; } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart index b7a83d8a7..eaabf65ae 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart' import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../data_model/drawing_parts.dart'; import './line_drawing.dart'; /// Creates a Line drawing piece by piece collected on every gesture @@ -12,6 +13,8 @@ class LineDrawingCreator extends StatefulWidget { const LineDrawingCreator({ required this.onAddDrawing, required this.quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, Key? key, }) : super(key: key); @@ -22,6 +25,12 @@ class LineDrawingCreator extends StatefulWidget { /// Conversion function for converting quote from chart's canvas' Y position. final double Function(double) quoteFromCanvasY; + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + @override _LineDrawingCreatorState createState() => _LineDrawingCreatorState(); } @@ -41,6 +50,12 @@ class _LineDrawingCreatorState extends State { /// Saved starting Y coordinates. double? _startingYPoint; + /// Saved ending epoch. + int? _endingEpoch; + + /// Saved ending Y coordinates. + double? _endingYPoint; + /// If drawing has been started. bool _isPenDown = false; @@ -73,40 +88,55 @@ class _LineDrawingCreatorState extends State { setState(() { position = details.localPosition; if (!_isPenDown) { + /// Draw the initial point of the line. _startingEpoch = epochFromX!(position!.dx); _startingYPoint = widget.quoteFromCanvasY(position!.dy); _isPenDown = true; _drawingId = 'line_$_startingEpoch'; _drawingParts.add(LineDrawing( - drawingPart: 'marker', + drawingPart: DrawingParts.marker, startEpoch: _startingEpoch!, startYCoord: _startingYPoint!, )); } else if (!_isDrawingFinished) { + /// Draw final point and the whole line. _isPenDown = false; _isDrawingFinished = true; - final int endEpoch = epochFromX!(position!.dx); - final double endYPoint = widget.quoteFromCanvasY(position!.dy); - - _drawingParts.addAll([ - LineDrawing( - drawingPart: 'marker', - startEpoch: endEpoch, - startYCoord: endYPoint, - ), - LineDrawing( - drawingPart: 'line', - startEpoch: _startingEpoch!, - startYCoord: _startingYPoint!, - endEpoch: endEpoch, - endYCoord: endYPoint, - ) - ]); + _endingEpoch = epochFromX!(position!.dx); + _endingYPoint = widget.quoteFromCanvasY(position!.dy); + + /// Checks if the initial point and the final point are the same. + if (Offset(_startingEpoch!.toDouble(), _startingYPoint!.toDouble()) == + Offset(_endingEpoch!.toDouble(), _endingYPoint!.toDouble())) { + /// If the initial point and the final point are the same, + /// remove the drawing and cleazn the drawing tool selection. + widget.removeDrawing(_drawingId); + widget.clearDrawingToolSelection(); + return; + } else { + /// If the initial point and the final point are not the same, + /// draw the final point and the whole line. + _drawingParts.addAll([ + LineDrawing( + drawingPart: DrawingParts.marker, + endEpoch: _endingEpoch!, + endYCoord: _endingYPoint!, + ), + LineDrawing( + drawingPart: DrawingParts.line, + startEpoch: _startingEpoch!, + startYCoord: _startingYPoint!, + endEpoch: _endingEpoch!, + endYCoord: _endingYPoint!, + ) + ]); + } } widget.onAddDrawing( - >{_drawingId: _drawingParts}, - isDrawingFinished: _isDrawingFinished); + >{_drawingId: _drawingParts}, + isDrawingFinished: _isDrawingFinished, + ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index d7924c5ae..c7b35f05a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -1,7 +1,14 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; -import '../drawing.dart'; /// Vertical drawing tool. A vertical is a vertical line defined by one point /// that is infinite in both directions. @@ -14,7 +21,7 @@ class VerticalDrawing extends Drawing { }); /// Part of a drawing: 'vertical' - final String drawingPart; + final DrawingParts drawingPart; /// Starting epoch. final int epoch; @@ -22,33 +29,68 @@ class VerticalDrawing extends Drawing { /// Starting Y coordinates. final double yCoord; + /// Keeps the latest position of the start of drawing + Point? startPoint; + /// Paint @override void onPaint( - Canvas canvas, - Size size, - ChartTheme theme, - double Function(int x) epochToX, - double Function(double y) quoteToY, - DrawingToolConfig config) { - final LineStyle lineStyle = config.toJson()['lineStyle']; - final String pattern = config.toJson()['pattern']; - final double startQuoteToY = quoteToY(yCoord); + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final DrawingPaintStyle paint = DrawingPaintStyle(); + final VerticalDrawingToolConfig config = + drawingData.config as VerticalDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; - if (drawingPart == 'vertical') { - final double xCoord = epochToX(epoch); + startPoint = draggableStartPoint.updatePosition( + epoch, + yCoord, + epochToX, + quoteToY, + ); - final double startY = startQuoteToY - 1000, - endingY = startQuoteToY + 1000; + final double xCoord = startPoint!.x; + final double startQuoteToY = startPoint!.y; - if (pattern == 'solid') { + if (drawingPart == DrawingParts.line) { + final double startY = startQuoteToY - 10000, + endingY = startQuoteToY + 10000; + + if (pattern == DrawingPatterns.solid) { canvas.drawLine( - Offset(xCoord, startY), - Offset(xCoord, endingY), - Paint() - ..color = lineStyle.color - ..strokeWidth = lineStyle.thickness); + Offset(xCoord, startY), + Offset(xCoord, endingY), + drawingData.isSelected + ? paint.glowyLinePaintStyle(lineStyle.color, lineStyle.thickness) + : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), + ); } } } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final LineStyle lineStyle = config.toJson()['lineStyle']; + + return position.dx > startPoint!.x - lineStyle.thickness - 5 && + position.dx < startPoint!.x + lineStyle.thickness + 5; + } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart index 005d413a9..2569bb8e1 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:flutter/material.dart'; @@ -73,14 +74,15 @@ class _VerticalDrawingCreatorState extends State { _isDrawingFinished = true; _drawingParts.add(VerticalDrawing( - drawingPart: 'vertical', + drawingPart: DrawingParts.line, epoch: _startingEpoch!, yCoord: _startingYPoint!, )); widget.onAddDrawing( - >{_drawingId: _drawingParts}, - isDrawingFinished: _isDrawingFinished); + >{_drawingId: _drawingParts}, + isDrawingFinished: _isDrawingFinished, + ); }); } diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index beb2c95c2..5f7f9e66b 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -5,11 +5,10 @@ import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_data_pai import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_area.dart'; import 'package:deriv_chart/src/deriv_chart/chart/loading_animation.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -26,6 +25,7 @@ class MainChart extends BasicChart { MainChart({ required DataSeries mainSeries, required this.onAddDrawing, + required this.clearDrawingToolSelection, this.drawings, this.selectedDrawingTool, this.isLive = false, @@ -74,6 +74,9 @@ class MainChart extends BasicChart { /// Selected drawing tool. final DrawingToolConfig? selectedDrawingTool; + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + /// The function that gets called on crosshair appearance. final VoidCallback? onCrosshairAppeared; @@ -117,6 +120,8 @@ class _ChartImplementationState extends BasicChartState { /// The current animation value of crosshair zoom out. late Animation crosshairZoomOutAnimation; + bool _isDrawingMoving = false; + @override double get verticalPadding { if (canvasSize == null) { @@ -284,19 +289,16 @@ class _ChartImplementationState extends BasicChartState { quoteToCanvasY: chartQuoteToCanvasY, ), ), - if (widget.drawings != null) - ...widget.drawings! - .map((DrawingData drawingData) => DrawingPainter( - drawingData: drawingData, - quoteToCanvasY: chartQuoteToCanvasY, - )), - if (widget.selectedDrawingTool != null) - DrawingCreator( - onAddDrawing: widget.onAddDrawing, - selectedDrawingTool: widget.selectedDrawingTool!, - quoteFromCanvasY: chartQuoteFromCanvasY, - ), - _buildCrosshairArea(), + DrawingToolChart( + drawings: widget.drawings, + onAddDrawing: widget.onAddDrawing, + onMoveDrawing: _onMoveDrawing, + selectedDrawingTool: widget.selectedDrawingTool, + clearDrawingToolSelection: widget.clearDrawingToolSelection, + chartQuoteToCanvasY: chartQuoteToCanvasY, + chartQuoteFromCanvasY: chartQuoteFromCanvasY, + ), + if (!_isDrawingMoving) _buildCrosshairArea(), if (_isScrollToLastTickAvailable) Positioned( bottom: 0, @@ -427,4 +429,10 @@ class _ChartImplementationState extends BasicChartState { maxQuote = safeMax(maxQuote, widget.chartDataList.getMaxValue()); return [minQuote, maxQuote]; } + + void _onMoveDrawing({bool isDrawingMoved = false}) { + setState(() { + _isDrawingMoving = isDrawingMoved; + }); + } } diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index 86fc56583..d5b581223 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -184,6 +184,7 @@ class _DerivChartState extends State { drawings: _drawings, onAddDrawing: _onAddDrawing, selectedDrawingTool: _selectedDrawingTool, + clearDrawingToolSelection: _clearDrawingToolSelection, markerSeries: widget.markerSeries, theme: widget.theme, onCrosshairAppeared: widget.onCrosshairAppeared, @@ -217,6 +218,15 @@ class _DerivChartState extends State { child: IconButton( icon: const Icon(Icons.drive_file_rename_outline_outlined), onPressed: () { + /// Remove unfinished drawings before openning the dialog. + /// For the scenario where the user adds part of a drawing + /// and then opens the dialog. + setState(() { + _drawings.removeWhere( + (DrawingData data) => !data.isDrawingFinished); + _selectedDrawingTool = null; + }); + showDialog( context: context, builder: ( @@ -270,16 +280,32 @@ class _DerivChartState extends State { _drawings.add(DrawingData( id: drawingId, config: _selectedDrawingTool!, - drawings: addedDrawing.values.first, + drawingParts: addedDrawing.values.first, + isDrawingFinished: isDrawingFinished, )); } else { - existingDrawing.updateDrawingList(addedDrawing.values.first); + existingDrawing + ..updateDrawingPartList(addedDrawing.values.first) + ..isSelected = true + ..isDrawingFinished = isDrawingFinished; } if (isDrawingFinished) { _drawingToolsRepo.add(_selectedDrawingTool!); _selectedDrawingTool = null; } + + if (_drawings.length > 1) { + _drawings.removeWhere((DrawingData data) => + data.id != drawingId && !data.isDrawingFinished); + } + }); + } + + /// Clean the drawing tool selection. + void _clearDrawingToolSelection() { + setState(() { + _selectedDrawingTool = null; }); } } diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart new file mode 100644 index 000000000..d297c5e5e --- /dev/null +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -0,0 +1,84 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; + +/// A wigdet for encapsulating drawing tools related business logic +class DrawingToolChart extends StatelessWidget { + /// Creates chart that expands to available space. + const DrawingToolChart({ + required this.onAddDrawing, + required this.chartQuoteFromCanvasY, + required this.chartQuoteToCanvasY, + required this.onMoveDrawing, + required this.clearDrawingToolSelection, + this.drawings, + this.selectedDrawingTool, + Key? key, + }) : super(key: key); + + /// Existing drawings. + final List? drawings; + + /// Callback to pass new drawing to the parent. + final void Function(Map> addedDrawing, + {bool isDrawingFinished}) onAddDrawing; + + /// Callback to pass new drawing to the parent. + final void Function({bool isDrawingMoved}) onMoveDrawing; + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Selected drawing tool. + final DrawingToolConfig? selectedDrawingTool; + + /// Conversion function for converting quote from chart's canvas' Y position. + final double Function(double) chartQuoteFromCanvasY; + + /// Conversion function for converting quote to chart's canvas' Y position. + final double Function(double) chartQuoteToCanvasY; + + /// Sets drawing as selected and unselects the rest of drawings + void _setIsDrawingSelected(DrawingData drawing) { + drawing.isSelected = !drawing.isSelected; + + for (final DrawingData data in drawings!) { + if (data.id != drawing.id) { + data.isSelected = false; + } + } + } + + /// Removes specific drawing from the list of drawings + void removeDrawing(String drawingId) { + drawings!.removeWhere((DrawingData data) => data.id == drawingId); + } + + @override + Widget build(BuildContext context) => ClipRect( + child: Stack( + fit: StackFit.expand, + children: [ + if (drawings != null) + ...drawings!.map((DrawingData drawingData) => DrawingPainter( + drawingData: drawingData, + quoteToCanvasY: chartQuoteToCanvasY, + quoteFromCanvasY: chartQuoteFromCanvasY, + onMoveDrawing: onMoveDrawing, + setIsDrawingSelected: _setIsDrawingSelected, + )), + if (selectedDrawingTool != null) + DrawingCreator( + onAddDrawing: onAddDrawing, + selectedDrawingTool: selectedDrawingTool!, + quoteFromCanvasY: chartQuoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, + ), + ], + ), + ); +} From 35573846434122bc965a1e8b75ded273752f0c9f Mon Sep 17 00:00:00 2001 From: Bahar Date: Fri, 4 Aug 2023 11:35:30 +0800 Subject: [PATCH 08/29] bahar/feat: add_continuous_drawing_tool (#229) - Add continuous drawing tool --- example/lib/main.dart | 4 +- .../continuous_drawing_tool_config.dart | 49 ++++++ .../continuous_drawing_tool_config.g.dart | 30 ++++ .../continuous_drawing_tool_item.dart | 75 +++++++++ .../drawing_tools_ui/drawing_tool_config.dart | 3 + .../drawing_tools_ui/drawing_tool_item.dart | 4 +- .../drawing_tools_dialog.dart | 31 ++-- .../line/line_drawing_tool_item.dart | 4 +- .../vertical/vertical_drawing_tool_item.dart | 4 +- lib/src/deriv_chart/chart/chart.dart | 29 +--- .../continuous_drawing_creator.dart | 119 +++++++++++++++ .../continuous/continuous_line_drawing.dart | 93 ++++++++++++ .../data_model/draggable_edge_point.dart | 64 +++++--- .../data_model/drawing_paint_style.dart | 7 +- .../drawing_tools/data_model/edge_point.dart | 19 +++ .../drawing_tools/data_model/point.dart | 3 +- .../drawing_tools/drawing.dart | 10 +- .../drawing_tools/drawing_creator.dart | 136 +++++++++++------ .../drawing_tools/drawing_painter.dart | 85 +++++++++-- .../drawing_tools/drawing_tool_widget.dart | 79 ++++++++++ .../drawing_tools/line/line_drawing.dart | 142 ++++++++++++------ .../line/line_drawing_creator.dart | 141 ++++++----------- .../vertical/vertical_drawing.dart | 26 ++-- .../vertical/vertical_drawing_creator.dart | 99 ++++-------- lib/src/deriv_chart/chart/main_chart.dart | 41 +---- lib/src/deriv_chart/deriv_chart.dart | 87 +---------- .../drawing_tool_chart.dart | 64 +++----- .../drawing_tool_chart/drawing_tools.dart | 107 +++++++++++++ 28 files changed, 1052 insertions(+), 503 deletions(-) create mode 100644 lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart create mode 100644 lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index a2c7ec6b2..c03683f98 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -372,12 +372,12 @@ class _FullscreenChartState extends State { DataSeries _getDataSeries(ChartStyle style) { if (ticks is List) { switch (style) { - case ChartStyle.candles: - return CandleSeries(ticks as List); case ChartStyle.hollow: return HollowCandleSeries(ticks as List); case ChartStyle.ohlc: return OhlcCandleSeries(ticks as List); + default: + return CandleSeries(ticks as List); } } return LineSeries(ticks, style: const LineStyle(hasArea: true)) diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart new file mode 100644 index 000000000..b7cfcf0da --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart @@ -0,0 +1,49 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'continuous_drawing_tool_item.dart'; + +part 'continuous_drawing_tool_config.g.dart'; + +/// Continuous drawing tool config +@JsonSerializable() +class ContinuousDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const ContinuousDrawingToolConfig({ + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = DrawingPatterns.solid, + }) : super(); + + /// Initializes from JSON. + factory ContinuousDrawingToolConfig.fromJson(Map json) => + _$ContinuousDrawingToolConfigFromJson(json); + + /// Drawing tool name + static const String name = 'dt_continuous'; + + @override + Map toJson() => _$ContinuousDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + // TODO(maryia-binary): implement 'dotted' and 'dashed' patterns + final DrawingPatterns pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + ContinuousDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart new file mode 100644 index 000000000..85e73ac68 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'continuous_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ContinuousDrawingToolConfig _$ContinuousDrawingToolConfigFromJson( + Map json) => + ContinuousDrawingToolConfig( + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); + +Map _$ContinuousDrawingToolConfigToJson( + ContinuousDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart new file mode 100644 index 000000000..4cc4561be --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart @@ -0,0 +1,75 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; +import 'continuous_drawing_tool_config.dart'; +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; + +/// Continuous drawing tool item in the list of drawing tools +class ContinuousDrawingToolItem extends DrawingToolItem { + /// Initializes + const ContinuousDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + ContinuousDrawingToolConfig config = const ContinuousDrawingToolConfig(), + }) : super( + key: key, + title: 'Continuous', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createDrawingToolItemState() => + ContinuousDrawingToolItemState(); +} + +/// ContinuousDrawingToolItem State class +class ContinuousDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _lineStyle; + DrawingPatterns? _pattern; + + @override + ContinuousDrawingToolConfig createDrawingToolConfig() => + ContinuousDrawingToolConfig( + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField(), + // TODO(maryia-binary): implement _buildPatternField() to set pattern + ], + ); + + Widget _buildColorField() => Row( + children: [ + Text( + ChartLocalization.of(context).labelColor, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: _currentLineStyle.color, + onColorChanged: (Color selectedColor) { + setState(() { + _lineStyle = _currentLineStyle.copyWith(color: selectedColor); + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as ContinuousDrawingToolConfig).lineStyle; + + DrawingPatterns get _currentPattern => + _pattern ?? (widget.config as ContinuousDrawingToolConfig).pattern; +} diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index b902a3933..f74da15b4 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:flutter/material.dart'; import 'line/line_drawing_tool_config.dart'; @@ -19,6 +20,8 @@ abstract class DrawingToolConfig extends AddOnConfig { } switch (json[nameKey]) { + case ContinuousDrawingToolConfig.name: + return ContinuousDrawingToolConfig.fromJson(json); case LineDrawingToolConfig.name: return LineDrawingToolConfig.fromJson(json); case VerticalDrawingToolConfig.name: diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart index 975593e98..3a09c640f 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart @@ -29,11 +29,11 @@ abstract class DrawingToolItem extends StatefulWidget { @override DrawingToolItemState createState() => - createIndicatorItemState(); + createDrawingToolItemState(); /// Create state object for this widget @protected - DrawingToolItemState createIndicatorItemState(); + DrawingToolItemState createDrawingToolItemState(); } /// State class of [DrawingToolItem] diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index 5f0a18d33..9e8b66c39 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -1,6 +1,8 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/widgets/animated_popup.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -11,21 +13,13 @@ import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; class DrawingToolsDialog extends StatefulWidget { /// Creates drawing tools dialog. const DrawingToolsDialog({ - required this.onDrawingToolSelection(DrawingToolConfig selectedDrawingTool), - required this.onDrawingToolRemoval(int index), - required this.onDrawingToolUpdate( - int index, DrawingToolConfig updatedConfig), + required this.drawingTools, Key? key, }) : super(key: key); - /// Callback to inform parent about drawing tool removal. - final void Function(int) onDrawingToolRemoval; - - /// Callback to inform parent about drawing tool selection. - final void Function(DrawingToolConfig) onDrawingToolSelection; - - /// Callback to inform parent about drawing tool update. - final void Function(int, DrawingToolConfig) onDrawingToolUpdate; + /// Keep the reference to the drawing tools class for + /// sharing data between the DerivChart and the DrawingToolsDialog + final DrawingTools drawingTools; @override _DrawingToolsDialogState createState() => _DrawingToolsDialogState(); @@ -47,8 +41,12 @@ class _DrawingToolsDialogState extends State { children: [ DropdownButton( value: _selectedDrawingTool, - hint: Text(ChartLocalization.of(context)!.selectDrawingTool), + hint: Text(ChartLocalization.of(context).selectDrawingTool), items: const >[ + DropdownMenuItem( + child: Text('Continuous'), + value: ContinuousDrawingToolConfig(), + ), DropdownMenuItem( child: Text('Line'), value: LineDrawingToolConfig(), @@ -71,7 +69,8 @@ class _DrawingToolsDialogState extends State { onPressed: _selectedDrawingTool != null && _selectedDrawingTool is DrawingToolConfig ? () { - widget.onDrawingToolSelection(_selectedDrawingTool!); + widget.drawingTools + .onDrawingToolSelection(_selectedDrawingTool!); Navigator.of(context).pop(); } : null, @@ -85,11 +84,11 @@ class _DrawingToolsDialogState extends State { itemBuilder: (BuildContext context, int index) => repo.getAddOns()[index].getItem( (DrawingToolConfig updatedConfig) { - widget.onDrawingToolUpdate(index, updatedConfig); + widget.drawingTools.onDrawingToolUpdate(index, updatedConfig); repo.updateAt(index, updatedConfig); }, () { - widget.onDrawingToolRemoval(index); + widget.drawingTools.onDrawingToolRemoval(index); repo.removeAt(index); }, ), diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart index 48501e464..f72d4425e 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart @@ -24,7 +24,7 @@ class LineDrawingToolItem extends DrawingToolItem { ); @override - DrawingToolItemState createIndicatorItemState() => + DrawingToolItemState createDrawingToolItemState() => LineDrawingToolItemState(); } @@ -51,7 +51,7 @@ class LineDrawingToolItemState Widget _buildColorField() => Row( children: [ Text( - ChartLocalization.of(context)!.labelColor, + ChartLocalization.of(context).labelColor, style: const TextStyle(fontSize: 16), ), ColorSelector( diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart index 5b23ac662..6f331c891 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart @@ -27,7 +27,7 @@ class VerticalDrawingToolItem extends DrawingToolItem { ); @override - DrawingToolItemState createIndicatorItemState() => + DrawingToolItemState createDrawingToolItemState() => VerticalDrawingToolItemState(); } @@ -54,7 +54,7 @@ class VerticalDrawingToolItemState Widget _buildColorField() => Row( children: [ Text( - ChartLocalization.of(context)!.labelColor, + ChartLocalization.of(context).labelColor, style: const TextStyle(fontSize: 16), ), ColorSelector( diff --git a/lib/src/deriv_chart/chart/chart.dart b/lib/src/deriv_chart/chart/chart.dart index bd35eff37..760262024 100644 --- a/lib/src/deriv_chart/chart/chart.dart +++ b/lib/src/deriv_chart/chart/chart.dart @@ -1,14 +1,12 @@ import 'package:deriv_chart/deriv_chart.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/misc/callbacks.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'bottom_chart.dart'; import 'data_visualization/chart_data.dart'; import 'main_chart.dart'; @@ -19,10 +17,7 @@ class Chart extends StatefulWidget { const Chart({ required this.mainSeries, required this.granularity, - required this.onAddDrawing, - required this.clearDrawingToolSelection, - this.drawings, - this.selectedDrawingTool, + required this.drawingTools, this.pipSize = 4, this.controller, this.overlaySeries, @@ -52,18 +47,9 @@ class Chart extends StatefulWidget { /// Open position marker series. final MarkerSeries? markerSeries; - /// Existing drawings. - final List? drawings; - - /// Callback to pass new drawing to the parent. - final void Function(Map> addedDrawing, - {bool isDrawingFinished}) onAddDrawing; - - /// Selected drawing tool. - final DrawingToolConfig? selectedDrawingTool; - - /// Callback to clean drawing tool selection. - final VoidCallback clearDrawingToolSelection; + /// Keep the reference to the drawing tools class for + /// sharing data between the DerivChart and the DrawingToolsDialog + final DrawingTools drawingTools; /// Chart's controller final ChartController? controller; @@ -172,10 +158,7 @@ class _ChartState extends State with WidgetsBindingObserver { Expanded( flex: 3, child: MainChart( - drawings: widget.drawings, - onAddDrawing: widget.onAddDrawing, - selectedDrawingTool: widget.selectedDrawingTool, - clearDrawingToolSelection: widget.clearDrawingToolSelection, + drawingTools: widget.drawingTools, controller: _controller, mainSeries: widget.mainSeries, overlaySeries: widget.overlaySeries, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart new file mode 100644 index 000000000..369d120a6 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart @@ -0,0 +1,119 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:flutter/material.dart'; +import '../data_model/drawing_parts.dart'; + +/// Creates a Continuous drawing piece by piece collected on every gesture +/// exists in a widget tree starting from selecting a continuous drawing tool +/// and until drawing should be finished. +class ContinuousDrawingCreator extends DrawingCreator { + /// Initializes the continuous drawing creator. + const ContinuousDrawingCreator({ + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, + required this.shouldStopDrawing, + Key? key, + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + + /// A flag to show when to stop drawing only for drawings which don't have + /// fixed number of points like continuous drawing + final bool shouldStopDrawing; + + @override + DrawingCreatorState createState() => + _ContinuousDrawingCreatorState(); +} + +class _ContinuousDrawingCreatorState + extends DrawingCreatorState { + @override + void onTap(TapUpDetails details) { + super.onTap(details); + final ContinuousDrawingCreator _widget = widget as ContinuousDrawingCreator; + + if (_widget.shouldStopDrawing) { + return; + } else { + isDrawingFinished = false; + } + setState(() { + position = details.localPosition; + tapCount++; + final int currentTap = tapCount - 1; + final int previousTap = tapCount - 2; + + if (edgePoints.isEmpty) { + /// Draw the initial point of the continuous. + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + drawingParts.add(ContinuousLineDrawing( + drawingPart: DrawingParts.marker, + startEdgePoint: edgePoints.first, + )); + } else if (!isDrawingFinished) { + /// Draw other points and the whole continuous drawing. + + isDrawingFinished = true; + + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + /// Checks if the initial point and the 2nd points are the same. + if (edgePoints[1] == edgePoints.first) { + /// If the initial point and the 2nd point are the same, + /// remove the drawing and clean the drawing tool selection. + _widget.removeDrawing(drawingId); + _widget.clearDrawingToolSelection(); + return; + } else { + /// If the initial point and the final point are not the same, + /// draw the final point and the whole drawing. + if (tapCount > 2) { + drawingParts = []; + + drawingParts.add(ContinuousLineDrawing( + drawingPart: DrawingParts.marker, + startEdgePoint: edgePoints[previousTap], + )); + } + drawingParts.addAll([ + ContinuousLineDrawing( + drawingPart: DrawingParts.marker, + endEdgePoint: edgePoints[currentTap], + ), + ContinuousLineDrawing( + drawingPart: DrawingParts.line, + startEdgePoint: edgePoints[previousTap], + endEdgePoint: edgePoints[currentTap], + ) + ]); + } + } + + widget.onAddDrawing( + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, + isInfiniteDrawing: true, + ); + }); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart new file mode 100644 index 000000000..1b79306e4 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -0,0 +1,93 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Line drawing tool. A line is a vector defined by two points that is +/// infinite in both directions. +class ContinuousLineDrawing extends Drawing { + /// Initializes + ContinuousLineDrawing({ + required DrawingParts drawingPart, + EdgePoint startEdgePoint = const EdgePoint(), + EdgePoint endEdgePoint = const EdgePoint(), + bool exceedStart = false, + bool exceedEnd = false, + }) : _lineDrawing = LineDrawing( + drawingPart: drawingPart, + startEdgePoint: startEdgePoint, + endEdgePoint: endEdgePoint, + exceedStart: exceedStart, + exceedEnd: exceedEnd); + + final LineDrawing _lineDrawing; + + /// Paint the line + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final ContinuousDrawingToolConfig config = + drawingData.config as ContinuousDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; + + _lineDrawing.onPaint( + canvas, + size, + theme, + epochToX, + quoteToY, + DrawingData( + id: drawingData.id, + config: LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), + drawingParts: drawingData.drawingParts, + isDrawingFinished: drawingData.isDrawingFinished, + isSelected: drawingData.isSelected, + ), + updatePositionCallback, + draggableStartPoint, + draggableEndPoint: draggableEndPoint); + } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen, for any of the edge points + /// it will call "setIsEdgeDragged" callback function to determine which + /// point is clicked + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, + }) => + _lineDrawing.hitTest(position, epochToX, quoteToY, config, + draggableStartPoint, setIsStartPointDragged, + draggableEndPoint: draggableEndPoint, + setIsEndPointDragged: setIsEndPointDragged); +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart index 0d9ec38f7..d363ce869 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; @@ -8,44 +9,71 @@ import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; /// And we want to hanle difftent types of drag events on them. /// For example with dots are draggable edge points for the line /// ⎯⎯⚪️⎯⎯⎯⚪️⎯⎯ -class DraggableEdgePoint { +class DraggableEdgePoint extends EdgePoint { + /// Initializes + DraggableEdgePoint({ + int epoch = 0, + double quote = 0, + this.isDrawingDragged = false, + this.isDragged = false, + }) : super(epoch: epoch, quote: quote); + /// Represents whether the whole drawing is currently being dragged or not - bool isDrawingDragged = false; + final bool isDrawingDragged; /// Represents whether the edge point is currently being dragged or not - bool isDragged = false; + final bool isDragged; /// Holds the current position of the edge point when it is being dragged. - Offset draggedPosition = Offset.zero; + Offset _draggedPosition = Offset.zero; /// A callback method that takes the relative x and y positions as parameter, /// sets the draggedPosition field to its value and return epoch and quote /// values. - Point updatePosition(int epoch, double yCoord, - double Function(int x) epochToX, double Function(double y) quoteToY) { + Point updatePosition( + int epoch, + double yCoord, + double Function(int x) epochToX, + double Function(double y) quoteToY, + ) { final Offset oldPosition = Offset(epoch.toDouble(), yCoord); - draggedPosition = isDrawingDragged ? draggedPosition : oldPosition; + _draggedPosition = isDrawingDragged ? _draggedPosition : oldPosition; - final double xCoord = epochToX(draggedPosition.dx.toInt()); - final double quoteY = quoteToY(draggedPosition.dy); + final double x = epochToX(_draggedPosition.dx.toInt()); + final double y = quoteToY(_draggedPosition.dy); - return Point(x: xCoord, y: quoteY); + return Point(x: x, y: y); } /// A method that takes the gesture delta Offset object as parameter /// and sets the draggedPosition field to its value. void updatePositionWithLocalPositions( - Offset delta, - XAxisModel xAxis, - double Function(double) quoteFromCanvasY, - double Function(double y) quoteToY, - {required bool isOtherEndDragged}) { + Offset delta, + XAxisModel xAxis, + double Function(double) quoteFromCanvasY, + double Function(double y) quoteToY, { + required bool isOtherEndDragged, + }) { final Offset localPosition = Offset( - xAxis.xFromEpoch(draggedPosition.dx.toInt()), - quoteToY(draggedPosition.dy)) + + xAxis.xFromEpoch(_draggedPosition.dx.toInt()), + quoteToY(_draggedPosition.dy)) + (isOtherEndDragged ? Offset.zero : delta); - draggedPosition = Offset(xAxis.epochFromX(localPosition.dx).toDouble(), + _draggedPosition = Offset(xAxis.epochFromX(localPosition.dx).toDouble(), quoteFromCanvasY(localPosition.dy)); } + + /// Creates a copy of this object. + DraggableEdgePoint copyWith({ + int? epoch, + double? quote, + bool? isDrawingDragged, + bool? isDragged, + }) => + DraggableEdgePoint( + epoch: epoch ?? this.epoch, + quote: quote ?? this.quote, + isDrawingDragged: isDrawingDragged ?? this.isDrawingDragged, + isDragged: isDragged ?? this.isDragged, + ).._draggedPosition = _draggedPosition; } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart index 94538edad..174b24143 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart @@ -6,8 +6,7 @@ class DrawingPaintStyle { Paint glowyLinePaintStyle(Color color, double thickness) => Paint() ..color = color ..strokeWidth = thickness + 3 - ..strokeCap = StrokeCap.round - ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 10); + ..strokeCap = StrokeCap.round; /// Returns the paint style of the the line Paint linePaintStyle(Color color, double thickness) => Paint() @@ -15,9 +14,7 @@ class DrawingPaintStyle { ..strokeWidth = thickness; /// Returns the paint style of the circle marker - Paint glowyCirclePaintStyle(Color color) => Paint() - ..color = color - ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 10); + Paint glowyCirclePaintStyle(Color color) => Paint()..color = color; /// Returns the paint style of the circle marker Paint transparentCirclePaintStyle() => Paint()..color = Colors.transparent; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart new file mode 100644 index 000000000..c5798968c --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; + +/// A class that holds epoch and yCoord of the edge points. +class EdgePoint with EquatableMixin { + /// Initializes + const EdgePoint({ + this.epoch = 0, + this.quote = 0, + }); + + /// Epoch. + final int epoch; + + /// Quote. + final double quote; + + @override + List get props => [epoch, quote]; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart index 12c18f4b1..0fee4a3a3 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart @@ -1,7 +1,8 @@ import 'dart:math'; import 'dart:ui'; -///A class that holds point data +/// A class that holds point data +/// This class is equivalent to Offest but with customized methods class Point { /// Initializes const Point({ diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index a5ec37dcb..9f6925a17 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -1,4 +1,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; @@ -15,6 +17,10 @@ abstract class Drawing { double Function(int x) epochToX, double Function(double y) quoteToY, DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { DraggableEdgePoint? draggableEndPoint, }); @@ -46,7 +52,9 @@ abstract class Drawing { double Function(int x) epochToX, double Function(double y) quoteToY, DrawingToolConfig config, - DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart index d0a09e5be..cce8a9db2 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart @@ -1,61 +1,109 @@ +import 'dart:math' as math; + +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:provider/provider.dart'; + +/// This is a function type used as a callback to pass a newly created drawing +/// of type [T] to the parent. +typedef OnAddDrawing = void Function( + String drawingId, + List drawingParts, { + bool isDrawingFinished, + bool isInfiniteDrawing, +}); -/// Creates a drawing piece by piece collected on every gesture -/// exists in a widget tree starting from selecting a selectedDrawingTool and -/// until drawing is finished -class DrawingCreator extends StatelessWidget { - /// Initializes drawing creator area. +/// This class is an abstract representation of a drawing creator, and it's a +/// [StatefulWidget]. It is generic, with the type parameter [T] constrained to +/// be a subclass of Drawing. +abstract class DrawingCreator extends StatefulWidget { + /// Initializes. const DrawingCreator({ required this.onAddDrawing, - required this.selectedDrawingTool, required this.quoteFromCanvasY, - required this.clearDrawingToolSelection, - required this.removeDrawing, Key? key, }) : super(key: key); - /// Selected drawing tool. - final DrawingToolConfig selectedDrawingTool; + /// A required callback property of type [OnAddDrawing] that is used to + /// pass the newly created drawing to the parent. + final OnAddDrawing onAddDrawing; - /// Callback to pass a newly created drawing to the parent. - final void Function(Map> addedDrawing, - {bool isDrawingFinished}) onAddDrawing; - - /// Conversion function for converting quote to chart's canvas' Y position. + /// Conversion function for converting quote from chart's canvas' Y position. final double Function(double) quoteFromCanvasY; - /// Callback to clean drawing tool selection. - final VoidCallback clearDrawingToolSelection; + @override + DrawingCreatorState createState(); +} + +/// This class is an abstract representation of the state associated with the +/// DrawingCreator. It extends [State>] and is generic with +/// the type parameter [T] constrained to be a subclass of Drawing. +abstract class DrawingCreatorState + extends State> { + /// Gesture manager state. + late final GestureManagerState _gestureManager; + + /// Parts of a particular drawing, e.g. marker, line etc. + @protected + List drawingParts = []; - /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + /// Tapped position. + @protected + Offset? position; + + /// Keeps track of how many times user tapped on the chart. + @protected + int tapCount = 0; + + /// Keeps the points tapped by the user to draw the continuous drawing. + final List edgePoints = []; + + /// Unique drawing id. + @protected + String drawingId = ''; + + /// If drawing has been finished. + @protected + bool isDrawingFinished = false; + + /// Get epoch from x. + @protected + int Function(double x)? epochFromX; @override - Widget build(BuildContext context) { - // TODO(bahar-deriv): Deligate the creation of drawing to the specific - // drawing tool config - final String drawingToolType = selectedDrawingTool.toJson()['name']; - - switch (drawingToolType) { - case 'dt_line': - return LineDrawingCreator( - onAddDrawing: onAddDrawing, - quoteFromCanvasY: quoteFromCanvasY, - clearDrawingToolSelection: clearDrawingToolSelection, - removeDrawing: removeDrawing, - ); - case 'dt_vertical': - return VerticalDrawingCreator( - onAddDrawing: onAddDrawing, - quoteFromCanvasY: quoteFromCanvasY, - ); - // TODO(maryia-binary): add the rest of drawing tools here - default: - return Container(); - } + void initState() { + super.initState(); + _gestureManager = context.read() + ..registerCallback(onTap); } + + @override + void dispose() { + _gestureManager.removeCallback(onTap); + super.dispose(); + } + + /// Returns the specific drawing id base on selected drawing tool name. + void generateDrawingId(String drawingToolName) { + drawingId = '${drawingToolName}_${math.Random().nextInt(1000)}'; + } + + /// Catches each single click on the chart to create a drawing. + void onTap(TapUpDetails details) { + generateDrawingId(runtimeType.toString()); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + final XAxisModel xAxis = context.watch(); + epochFromX = xAxis.epochFromX; + } + + @override + Widget build(BuildContext context) => const SizedBox.shrink(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index 0909bdcb9..6aa151fae 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -1,4 +1,7 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; @@ -17,9 +20,13 @@ class DrawingPainter extends StatefulWidget { required this.quoteFromCanvasY, required this.onMoveDrawing, required this.setIsDrawingSelected, + required this.selectedDrawingTool, Key? key, }) : super(key: key); + /// Selected drawing tool. + final DrawingToolConfig? selectedDrawingTool; + /// Contains each drawing data final DrawingData? drawingData; @@ -42,8 +49,8 @@ class DrawingPainter extends StatefulWidget { class _DrawingPainterState extends State { bool _isDrawingDragged = false; - final DraggableEdgePoint _draggableStartPoint = DraggableEdgePoint(); - final DraggableEdgePoint _draggableEndPoint = DraggableEdgePoint(); + DraggableEdgePoint _draggableStartPoint = DraggableEdgePoint(); + DraggableEdgePoint _draggableEndPoint = DraggableEdgePoint(); Offset? _previousPosition; @override @@ -55,18 +62,20 @@ class _DrawingPainterState extends State { widget.drawingData!.isDrawingFinished) { setState(() { _isDrawingDragged = details.delta != Offset.zero; - _draggableStartPoint - ..isDrawingDragged = _isDrawingDragged - ..updatePositionWithLocalPositions( + + _draggableStartPoint = _draggableStartPoint.copyWith( + isDrawingDragged: _isDrawingDragged, + )..updatePositionWithLocalPositions( details.delta, xAxis, widget.quoteFromCanvasY, widget.quoteToCanvasY, isOtherEndDragged: _draggableEndPoint.isDragged, ); - _draggableEndPoint - ..isDrawingDragged = _isDrawingDragged - ..updatePositionWithLocalPositions( + + _draggableEndPoint = _draggableEndPoint.copyWith( + isDrawingDragged: _isDrawingDragged, + )..updatePositionWithLocalPositions( details.delta, xAxis, widget.quoteFromCanvasY, @@ -105,8 +114,12 @@ class _DrawingPainterState extends State { }, onLongPressUp: () { widget.onMoveDrawing(isDrawingMoved: false); - _draggableStartPoint.isDragged = false; - _draggableEndPoint.isDragged = false; + _draggableStartPoint = _draggableStartPoint.copyWith( + isDragged: false, + ); + _draggableEndPoint = _draggableEndPoint.copyWith( + isDragged: false, + ); }, onPanStart: (DragStartDetails details) { widget.onMoveDrawing(isDrawingMoved: true); @@ -116,8 +129,12 @@ class _DrawingPainterState extends State { }, onPanEnd: (DragEndDetails details) { setState(() { - _draggableStartPoint.isDragged = false; - _draggableEndPoint.isDragged = false; + _draggableStartPoint = _draggableStartPoint.copyWith( + isDragged: false, + ); + _draggableEndPoint = _draggableEndPoint.copyWith( + isDragged: false, + ); }); widget.onMoveDrawing(isDrawingMoved: false); }, @@ -128,7 +145,26 @@ class _DrawingPainterState extends State { epochToX: xAxis.xFromEpoch, quoteToY: widget.quoteToCanvasY, draggableStartPoint: _draggableStartPoint, + isDrawingToolSelected: widget.selectedDrawingTool != null, draggableEndPoint: _draggableEndPoint, + updatePositionCallback: ( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) => + draggableEdgePoint.updatePosition( + edgePoint.epoch, + edgePoint.quote, + xAxis.xFromEpoch, + widget.quoteToCanvasY, + ), + setIsStartPointDragged: ({required bool isDragged}) { + _draggableStartPoint = + _draggableStartPoint.copyWith(isDragged: isDragged); + }, + setIsEndPointDragged: ({required bool isDragged}) { + _draggableEndPoint = + _draggableEndPoint.copyWith(isDragged: isDragged); + }, ), size: const Size(double.infinity, double.infinity), ), @@ -144,15 +180,26 @@ class _DrawingPainter extends CustomPainter { required this.epochToX, required this.quoteToY, required this.draggableStartPoint, + required this.setIsStartPointDragged, + required this.updatePositionCallback, + this.isDrawingToolSelected = false, this.draggableEndPoint, + this.setIsEndPointDragged, }); final DrawingData drawingData; final ChartTheme theme; - double Function(int x) epochToX; - double Function(double y) quoteToY; - DraggableEdgePoint draggableStartPoint; - DraggableEdgePoint? draggableEndPoint; + final bool isDrawingToolSelected; + final double Function(int x) epochToX; + final double Function(double y) quoteToY; + final DraggableEdgePoint draggableStartPoint; + final DraggableEdgePoint? draggableEndPoint; + final void Function({required bool isDragged}) setIsStartPointDragged; + final void Function({required bool isDragged})? setIsEndPointDragged; + final Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback; @override void paint(Canvas canvas, Size size) { @@ -164,6 +211,7 @@ class _DrawingPainter extends CustomPainter { epochToX, quoteToY, drawingData, + updatePositionCallback, draggableStartPoint, draggableEndPoint: draggableEndPoint, ); @@ -185,8 +233,13 @@ class _DrawingPainter extends CustomPainter { quoteToY, drawingData.config, draggableStartPoint, + setIsStartPointDragged, draggableEndPoint: draggableEndPoint, + setIsEndPointDragged: setIsEndPointDragged, )) { + if (isDrawingToolSelected) { + return false; + } return true; } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart new file mode 100644 index 000000000..e1d2958d4 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -0,0 +1,79 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; + +/// The class acts as a bridge between the selected drawing tool and the +/// drawing creation process, delegating the specific creation logic to the +/// corresponding drawing tool configurations. +class DrawingToolWidget extends StatelessWidget { + /// Initializes drawing creator area. + const DrawingToolWidget({ + required this.onAddDrawing, + required this.selectedDrawingTool, + required this.quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, + this.shouldStopDrawing, + Key? key, + }) : super(key: key); + + /// Selected drawing tool. + final DrawingToolConfig selectedDrawingTool; + + /// Callback to pass a newly created drawing to the parent. + final void Function( + String drawingId, + List drawingParts, { + bool isDrawingFinished, + bool isInfiniteDrawing, + }) onAddDrawing; + + /// Conversion function for converting quote to chart's canvas' Y position. + final double Function(double) quoteFromCanvasY; + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + + /// A flag to show when to stop drawing only for drawings which don't have + /// fixed number of points like continuous drawing + final bool? shouldStopDrawing; + + @override + Widget build(BuildContext context) { + // TODO(bahar-deriv): Deligate the creation of drawing to the specific + // drawing tool config + final String drawingToolType = selectedDrawingTool.toJson()['name']; + + switch (drawingToolType) { + case 'dt_continuous': + return ContinuousDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, + shouldStopDrawing: shouldStopDrawing!, + ); + case 'dt_line': + return LineDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, + ); + case 'dt_vertical': + return VerticalDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + // TODO(maryia-binary): add the rest of drawing tools here + default: + return Container(); + } + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index 3bcbb0f52..a6e369a31 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -6,6 +6,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; @@ -19,26 +20,26 @@ class LineDrawing extends Drawing { /// Initializes LineDrawing({ required this.drawingPart, - this.startEpoch = 0, - this.startYCoord = 0, - this.endEpoch = 0, - this.endYCoord = 0, + this.startEdgePoint = const EdgePoint(), + this.endEdgePoint = const EdgePoint(), + this.exceedStart = false, + this.exceedEnd = false, }); /// Part of a drawing: 'marker' or 'line' final DrawingParts drawingPart; - /// Starting epoch. - final int startEpoch; + /// Starting point of drawing + final EdgePoint startEdgePoint; - /// Starting Y coordinates. - final double startYCoord; + /// Ending point of drawing + final EdgePoint endEdgePoint; - /// Ending epoch. - final int endEpoch; + /// If the line pass the start point. + final bool exceedStart; - /// Ending Y coordinates. - final double endYCoord; + /// If the line pass the end point. + final bool exceedEnd; /// Marker radius. final double markerRadius = 10; @@ -61,17 +62,52 @@ class LineDrawing extends Drawing { x1: endXCoord, y1: endQuoteToY, ); - if (vec.x0 > vec.x1) { - vec = Vector( - x0: endXCoord, - y0: endQuoteToY, - x1: startXCoord, - y1: startQuoteToY, - ); + + late double earlier, later; + if (exceedEnd && !exceedStart) { + earlier = vec.x0; + if (vec.x0 > vec.x1) { + later = vec.x1 - 1000; + } else { + later = vec.x1 + 1000; + } + } + if (exceedStart && !exceedEnd) { + later = vec.x1; + + if (vec.x0 > vec.x1) { + earlier = vec.x0 + 1000; + } else { + earlier = vec.x0 - 1000; + } + } + + if (exceedStart && exceedEnd) { + if (vec.x0 > vec.x1) { + vec = Vector( + x0: endXCoord, + y0: endQuoteToY, + x1: startXCoord, + y1: startQuoteToY, + ); + } + + earlier = vec.x0 - 1000; + later = vec.x1 + 1000; } - final double earlier = vec.x0 - 1000; - final double later = vec.x1 + 1000; + if (!exceedEnd && !exceedStart) { + if (vec.x0 > vec.x1) { + vec = Vector( + x0: endXCoord, + y0: endQuoteToY, + x1: startXCoord, + y1: startQuoteToY, + ); + } + earlier = vec.x0; + later = vec.x1; + } final double startY = getYIntersection(vec, earlier) ?? 0, endingY = getYIntersection(vec, later) ?? 0, @@ -95,28 +131,24 @@ class LineDrawing extends Drawing { double Function(int x) epochToX, double Function(double y) quoteToY, DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); + + /// Get the latest config of any drawing tool which is used to draw the line final LineDrawingToolConfig config = drawingData.config as LineDrawingToolConfig; final LineStyle lineStyle = config.lineStyle; final DrawingPatterns pattern = config.pattern; - _startPoint = draggableStartPoint.updatePosition( - startEpoch, - startYCoord, - epochToX, - quoteToY, - ); - _endPoint = draggableEndPoint!.updatePosition( - endEpoch, - endYCoord, - epochToX, - quoteToY, - ); + _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); final double startXCoord = _startPoint!.x; final double startQuoteToY = _startPoint!.y; @@ -125,7 +157,7 @@ class LineDrawing extends Drawing { final double endQuoteToY = _endPoint!.y; if (drawingPart == DrawingParts.marker) { - if (endEpoch != 0 && endQuoteToY != 0) { + if (endEdgePoint.epoch != 0 && endQuoteToY != 0) { /// Draw first point canvas.drawCircle( Offset(endXCoord, endQuoteToY), @@ -133,7 +165,7 @@ class LineDrawing extends Drawing { drawingData.isSelected ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); - } else if (startEpoch != 0 && startQuoteToY != 0) { + } else if (startEdgePoint.epoch != 0 && startQuoteToY != 0) { /// Draw second point canvas.drawCircle( Offset(startXCoord, startQuoteToY), @@ -172,27 +204,40 @@ class LineDrawing extends Drawing { double Function(int x) epochToX, double Function(double y) quoteToY, DrawingToolConfig config, - DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, }) { + setIsStartPointDragged(isDragged: false); + setIsEndPointDragged!(isDragged: false); + final LineStyle lineStyle = config.toJson()['lineStyle']; - final double startXCoord = _startPoint!.x; - final double startQuoteToY = _startPoint!.y; + double startXCoord = _startPoint!.x; + double startQuoteToY = _startPoint!.y; - final double endXCoord = _endPoint!.x; - final double endQuoteToY = _endPoint!.y; + double endXCoord = _endPoint!.x; + double endQuoteToY = _endPoint!.y; /// Check if start point clicked if (_startPoint!.isClicked(position, markerRadius)) { - draggableStartPoint.isDragged = true; + setIsStartPointDragged(isDragged: true); } /// Check if end point clicked if (_endPoint!.isClicked(position, markerRadius)) { - draggableEndPoint!.isDragged = true; + setIsEndPointDragged(isDragged: true); } + startXCoord = _vector.x0; + startQuoteToY = _vector.y0; + endXCoord = _vector.x1; + endQuoteToY = _vector.y1; + + final double lineLength = sqrt( + pow(endQuoteToY - startQuoteToY, 2) + pow(endXCoord - startXCoord, 2)); + /// Computes the distance between a point and a line which should be less /// than the line thickness + 6 to make sure the user can easily click on final double distance = ((endQuoteToY - startQuoteToY) * position.dx - @@ -202,6 +247,17 @@ class LineDrawing extends Drawing { sqrt(pow(endQuoteToY - startQuoteToY, 2) + pow(endXCoord - startXCoord, 2)); - return distance.abs() <= lineStyle.thickness + 6; + final double xDistToStart = position.dx - startXCoord; + final double yDistToStart = position.dy - startQuoteToY; + + /// Limit the detection to start and end point of the line + final double dotProduct = (xDistToStart * (endXCoord - startXCoord) + + yDistToStart * (endQuoteToY - startQuoteToY)) / + lineLength; + + final bool isWithinRange = dotProduct > 0 && dotProduct < lineLength; + + return isWithinRange && distance.abs() <= lineStyle.thickness + 6 || + (draggableStartPoint.isDragged || draggableEndPoint!.isDragged); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart index eaabf65ae..1674fc876 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart @@ -1,29 +1,25 @@ -import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../data_model/drawing_parts.dart'; -import './line_drawing.dart'; /// Creates a Line drawing piece by piece collected on every gesture /// exists in a widget tree starting from selecting a line drawing tool and /// until drawing is finished -class LineDrawingCreator extends StatefulWidget { +class LineDrawingCreator extends DrawingCreator { /// Initializes the line drawing creator. const LineDrawingCreator({ - required this.onAddDrawing, - required this.quoteFromCanvasY, + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, required this.removeDrawing, Key? key, - }) : super(key: key); - - /// Callback to pass a newly created line drawing to the parent. - final void Function(Map> addedDrawing, - {bool isDrawingFinished}) onAddDrawing; - - /// Conversion function for converting quote from chart's canvas' Y position. - final double Function(double) quoteFromCanvasY; + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); /// Callback to clean drawing tool selection. final VoidCallback clearDrawingToolSelection; @@ -32,119 +28,80 @@ class LineDrawingCreator extends StatefulWidget { final void Function(String drawingId) removeDrawing; @override - _LineDrawingCreatorState createState() => _LineDrawingCreatorState(); + DrawingCreatorState createState() => _LineDrawingCreatorState(); } -class _LineDrawingCreatorState extends State { - late GestureManagerState gestureManager; - - /// Parts of a particular line drawing, e.g. marker, line - final List _drawingParts = []; - - /// Tapped position. - Offset? position; - - /// Saved starting epoch. - int? _startingEpoch; - - /// Saved starting Y coordinates. - double? _startingYPoint; - - /// Saved ending epoch. - int? _endingEpoch; - - /// Saved ending Y coordinates. - double? _endingYPoint; - +class _LineDrawingCreatorState extends DrawingCreatorState { /// If drawing has been started. bool _isPenDown = false; - /// Unique drawing id. - String _drawingId = ''; - - /// If drawing has been finished. - bool _isDrawingFinished = false; - - /// Get epoch from x. - int Function(double x)? epochFromX; - @override - void initState() { - super.initState(); - gestureManager = context.read() - ..registerCallback(_onTap); - } + void onTap(TapUpDetails details) { + super.onTap(details); - @override - void dispose() { - gestureManager.removeCallback(_onTap); - super.dispose(); - } + final LineDrawingCreator _widget = widget as LineDrawingCreator; - void _onTap(TapUpDetails details) { - if (_isDrawingFinished) { + if (isDrawingFinished) { return; } setState(() { position = details.localPosition; + tapCount++; + if (!_isPenDown) { /// Draw the initial point of the line. - _startingEpoch = epochFromX!(position!.dx); - _startingYPoint = widget.quoteFromCanvasY(position!.dy); + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); _isPenDown = true; - _drawingId = 'line_$_startingEpoch'; - _drawingParts.add(LineDrawing( + drawingParts.add(LineDrawing( drawingPart: DrawingParts.marker, - startEpoch: _startingEpoch!, - startYCoord: _startingYPoint!, + startEdgePoint: edgePoints.first, )); - } else if (!_isDrawingFinished) { + } else if (!isDrawingFinished) { /// Draw final point and the whole line. _isPenDown = false; - _isDrawingFinished = true; - _endingEpoch = epochFromX!(position!.dx); - _endingYPoint = widget.quoteFromCanvasY(position!.dy); + isDrawingFinished = true; + final int currentTap = tapCount - 1; + final int previousTap = tapCount - 2; + + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); /// Checks if the initial point and the final point are the same. - if (Offset(_startingEpoch!.toDouble(), _startingYPoint!.toDouble()) == - Offset(_endingEpoch!.toDouble(), _endingYPoint!.toDouble())) { - /// If the initial point and the final point are the same, - /// remove the drawing and cleazn the drawing tool selection. - widget.removeDrawing(_drawingId); - widget.clearDrawingToolSelection(); + if (edgePoints[1] == edgePoints.first) { + /// If the initial point and the 2nd point are the same, + /// remove the drawing and clean the drawing tool selection. + _widget.removeDrawing(drawingId); + _widget.clearDrawingToolSelection(); return; } else { /// If the initial point and the final point are not the same, /// draw the final point and the whole line. - _drawingParts.addAll([ + drawingParts.addAll([ LineDrawing( drawingPart: DrawingParts.marker, - endEpoch: _endingEpoch!, - endYCoord: _endingYPoint!, + endEdgePoint: edgePoints[currentTap], ), LineDrawing( drawingPart: DrawingParts.line, - startEpoch: _startingEpoch!, - startYCoord: _startingYPoint!, - endEpoch: _endingEpoch!, - endYCoord: _endingYPoint!, + startEdgePoint: edgePoints[previousTap], + endEdgePoint: edgePoints[currentTap], + exceedStart: true, + exceedEnd: true, ) ]); } } widget.onAddDrawing( - >{_drawingId: _drawingParts}, - isDrawingFinished: _isDrawingFinished, + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, ); }); } - - @override - Widget build(BuildContext context) { - final XAxisModel xAxis = context.watch(); - epochFromX = xAxis.epochFromX; - - return Container(); - } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index c7b35f05a..d78c76fe8 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -4,6 +4,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; @@ -16,18 +17,14 @@ class VerticalDrawing extends Drawing { /// Initializes VerticalDrawing({ required this.drawingPart, - this.epoch = 0, - this.yCoord = 0, + required this.edgePoint, }); /// Part of a drawing: 'vertical' final DrawingParts drawingPart; - /// Starting epoch. - final int epoch; - - /// Starting Y coordinates. - final double yCoord; + /// Starting point of drawing + final EdgePoint edgePoint; /// Keeps the latest position of the start of drawing Point? startPoint; @@ -41,6 +38,10 @@ class VerticalDrawing extends Drawing { double Function(int x) epochToX, double Function(double y) quoteToY, DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { DraggableEdgePoint? draggableEndPoint, }) { @@ -51,12 +52,7 @@ class VerticalDrawing extends Drawing { final LineStyle lineStyle = config.lineStyle; final DrawingPatterns pattern = config.pattern; - startPoint = draggableStartPoint.updatePosition( - epoch, - yCoord, - epochToX, - quoteToY, - ); + startPoint = updatePositionCallback(edgePoint, draggableStartPoint); final double xCoord = startPoint!.x; final double startQuoteToY = startPoint!.y; @@ -85,8 +81,10 @@ class VerticalDrawing extends Drawing { double Function(int x) epochToX, double Function(double y) quoteToY, DrawingToolConfig config, - DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, }) { final LineStyle lineStyle = config.toJson()['lineStyle']; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart index 2569bb8e1..1ba24040f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart @@ -1,96 +1,55 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import './vertical_drawing.dart'; /// Creates a Vertical line drawing -class VerticalDrawingCreator extends StatefulWidget { +class VerticalDrawingCreator extends DrawingCreator { /// Initializes the vertical drawing creator. const VerticalDrawingCreator({ - required this.onAddDrawing, - required this.quoteFromCanvasY, + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, Key? key, - }) : super(key: key); - - /// Callback to pass a newly created vertical drawing to the parent. - final void Function(Map> addedDrawing, - {bool isDrawingFinished}) onAddDrawing; - - /// Conversion function for converting quote from chart's canvas' Y position. - final double Function(double) quoteFromCanvasY; + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); @override - _VerticalDrawingCreatorState createState() => _VerticalDrawingCreatorState(); + DrawingCreatorState createState() => + _VerticalDrawingCreatorState(); } -class _VerticalDrawingCreatorState extends State { - late GestureManagerState gestureManager; - - /// Parts of a particular vertical drawing, e.g. marker, line - final List _drawingParts = []; - - /// Tapped position. - Offset? position; - - /// Saved starting epoch. - int? _startingEpoch; - - /// Saved starting Y coordinates. - double? _startingYPoint; - - /// Unique drawing id. - String _drawingId = ''; - - /// If drawing has been finished. - bool _isDrawingFinished = false; - - /// Get epoch from x. - int Function(double x)? epochFromX; - - @override - void initState() { - super.initState(); - gestureManager = context.read() - ..registerCallback(_onTap); - } - +class _VerticalDrawingCreatorState + extends DrawingCreatorState { @override - void dispose() { - gestureManager.removeCallback(_onTap); - super.dispose(); - } - - void _onTap(TapUpDetails details) { - if (_isDrawingFinished) { + void onTap(TapUpDetails details) { + super.onTap(details); + if (isDrawingFinished) { return; } setState(() { position = details.localPosition; - _startingEpoch = epochFromX!(position!.dx); - _startingYPoint = widget.quoteFromCanvasY(position!.dy); - _drawingId = 'vertical_$_startingEpoch'; - _isDrawingFinished = true; - _drawingParts.add(VerticalDrawing( + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + isDrawingFinished = true; + + drawingParts.add(VerticalDrawing( drawingPart: DrawingParts.line, - epoch: _startingEpoch!, - yCoord: _startingYPoint!, + edgePoint: edgePoints.first, )); widget.onAddDrawing( - >{_drawingId: _drawingParts}, - isDrawingFinished: _isDrawingFinished, + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, ); }); } - - @override - Widget build(BuildContext context) { - final XAxisModel xAxis = context.watch(); - epochFromX = xAxis.epochFromX; - - return Container(); - } } diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index 5f7f9e66b..a5b4e34b8 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -3,16 +3,15 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/crosshair/crosshair_area.dart'; import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_data_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_painter.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_area.dart'; import 'package:deriv_chart/src/deriv_chart/chart/loading_animation.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'basic_chart.dart'; import 'data_visualization/chart_data.dart'; import 'data_visualization/models/animation_info.dart'; @@ -24,10 +23,7 @@ class MainChart extends BasicChart { /// Initializes the main chart to display in the chart widget. MainChart({ required DataSeries mainSeries, - required this.onAddDrawing, - required this.clearDrawingToolSelection, - this.drawings, - this.selectedDrawingTool, + required this.drawingTools, this.isLive = false, int pipSize = 4, Key? key, @@ -64,18 +60,9 @@ class MainChart extends BasicChart { /// The series that hold the list markers. final MarkerSeries? markerSeries; - /// Existing drawings. - final List? drawings; - - /// Callback to pass new drawing to the parent. - final void Function(Map> addedDrawing, - {bool isDrawingFinished}) onAddDrawing; - - /// Selected drawing tool. - final DrawingToolConfig? selectedDrawingTool; - - /// Callback to clean drawing tool selection. - final VoidCallback clearDrawingToolSelection; + /// Keep the reference to the drawing tools class for + /// sharing data between the DerivChart and the DrawingToolsDialog + final DrawingTools drawingTools; /// The function that gets called on crosshair appearance. final VoidCallback? onCrosshairAppeared; @@ -120,8 +107,6 @@ class _ChartImplementationState extends BasicChartState { /// The current animation value of crosshair zoom out. late Animation crosshairZoomOutAnimation; - bool _isDrawingMoving = false; - @override double get verticalPadding { if (canvasSize == null) { @@ -290,15 +275,11 @@ class _ChartImplementationState extends BasicChartState { ), ), DrawingToolChart( - drawings: widget.drawings, - onAddDrawing: widget.onAddDrawing, - onMoveDrawing: _onMoveDrawing, - selectedDrawingTool: widget.selectedDrawingTool, - clearDrawingToolSelection: widget.clearDrawingToolSelection, chartQuoteToCanvasY: chartQuoteToCanvasY, chartQuoteFromCanvasY: chartQuoteFromCanvasY, + drawingTools: widget.drawingTools, ), - if (!_isDrawingMoving) _buildCrosshairArea(), + if (!widget.drawingTools.isDrawingMoving) _buildCrosshairArea(), if (_isScrollToLastTickAvailable) Positioned( bottom: 0, @@ -429,10 +410,4 @@ class _ChartImplementationState extends BasicChartState { maxQuote = safeMax(maxQuote, widget.chartDataList.getMaxValue()); return [minQuote, maxQuote]; } - - void _onMoveDrawing({bool isDrawingMoved = false}) { - setState(() { - _isDrawingMoving = isDrawingMoved; - }); - } } diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index d5b581223..ba36a239a 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -10,10 +10,9 @@ import 'package:deriv_chart/src/deriv_chart/chart/chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/annotations/chart_annotation.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/chart_object.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/misc/callbacks.dart'; import 'package:deriv_chart/src/misc/chart_controller.dart'; import 'package:deriv_chart/src/models/chart_axis_config.dart'; @@ -98,11 +97,7 @@ class _DerivChartState extends State { final AddOnsRepository _drawingToolsRepo = AddOnsRepository(DrawingToolConfig); - /// Selected drawing tool. - DrawingToolConfig? _selectedDrawingTool; - - /// Existing drawings. - final List _drawings = []; + final DrawingTools _drawingTools = DrawingTools(); @override void initState() { @@ -181,10 +176,7 @@ class _DerivChartState extends State { ), )) ], - drawings: _drawings, - onAddDrawing: _onAddDrawing, - selectedDrawingTool: _selectedDrawingTool, - clearDrawingToolSelection: _clearDrawingToolSelection, + drawingTools: _drawingTools, markerSeries: widget.markerSeries, theme: widget.theme, onCrosshairAppeared: widget.onCrosshairAppeared, @@ -218,15 +210,11 @@ class _DerivChartState extends State { child: IconButton( icon: const Icon(Icons.drive_file_rename_outline_outlined), onPressed: () { - /// Remove unfinished drawings before openning the dialog. - /// For the scenario where the user adds part of a drawing - /// and then opens the dialog. setState(() { - _drawings.removeWhere( - (DrawingData data) => !data.isDrawingFinished); - _selectedDrawingTool = null; + _drawingTools + ..init() + ..drawingToolsRepo = _drawingToolsRepo; }); - showDialog( context: context, builder: ( @@ -236,24 +224,7 @@ class _DerivChartState extends State { AddOnsRepository>.value( value: _drawingToolsRepo, child: DrawingToolsDialog( - onDrawingToolRemoval: (int index) { - setState(() { - _drawings.removeAt(index); - }); - }, - onDrawingToolSelection: - (DrawingToolConfig selectedDrawingTool) { - setState(() { - _selectedDrawingTool = selectedDrawingTool; - }); - }, - onDrawingToolUpdate: - (int index, DrawingToolConfig updatedConfig) { - setState(() { - _drawings[index] = - _drawings[index].updateConfig(updatedConfig); - }); - }, + drawingTools: _drawingTools, ), ), ); @@ -264,48 +235,4 @@ class _DerivChartState extends State { ), ), ); - - void _onAddDrawing( - Map> addedDrawing, { - bool isDrawingFinished = false, - }) { - setState(() { - final String drawingId = addedDrawing.keys.first; - - final DrawingData? existingDrawing = _drawings.firstWhereOrNull( - (DrawingData drawing) => drawing.id == drawingId, - ); - - if (existingDrawing == null) { - _drawings.add(DrawingData( - id: drawingId, - config: _selectedDrawingTool!, - drawingParts: addedDrawing.values.first, - isDrawingFinished: isDrawingFinished, - )); - } else { - existingDrawing - ..updateDrawingPartList(addedDrawing.values.first) - ..isSelected = true - ..isDrawingFinished = isDrawingFinished; - } - - if (isDrawingFinished) { - _drawingToolsRepo.add(_selectedDrawingTool!); - _selectedDrawingTool = null; - } - - if (_drawings.length > 1) { - _drawings.removeWhere((DrawingData data) => - data.id != drawingId && !data.isDrawingFinished); - } - }); - } - - /// Clean the drawing tool selection. - void _clearDrawingToolSelection() { - setState(() { - _selectedDrawingTool = null; - }); - } } diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index d297c5e5e..b319de4a2 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -1,51 +1,33 @@ -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; /// A wigdet for encapsulating drawing tools related business logic class DrawingToolChart extends StatelessWidget { /// Creates chart that expands to available space. const DrawingToolChart({ - required this.onAddDrawing, required this.chartQuoteFromCanvasY, required this.chartQuoteToCanvasY, - required this.onMoveDrawing, - required this.clearDrawingToolSelection, - this.drawings, - this.selectedDrawingTool, + required this.drawingTools, Key? key, }) : super(key: key); - /// Existing drawings. - final List? drawings; - - /// Callback to pass new drawing to the parent. - final void Function(Map> addedDrawing, - {bool isDrawingFinished}) onAddDrawing; - - /// Callback to pass new drawing to the parent. - final void Function({bool isDrawingMoved}) onMoveDrawing; - - /// Callback to clean drawing tool selection. - final VoidCallback clearDrawingToolSelection; - - /// Selected drawing tool. - final DrawingToolConfig? selectedDrawingTool; - /// Conversion function for converting quote from chart's canvas' Y position. final double Function(double) chartQuoteFromCanvasY; /// Conversion function for converting quote to chart's canvas' Y position. final double Function(double) chartQuoteToCanvasY; + /// Contains drawing tools related data and methods + final DrawingTools drawingTools; + /// Sets drawing as selected and unselects the rest of drawings void _setIsDrawingSelected(DrawingData drawing) { drawing.isSelected = !drawing.isSelected; - for (final DrawingData data in drawings!) { + for (final DrawingData data in drawingTools.drawings) { if (data.id != drawing.id) { data.isSelected = false; } @@ -54,7 +36,8 @@ class DrawingToolChart extends StatelessWidget { /// Removes specific drawing from the list of drawings void removeDrawing(String drawingId) { - drawings!.removeWhere((DrawingData data) => data.id == drawingId); + drawingTools.drawings + .removeWhere((DrawingData data) => data.id == drawingId); } @override @@ -62,21 +45,24 @@ class DrawingToolChart extends StatelessWidget { child: Stack( fit: StackFit.expand, children: [ - if (drawings != null) - ...drawings!.map((DrawingData drawingData) => DrawingPainter( - drawingData: drawingData, - quoteToCanvasY: chartQuoteToCanvasY, - quoteFromCanvasY: chartQuoteFromCanvasY, - onMoveDrawing: onMoveDrawing, - setIsDrawingSelected: _setIsDrawingSelected, - )), - if (selectedDrawingTool != null) - DrawingCreator( - onAddDrawing: onAddDrawing, - selectedDrawingTool: selectedDrawingTool!, + ...drawingTools.drawings + .map((DrawingData drawingData) => DrawingPainter( + drawingData: drawingData, + quoteToCanvasY: chartQuoteToCanvasY, + quoteFromCanvasY: chartQuoteFromCanvasY, + onMoveDrawing: drawingTools.onMoveDrawing, + setIsDrawingSelected: _setIsDrawingSelected, + selectedDrawingTool: drawingTools.selectedDrawingTool, + )), + if (drawingTools.selectedDrawingTool != null) + DrawingToolWidget( + onAddDrawing: drawingTools.onAddDrawing, + selectedDrawingTool: drawingTools.selectedDrawingTool!, quoteFromCanvasY: chartQuoteFromCanvasY, - clearDrawingToolSelection: clearDrawingToolSelection, + clearDrawingToolSelection: + drawingTools.clearDrawingToolSelection, removeDrawing: removeDrawing, + shouldStopDrawing: drawingTools.shouldStopDrawing, ), ], ), diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart new file mode 100644 index 000000000..589e32d84 --- /dev/null +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart @@ -0,0 +1,107 @@ +import 'package:collection/collection.dart'; +import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; + +/// This calss is used to keep all the methods and data related to drawing tools +/// Which need to be shared between the DerivChart and the DrawingToolsDialog +class DrawingTools { + /// Existing drawings. + final List drawings = []; + + /// A flag to show when to stop drawing only for drawings which don't have + /// fixed number of points like continuous drawing + bool? shouldStopDrawing; + + /// Selected drawing tool. + DrawingToolConfig? selectedDrawingTool; + + /// Keep the reference to the drawing tools repository. + AddOnsRepository? drawingToolsRepo; + + /// For tracking the drawing movement + bool isDrawingMoving = false; + + /// Remove unfinished drawings before openning the dialog. + /// For the scenario where the user adds part of a drawing + /// and then opens the dialog. + void init() { + if (selectedDrawingTool != null) { + shouldStopDrawing = true; + } + drawings.removeWhere((DrawingData data) => !data.isDrawingFinished); + selectedDrawingTool = null; + } + + /// Callback to remove a drawing. + void onDrawingToolRemoval(int index) { + drawings.removeAt(index); + } + + /// Callback for keeping the selected drawing tool data. + void onDrawingToolSelection(DrawingToolConfig config) { + shouldStopDrawing = false; + selectedDrawingTool = config; + } + + /// Callback for updating the drawing data list. + void onDrawingToolUpdate(int index, DrawingToolConfig config) { + drawings[index] = drawings[index].updateConfig(config); + } + + /// Callback to add the new drawing to the list of drawings + /// isInfiniteDrawing used for drawings which don't have fixed number of + /// points + void onAddDrawing( + String drawingId, + List drawingParts, { + bool isDrawingFinished = false, + bool isInfiniteDrawing = false, + }) { + final DrawingData? existingDrawing = drawings.firstWhereOrNull( + (DrawingData drawing) => drawing.id == drawingId, + ); + + if (existingDrawing == null) { + drawings.add(DrawingData( + id: drawingId, + config: selectedDrawingTool!, + drawingParts: drawingParts, + isDrawingFinished: isDrawingFinished, + )); + } else { + existingDrawing + ..updateDrawingPartList(drawingParts) + ..isSelected = true + ..isDrawingFinished = isDrawingFinished; + } + + if (isDrawingFinished) { + drawingToolsRepo!.add(selectedDrawingTool!); + + if (isInfiniteDrawing && shouldStopDrawing!) { + selectedDrawingTool = null; + } + if (!isInfiniteDrawing) { + selectedDrawingTool = null; + } + } + + if (drawings.length > 1) { + drawings.removeWhere((DrawingData data) => + data.id != drawingId && !data.isDrawingFinished); + } + } + + /// Callback to inform parent about drawing tool removal. + void clearDrawingToolSelection() { + selectedDrawingTool = null; + } + + /// Callback for tracking the drawing movement. + // ignore: use_setters_to_change_properties + void onMoveDrawing({bool isDrawingMoved = false}) { + isDrawingMoving = isDrawingMoved; + } +} From 216cc353a86a20721ba92b24e3a3b6fce1e53dac Mon Sep 17 00:00:00 2001 From: ahmadtaimoor-deriv <129935294+ahmadtaimoor-deriv@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:31:13 +0800 Subject: [PATCH 09/29] Ahmad/Add trend drawing tool (#236) - add trend drawing tool --- lib/generated/intl/messages_en.dart | 1 + lib/generated/l10n.dart | 10 + lib/l10n/intl_en.arb | 4 +- .../drawing_tools_ui/distance_constants.dart | 7 + .../drawing_tools_ui/drawing_tool_config.dart | 4 + .../drawing_tools_dialog.dart | 5 + .../trend/trend_drawing_tool_config.dart | 53 +++ .../trend/trend_drawing_tool_config.g.dart | 34 ++ .../trend/trend_drawing_tool_item.dart | 89 +++++ .../data_model/drawing_paint_style.dart | 12 + .../data_model/drawing_parts.dart | 3 + .../drawing_tools/drawing.dart | 2 +- .../drawing_tools/drawing_data.dart | 5 + .../drawing_tools/drawing_tool_widget.dart | 15 + .../drawing_tools/trend/trend_drawing.dart | 328 ++++++++++++++++++ .../trend/trend_drawing_creator.dart | 265 ++++++++++++++ lib/src/deriv_chart/chart/main_chart.dart | 1 + .../drawing_tool_chart.dart | 48 ++- 18 files changed, 870 insertions(+), 16 deletions(-) create mode 100644 lib/src/add_ons/drawing_tools_ui/distance_constants.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 2609cab09..7b43c5db8 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -39,6 +39,7 @@ class MessageLookup extends MessageLookupByLibrary { "labelFastMAPeriod": MessageLookupByLibrary.simpleMessage("Fast MA Period"), "labelField": MessageLookupByLibrary.simpleMessage("Field"), + "labelFillColor": MessageLookupByLibrary.simpleMessage("Fill Color"), "labelHighPeriod": MessageLookupByLibrary.simpleMessage("High Period"), "labelHistogram": MessageLookupByLibrary.simpleMessage("Histogram"), "labelIsSmooth": MessageLookupByLibrary.simpleMessage("Is Smooth"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index d454a7516..921540b0e 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -499,6 +499,16 @@ class ChartLocalization { args: [], ); } + + /// `Fill Color` + String get labelFillColor { + return Intl.message( + 'Fill Color', + name: 'labelFillColor', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 366202600..91eacc1bd 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -44,5 +44,7 @@ "warnFailedLoadingIndicators": "Failed to load indicators.", "warnFailedLoadingDrawingTools": "Failed to load drawing tools.", "selectDrawingTool": "Select drawing tool", - "labelColor": "Color" + "labelColor": "Color", + "labelFillColor": "Fill Color" + } diff --git a/lib/src/add_ons/drawing_tools_ui/distance_constants.dart b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart new file mode 100644 index 000000000..4d03d8808 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart @@ -0,0 +1,7 @@ +/// This class include the distance/value used in the drawing tools . +/// (i.e. Horizontal , line chart) +/// +class DrawingToolDistance { + /// horizontal distance + static const double horizontalDistance = 999999; +} diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index f74da15b4..0356d2c31 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:flutter/material.dart'; import 'line/line_drawing_tool_config.dart'; import 'vertical/vertical_drawing_tool_config.dart'; @@ -24,8 +25,11 @@ abstract class DrawingToolConfig extends AddOnConfig { return ContinuousDrawingToolConfig.fromJson(json); case LineDrawingToolConfig.name: return LineDrawingToolConfig.fromJson(json); + case TrendDrawingToolConfig.name: + return TrendDrawingToolConfig.fromJson(json); case VerticalDrawingToolConfig.name: return VerticalDrawingToolConfig.fromJson(json); + // Add new drawing tools here. default: throw ArgumentError.value( diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index 9e8b66c39..476665a06 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/widgets/animated_popup.dart'; @@ -51,6 +52,10 @@ class _DrawingToolsDialogState extends State { child: Text('Line'), value: LineDrawingToolConfig(), ), + DropdownMenuItem( + child: Text('Trend'), + value: TrendDrawingToolConfig(), + ), DropdownMenuItem( child: Text('Vertical'), value: VerticalDrawingToolConfig(), diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart new file mode 100644 index 000000000..02ff9fbe3 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart @@ -0,0 +1,53 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../callbacks.dart'; + +part 'trend_drawing_tool_config.g.dart'; + +/// Trend drawing tool configurations. +@JsonSerializable() +class TrendDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const TrendDrawingToolConfig({ + this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = DrawingPatterns.solid, + }) : super(); + + /// Initializes from JSON. + factory TrendDrawingToolConfig.fromJson(Map json) => + _$TrendDrawingToolConfigFromJson(json); + + /// Unique name for this drawing tool. + static const String name = 'dt_trend'; + + @override + Map toJson() => _$TrendDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool fill style + final LineStyle fillStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + final DrawingPatterns pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + TrendDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart new file mode 100644 index 000000000..5d334eaed --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'trend_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TrendDrawingToolConfig _$TrendDrawingToolConfigFromJson( + Map json) => + TrendDrawingToolConfig( + fillStyle: json['fillStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.blue) + : LineStyle.fromJson(json['fillStyle'] as Map), + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); + +Map _$TrendDrawingToolConfigToJson( + TrendDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'fillStyle': instance.fillStyle, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart new file mode 100644 index 000000000..7e4608698 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart @@ -0,0 +1,89 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; + +import 'package:flutter/material.dart'; + +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; +import '../drawing_tool_item.dart'; + +/// Trend drawing tool item in the list of drawing tool which provide this +/// drawing tools options menu. +class TrendDrawingToolItem extends DrawingToolItem { + /// Initializes + const TrendDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + TrendDrawingToolConfig config = const TrendDrawingToolConfig(), + }) : super( + key: key, + title: 'Trend', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createDrawingToolItemState() => + TrendDrawingToolItemState(); +} + +/// Trend drawing tool Item State class +class TrendDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _fillStyle; + LineStyle? _lineStyle; + DrawingPatterns? _pattern; + + @override + TrendDrawingToolConfig createDrawingToolConfig() => TrendDrawingToolConfig( + fillStyle: _currentFillStyle, + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField( + ChartLocalization.of(context).labelColor, _currentLineStyle), + _buildColorField( + ChartLocalization.of(context).labelFillColor, _currentFillStyle), + // TODO(maryia-deriv): implement _buildPatternField() to set pattern + ], + ); + Widget _buildColorField(String label, LineStyle style) => Row( + children: [ + Text( + label, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: style.color, + onColorChanged: (Color selectedColor) { + setState(() { + final LineStyle newColor = style.copyWith(color: selectedColor); + if (label == ChartLocalization.of(context).labelColor) { + _lineStyle = newColor; + } else { + _fillStyle = newColor; + } + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentFillStyle => + _fillStyle ?? (widget.config as TrendDrawingToolConfig).fillStyle; + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as TrendDrawingToolConfig).lineStyle; + + DrawingPatterns get _currentPattern => + _pattern ?? (widget.config as TrendDrawingToolConfig).pattern; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart index 174b24143..077426001 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart @@ -13,6 +13,18 @@ class DrawingPaintStyle { ..color = color ..strokeWidth = thickness; + /// Returns the paint style of the inner filling of container + Paint fillPaintStyle(Color color, double thickness) => Paint() + ..color = color + ..style = PaintingStyle.fill + ..strokeWidth = thickness; + + /// Returns the paint style of the outer stroke of the container + Paint strokeStyle(Color color, double thickness) => Paint() + ..color = color + ..style = PaintingStyle.stroke + ..strokeWidth = thickness; + /// Returns the paint style of the circle marker Paint glowyCirclePaintStyle(Color color) => Paint()..color = color; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart index 9729dca23..b734d1629 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart @@ -5,4 +5,7 @@ enum DrawingParts { /// Used to show the line. line, + + /// Used to show the rectangle + rectangle } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index 9f6925a17..cd78ab683 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -1,9 +1,9 @@ +import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; -import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart index 5661940b9..c66f99a03 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; @@ -10,6 +11,7 @@ class DrawingData { required this.drawingParts, this.isDrawingFinished = false, this.isSelected = true, + this.series, }); /// Unique id of the current drawing. @@ -18,6 +20,9 @@ class DrawingData { /// Configuration of the current drawing. final DrawingToolConfig config; + /// Series of ticks + List? series; + /// Drawing list. final List drawingParts; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index e1d2958d4..82db13a51 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -1,6 +1,8 @@ +import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; @@ -16,6 +18,7 @@ class DrawingToolWidget extends StatelessWidget { required this.quoteFromCanvasY, required this.clearDrawingToolSelection, required this.removeDrawing, + required this.series, this.shouldStopDrawing, Key? key, }) : super(key: key); @@ -23,6 +26,9 @@ class DrawingToolWidget extends StatelessWidget { /// Selected drawing tool. final DrawingToolConfig selectedDrawingTool; + /// Series of tick + final DataSeries series; + /// Callback to pass a newly created drawing to the parent. final void Function( String drawingId, @@ -71,6 +77,15 @@ class DrawingToolWidget extends StatelessWidget { onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, ); + case 'dt_trend': + return TrendDrawingCreator( + onAddDrawing: onAddDrawing, + clearDrawingToolSelection: clearDrawingToolSelection, + quoteFromCanvasY: quoteFromCanvasY, + removeDrawing: removeDrawing, + series: series, + ); + // TODO(maryia-binary): add the rest of drawing tools here default: return Container(); diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart new file mode 100644 index 000000000..a7c85ad35 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart @@ -0,0 +1,328 @@ +import 'dart:math'; + +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/functions/min_max_calculator.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Trend drawing tool. +class TrendDrawing extends Drawing { + /// Initializes + TrendDrawing({ + required this.drawingPart, + required this.epochFromX, + required this.setCalculator, + required this.isClickedOnRectangleBoundary, + required this.touchTolerance, + this.startEdgePoint = const EdgePoint(), + this.endEdgePoint = const EdgePoint(), + }); + + /// Function to check if the clicked position (Offset) is on + /// boundary of the rectangle + final bool Function(Rect rect, Offset position) isClickedOnRectangleBoundary; + + /// Callback that returns the minmax calculator between start and end epoch + final MinMaxCalculator? Function( + int minimumEpoch, int maximumEpoch, List? series) setCalculator; + + /// Get epoch from x. + int Function(double x)? epochFromX; + + /// Instance of enum including all possible drawing parts(marker,rectangle) + final DrawingParts drawingPart; + + /// The area impacted upon touch on all lines within the + /// trend drawing tool. .i.e outer rectangle , inner rectangle + /// and center line. + final double touchTolerance; + + /// Marker radius. + final double _markerRadius = 10; + + /// Keeps the latest position of the start and end point of drawing + Point? _startPoint, _endPoint; + + /// Instance of MinMaxCalculator class that holds the minimum + /// and maximum quote in the trend range w.r.t epoch + MinMaxCalculator? _calculator; + + /// Store the starting X Coordinate + double startXCoord = 0; + + /// Store the starting Y Coordinate + double startYCoord = 0; + + /// Store the ending X Coordinate + double endXCoord = 0; + + /// Starting point of drawing + EdgePoint startEdgePoint; + + /// Ending point of drawing + EdgePoint endEdgePoint; + + /// Store the complete rectangle between start,end epoch and + /// minimum,maximum quote. + Rect _mainRect = Rect.zero; + + /// Stores the middle rectangle for the trend , + Rect _middleRect = Rect.zero; + + /// Stores the center of the area for the markers + double _rectCenter = 0; + + /// Stores a flag if the rectangle sides are swapped .i.e the left + /// side is dragged to the right of the right side + bool _isRectangleSwapped = false; + + /// Paint the trend drawing tools + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final DrawingPaintStyle paint = DrawingPaintStyle(); + final List? series = drawingData.series; + // Maximum epoch of the drawing + final int minimumEpoch = + startXCoord == 0 ? startEdgePoint.epoch : epochFromX!(startXCoord); + + // Minimum epoch of the drawing + final int maximumEpoch = + endXCoord == 0 ? endEdgePoint.epoch : epochFromX!(endXCoord); + + if (maximumEpoch != 0 && minimumEpoch != 0) { + // setting calculator + _calculator = setCalculator(minimumEpoch, maximumEpoch, series); + + // center of rectangle + _rectCenter = quoteToY(_calculator!.min) + + ((quoteToY(_calculator!.max) - quoteToY(_calculator!.min)) / 2); + } + + final TrendDrawingToolConfig config = + drawingData.config as TrendDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final LineStyle fillStyle = config.fillStyle; + final DrawingPatterns pattern = config.pattern; + + if (_calculator != null) { + _startPoint = updatePositionCallback( + EdgePoint( + epoch: startEdgePoint.epoch, + quote: + _calculator!.min + (_calculator!.max - _calculator!.min) / 2), + draggableStartPoint); + + _endPoint = updatePositionCallback( + EdgePoint( + epoch: endEdgePoint.epoch, + quote: + _calculator!.min + (_calculator!.max - _calculator!.min) / 2), + draggableEndPoint!); + + startXCoord = _startPoint!.x; + startYCoord = _startPoint!.y; + + endXCoord = _endPoint!.x; + } + + // If the rectangle vertical side are swapped + // .i.e dragging left side to the right of the right side + if (endXCoord < startXCoord && endEdgePoint.epoch != 0) { + final double _tempCoord = endXCoord; + endXCoord = startXCoord; + startXCoord = _tempCoord; + _isRectangleSwapped = true; + } else { + _isRectangleSwapped = false; + } + + /// When both points are dragged to same point + if (_calculator != null && quoteToY(_calculator!.max).isNaN) { + return; + } + + if (drawingPart == DrawingParts.marker) { + if (endEdgePoint.epoch == 0) { + _startPoint = updatePositionCallback( + EdgePoint(epoch: startEdgePoint.epoch, quote: startEdgePoint.quote), + draggableStartPoint); + + startXCoord = _startPoint!.x; + startYCoord = _startPoint!.y; + + canvas.drawCircle( + Offset(startXCoord, startYCoord), + _markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle(), + ); + } else { + canvas + ..drawCircle( + Offset(startXCoord, _rectCenter), + _markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle(), + ) + ..drawCircle( + Offset(endXCoord, _rectCenter), + _markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle(), + ); + } + } + + if (drawingPart == DrawingParts.rectangle) { + /// Store the distance between minimum and maximum quote of the drawing + final double _distance = + (quoteToY(_calculator!.min) - quoteToY(_calculator!.max)).abs(); + + if (pattern == DrawingPatterns.solid) { + _middleRect = Rect.fromLTRB( + startXCoord, + quoteToY(_calculator!.max) + _distance / 3, + endXCoord, + quoteToY(_calculator!.max) + (_distance - _distance / 3), + ); + + _mainRect = Rect.fromLTRB( + startXCoord, + quoteToY(_calculator!.max), + endXCoord, + quoteToY(_calculator!.min), + ); + + canvas + ..drawRect( + _mainRect, + drawingData.isSelected + ? paint.glowyLinePaintStyle( + fillStyle.color.withOpacity(0.2), lineStyle.thickness) + : paint.fillPaintStyle( + fillStyle.color.withOpacity(0.2), lineStyle.thickness), + ) + ..drawRect( + _mainRect, + paint.strokeStyle(lineStyle.color, lineStyle.thickness), + ) + ..drawRect( + _middleRect, + drawingData.isSelected + ? paint.glowyLinePaintStyle( + fillStyle.color.withOpacity(0.2), lineStyle.thickness) + : paint.fillPaintStyle( + fillStyle.color.withOpacity(0.2), lineStyle.thickness), + ) + ..drawRect( + _middleRect, + paint.strokeStyle(lineStyle.color, lineStyle.thickness), + ); + } + } + + if (drawingPart == DrawingParts.line) { + if (pattern == DrawingPatterns.solid) { + canvas.drawLine( + Offset(startXCoord, _rectCenter), + Offset(endXCoord, _rectCenter), + paint.glowyCirclePaintStyle(lineStyle.color), + ); + } + } + } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen, + /// the drawing is selected on clicking on any boundary(line) of the drawing + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, + }) { + setIsStartPointDragged(isDragged: false); + setIsEndPointDragged!(isDragged: false); + + // Calculate the difference between the start Point and the tap point. + final double startDx = position.dx - startXCoord; + final double startDy = position.dy - _rectCenter; + + // Calculate the difference between the end Point and the tap point. + final double endDx = position.dx - endXCoord; + final double endDy = position.dy - _rectCenter; + + // Getting the distance of end point + double endPointDistance = sqrt(endDx * endDx + endDy * endDy); + + // Getting the distance of start point + double startPointDistance = sqrt(startDx * startDx + startDy * startDy); + + if (_isRectangleSwapped) { + final double tempDistance = startPointDistance; + startPointDistance = endPointDistance; + endPointDistance = tempDistance; + } + + if (startPointDistance <= _markerRadius) { + setIsStartPointDragged(isDragged: true); + } + + if (endPointDistance <= _markerRadius) { + setIsEndPointDragged(isDragged: true); + } + + // For clicking the center line + final double lineArea = (0.5 * + (startXCoord * _rectCenter + + endXCoord * position.dy + + position.dx * _rectCenter - + endXCoord * _rectCenter - + position.dx * _rectCenter - + startXCoord * position.dy)) + .abs(); + + final double baseArea = endXCoord - startXCoord; + final double lineHeight = 2 * lineArea / baseArea; + + if (endEdgePoint.epoch != 0) { + return isClickedOnRectangleBoundary(_mainRect, position) || + isClickedOnRectangleBoundary(_middleRect, position) || + startPointDistance <= _markerRadius || + endPointDistance <= _markerRadius || + lineHeight <= touchTolerance; + } + return false; + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart new file mode 100644 index 000000000..597cd2240 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart @@ -0,0 +1,265 @@ +// ignore_for_file: use_setters_to_change_properties + +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/functions/min_max_calculator.dart'; +import 'package:flutter/material.dart'; +import '../data_model/drawing_parts.dart'; + +/// Creates a Trend drawing right after selecting the trend drawing tool +/// and until drawing is finished +class TrendDrawingCreator extends DrawingCreator { + /// Initializes the trend drawing creator. + const TrendDrawingCreator({ + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, + required this.series, + Key? key, + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + + /// Series of tick + final DataSeries series; + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + + @override + DrawingCreatorState createState() => + _TrendDrawingCreatorState(); +} + +class _TrendDrawingCreatorState extends DrawingCreatorState { + /// If drawing has been started. + bool _isPenDown = false; + + /// Stores coordinate of first point on the graph + int? _startingPointEpoch; + + /// Stores the previously changed minimum epoch + int prevMinimumEpoch = 0; + + /// Stores the previously changed maximum epoch + int prevMaximumEpoch = 0; + + /// Instance of MinMaxCalculator class that holds the minimum + /// and maximum quote in the trend range w.r.t epoch + MinMaxCalculator? _calculator; + + static const int touchDistanceThreshold = 200; + + /// The area impacted upon touch on all lines within the + /// trend drawing tool. .i.e outer rectangle , inner rectangle + /// and center line. + final double _touchTolerance = 5; + + /// Binary search to find closest index to the [epoch]. + int _findClosestIndex(int epoch, List? entries) { + int lo = 0; + int hi = entries!.length - 1; + int localEpoch = epoch; + + if (localEpoch > entries[hi].epoch) { + localEpoch = entries[hi].epoch; + } + + while (lo <= hi) { + final int mid = (hi + lo) ~/ 2; + // int getEpochOf(T t, int index) => t.epoch; + if (localEpoch < entries[mid].epoch) { + hi = mid - 1; + } else if (localEpoch > entries[mid].epoch) { + lo = mid + 1; + } else { + return mid; + } + } + + return (entries[lo].epoch - localEpoch) < (localEpoch - entries[hi].epoch) + ? lo + : hi; + } + + /// Setting the minmax calculator between the range of + /// start and end epoch + MinMaxCalculator? _setCalculator( + int minimumEpoch, int maximumEpoch, List? series) { + if (prevMaximumEpoch != maximumEpoch || prevMinimumEpoch != minimumEpoch) { + prevMaximumEpoch = maximumEpoch; + prevMinimumEpoch = minimumEpoch; + int minimumEpochIndex = _findClosestIndex(minimumEpoch, series); + int maximumEpochIndex = _findClosestIndex(maximumEpoch, series); + + if (minimumEpochIndex > maximumEpochIndex) { + final int tempEpochIndex = minimumEpochIndex; + minimumEpochIndex = maximumEpochIndex; + maximumEpochIndex = tempEpochIndex; + } + + final List? epochRange = + series!.sublist(minimumEpochIndex, maximumEpochIndex); + + double minValueOf(Tick t) => t.quote; + double maxValueOf(Tick t) => t.quote; + + _calculator = MinMaxCalculator(minValueOf, maxValueOf) + ..calculate(epochRange!); + } + return _calculator; + } + + /// Function to check if the clicked position (Offset) is on + /// boundary of the rectangle + bool _isClickedOnRectangleBoundary(Rect rect, Offset position) { + /// Width of the rectangle line + const double lineWidth = 3; + + final Rect topLineBounds = Rect.fromLTWH( + rect.left - _touchTolerance, + rect.top - _touchTolerance, + rect.width + _touchTolerance * 2, + lineWidth + _touchTolerance * 2, + ); + + final Rect leftLineBounds = Rect.fromLTWH( + rect.left - _touchTolerance, + rect.top - _touchTolerance, + lineWidth + _touchTolerance * 2, + rect.height + _touchTolerance * 2, + ); + + final Rect rightLineBounds = Rect.fromLTWH( + rect.right - lineWidth - _touchTolerance * 2, + rect.top - _touchTolerance, + lineWidth + _touchTolerance * 2, + rect.height + _touchTolerance * 2, + ); + + final Rect bottomLineBounds = Rect.fromLTWH( + rect.left - _touchTolerance, + rect.bottom - lineWidth - _touchTolerance * 2, + rect.width + _touchTolerance * 2 + 2, + lineWidth + _touchTolerance * 2 + 2, + ); + + return topLineBounds.inflate(2).contains(position) || + leftLineBounds.inflate(2).contains(position) || + rightLineBounds.inflate(2).contains(position) || + bottomLineBounds.inflate(2).contains(position); + } + + @override + void onTap(TapUpDetails details) { + super.onTap(details); + final TrendDrawingCreator _widget = widget as TrendDrawingCreator; + + if (isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + if (!_isPenDown) { + // index of the start point in the series + final int startPointIndex = _findClosestIndex( + epochFromX!(position!.dx), _widget.series.entries); + + // starting point on graph + final Tick? startingPoint = _widget.series.entries![startPointIndex]; + + _startingPointEpoch = startingPoint!.epoch; + + /// Draw the initial point of the line. + edgePoints.add( + EdgePoint( + epoch: startingPoint.epoch, + quote: startingPoint.quote, + ), + ); + + _isPenDown = true; + + drawingParts.add( + TrendDrawing( + epochFromX: epochFromX, + drawingPart: DrawingParts.marker, + startEdgePoint: edgePoints.first, + setCalculator: _setCalculator, + isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, + touchTolerance: _touchTolerance, + ), + ); + } else if (!isDrawingFinished) { + edgePoints.add( + EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + ), + ); + + /// Draw final drawing + _isPenDown = false; + isDrawingFinished = true; + final EdgePoint startingEdgePoint = edgePoints.first; + final EdgePoint endingEdgePoint = edgePoints[1]; + + // When the second point is on the same y + // coordinate as the first point + if ((_startingPointEpoch! - endingEdgePoint.epoch).abs() <= + touchDistanceThreshold) { + /// remove the drawing and clean the drawing tool selection. + _widget.removeDrawing(drawingId); + _widget.clearDrawingToolSelection(); + return; + } + + drawingParts + ..removeAt(0) + ..addAll([ + TrendDrawing( + epochFromX: epochFromX, + drawingPart: DrawingParts.rectangle, + startEdgePoint: startingEdgePoint, + endEdgePoint: endingEdgePoint, + setCalculator: _setCalculator, + isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, + touchTolerance: _touchTolerance, + ), + TrendDrawing( + epochFromX: epochFromX, + drawingPart: DrawingParts.line, + startEdgePoint: startingEdgePoint, + endEdgePoint: endingEdgePoint, + setCalculator: _setCalculator, + isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, + touchTolerance: _touchTolerance, + ), + TrendDrawing( + epochFromX: epochFromX, + drawingPart: DrawingParts.marker, + startEdgePoint: startingEdgePoint, + endEdgePoint: endingEdgePoint, + setCalculator: _setCalculator, + isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, + touchTolerance: _touchTolerance, + ) + ]); + } + + widget.onAddDrawing( + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, + ); + }); + } +} diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index a5b4e34b8..f4110474b 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -275,6 +275,7 @@ class _ChartImplementationState extends BasicChartState { ), ), DrawingToolChart( + series: widget.mainSeries as DataSeries, chartQuoteToCanvasY: chartQuoteToCanvasY, chartQuoteFromCanvasY: chartQuoteFromCanvasY, drawingTools: widget.drawingTools, diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index b319de4a2..116f2a63b 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; @@ -5,15 +6,19 @@ import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dar import 'package:flutter/material.dart'; /// A wigdet for encapsulating drawing tools related business logic -class DrawingToolChart extends StatelessWidget { +class DrawingToolChart extends StatefulWidget { /// Creates chart that expands to available space. const DrawingToolChart({ required this.chartQuoteFromCanvasY, required this.chartQuoteToCanvasY, required this.drawingTools, + required this.series, Key? key, }) : super(key: key); + /// Series of tick + final DataSeries series; + /// Conversion function for converting quote from chart's canvas' Y position. final double Function(double) chartQuoteFromCanvasY; @@ -23,11 +28,16 @@ class DrawingToolChart extends StatelessWidget { /// Contains drawing tools related data and methods final DrawingTools drawingTools; + @override + State createState() => _DrawingToolChartState(); +} + +class _DrawingToolChartState extends State { /// Sets drawing as selected and unselects the rest of drawings void _setIsDrawingSelected(DrawingData drawing) { drawing.isSelected = !drawing.isSelected; - for (final DrawingData data in drawingTools.drawings) { + for (final DrawingData data in widget.drawingTools.drawings) { if (data.id != drawing.id) { data.isSelected = false; } @@ -36,33 +46,43 @@ class DrawingToolChart extends StatelessWidget { /// Removes specific drawing from the list of drawings void removeDrawing(String drawingId) { - drawingTools.drawings + widget.drawingTools.drawings .removeWhere((DrawingData data) => data.id == drawingId); } + @override + void didUpdateWidget(DrawingToolChart oldWidget) { + super.didUpdateWidget(oldWidget); + for (final DrawingData data in widget.drawingTools.drawings) { + data.series = widget.series.entries; + } + } + @override Widget build(BuildContext context) => ClipRect( child: Stack( fit: StackFit.expand, children: [ - ...drawingTools.drawings + ...widget.drawingTools.drawings .map((DrawingData drawingData) => DrawingPainter( drawingData: drawingData, - quoteToCanvasY: chartQuoteToCanvasY, - quoteFromCanvasY: chartQuoteFromCanvasY, - onMoveDrawing: drawingTools.onMoveDrawing, + quoteToCanvasY: widget.chartQuoteToCanvasY, + quoteFromCanvasY: widget.chartQuoteFromCanvasY, + onMoveDrawing: widget.drawingTools.onMoveDrawing, setIsDrawingSelected: _setIsDrawingSelected, - selectedDrawingTool: drawingTools.selectedDrawingTool, + selectedDrawingTool: + widget.drawingTools.selectedDrawingTool, )), - if (drawingTools.selectedDrawingTool != null) + if (widget.drawingTools.selectedDrawingTool != null) DrawingToolWidget( - onAddDrawing: drawingTools.onAddDrawing, - selectedDrawingTool: drawingTools.selectedDrawingTool!, - quoteFromCanvasY: chartQuoteFromCanvasY, + onAddDrawing: widget.drawingTools.onAddDrawing, + selectedDrawingTool: widget.drawingTools.selectedDrawingTool!, + quoteFromCanvasY: widget.chartQuoteFromCanvasY, clearDrawingToolSelection: - drawingTools.clearDrawingToolSelection, + widget.drawingTools.clearDrawingToolSelection, + series: widget.series, removeDrawing: removeDrawing, - shouldStopDrawing: drawingTools.shouldStopDrawing, + shouldStopDrawing: widget.drawingTools.shouldStopDrawing, ), ], ), From 42951e5291106134abace7e615fcda271a6ba3b1 Mon Sep 17 00:00:00 2001 From: Bahar Date: Mon, 21 Aug 2023 11:04:23 +0800 Subject: [PATCH 10/29] bahar/Add ray drawing tool (#235) - add ray drawing tool --- .../drawing_tools_ui/drawing_tool_config.dart | 3 + .../drawing_tools_dialog.dart | 5 + .../ray/ray_drawing_tool_config.dart | 49 ++++++++ .../ray/ray_drawing_tool_config.g.dart | 30 +++++ .../ray/ray_drawing_tool_item.dart | 74 ++++++++++++ .../drawing_tools/drawing_creator.dart | 2 + .../drawing_tools/drawing_tool_widget.dart | 8 ++ .../ray/ray_drawing_creator.dart | 106 ++++++++++++++++++ .../drawing_tools/ray/ray_line_drawing.dart | 93 +++++++++++++++ 9 files changed, 370 insertions(+) create mode 100644 lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index 0356d2c31..3f8e22c7b 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:flutter/material.dart'; import 'line/line_drawing_tool_config.dart'; @@ -25,6 +26,8 @@ abstract class DrawingToolConfig extends AddOnConfig { return ContinuousDrawingToolConfig.fromJson(json); case LineDrawingToolConfig.name: return LineDrawingToolConfig.fromJson(json); + case RayDrawingToolConfig.name: + return RayDrawingToolConfig.fromJson(json); case TrendDrawingToolConfig.name: return TrendDrawingToolConfig.fromJson(json); case VerticalDrawingToolConfig.name: diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index 476665a06..f2aeb8cc8 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; @@ -52,6 +53,10 @@ class _DrawingToolsDialogState extends State { child: Text('Line'), value: LineDrawingToolConfig(), ), + DropdownMenuItem( + child: Text('Ray'), + value: RayDrawingToolConfig(), + ), DropdownMenuItem( child: Text('Trend'), value: TrendDrawingToolConfig(), diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart new file mode 100644 index 000000000..37dfe9d5e --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart @@ -0,0 +1,49 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'ray_drawing_tool_item.dart'; + +part 'ray_drawing_tool_config.g.dart'; + +/// Ray drawing tool config +@JsonSerializable() +class RayDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const RayDrawingToolConfig({ + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = DrawingPatterns.solid, + }) : super(); + + /// Initializes from JSON. + factory RayDrawingToolConfig.fromJson(Map json) => + _$RayDrawingToolConfigFromJson(json); + + /// Drawing tool name + static const String name = 'dt_ray'; + + @override + Map toJson() => _$RayDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + // TODO(maryia-binary): implement 'dotted' and 'dashed' patterns + final DrawingPatterns pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + RayDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart new file mode 100644 index 000000000..6a9a149e9 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ray_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RayDrawingToolConfig _$RayDrawingToolConfigFromJson( + Map json) => + RayDrawingToolConfig( + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); + +Map _$RayDrawingToolConfigToJson( + RayDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart new file mode 100644 index 000000000..7c9eac23f --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart @@ -0,0 +1,74 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; +import 'ray_drawing_tool_config.dart'; +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; + +/// Ray drawing tool item in the list of drawing tools +class RayDrawingToolItem extends DrawingToolItem { + /// Initializes + const RayDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + RayDrawingToolConfig config = const RayDrawingToolConfig(), + }) : super( + key: key, + title: 'Ray', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createDrawingToolItemState() => + RayDrawingToolItemState(); +} + +/// RayDrawingToolItem State class +class RayDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _lineStyle; + DrawingPatterns? _pattern; + + @override + RayDrawingToolConfig createDrawingToolConfig() => RayDrawingToolConfig( + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField(), + // TODO(maryia-binary): implement _buildPatternField() to set pattern + ], + ); + + Widget _buildColorField() => Row( + children: [ + Text( + ChartLocalization.of(context).labelColor, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: _currentLineStyle.color, + onColorChanged: (Color selectedColor) { + setState(() { + _lineStyle = _currentLineStyle.copyWith(color: selectedColor); + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as RayDrawingToolConfig).lineStyle; + + DrawingPatterns get _currentPattern => + _pattern ?? (widget.config as RayDrawingToolConfig).pattern; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart index cce8a9db2..69504f76d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart @@ -93,6 +93,8 @@ abstract class DrawingCreatorState /// Catches each single click on the chart to create a drawing. void onTap(TapUpDetails details) { + // TODO(bahar-deriv): We need to apply some refactors here once all the + // drawing tools merged. Duplicated codes should be moved here. generateDrawingId(runtimeType.toString()); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index 82db13a51..d4e8dbd78 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; import 'package:flutter/material.dart'; @@ -72,6 +73,13 @@ class DrawingToolWidget extends StatelessWidget { clearDrawingToolSelection: clearDrawingToolSelection, removeDrawing: removeDrawing, ); + case 'dt_ray': + return RayDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, + ); case 'dt_vertical': return VerticalDrawingCreator( onAddDrawing: onAddDrawing, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart new file mode 100644 index 000000000..8efc500a2 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart @@ -0,0 +1,106 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart'; +import 'package:flutter/material.dart'; + +/// Creates a Ray drawing piece by piece collected on every gesture +/// exists in a widget tree starting from selecting a line drawing tool and +/// until drawing is finished +class RayDrawingCreator extends DrawingCreator { + /// Initializes the line drawing creator. + const RayDrawingCreator({ + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, + Key? key, + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + + @override + DrawingCreatorState createState() => + _RayDrawingCreatorState(); +} + +class _RayDrawingCreatorState extends DrawingCreatorState { + /// If drawing has been started. + bool _isPenDown = false; + + @override + void onTap(TapUpDetails details) { + super.onTap(details); + final RayDrawingCreator _widget = widget as RayDrawingCreator; + + if (isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + tapCount++; + + if (!_isPenDown) { + /// Draw the initial point of the line. + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + _isPenDown = true; + + drawingParts.add(RayLineDrawing( + drawingPart: DrawingParts.marker, + startEdgePoint: edgePoints.first, + )); + } else if (!isDrawingFinished) { + /// Draw final point and the whole line. + _isPenDown = false; + isDrawingFinished = true; + final int currentTap = tapCount - 1; + final int previousTap = tapCount - 2; + + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + /// Checks if the initial point and the final point are the same. + if (edgePoints[1] == edgePoints.first) { + /// If the initial point and the 2nd point are the same, + /// remove the drawing and clean the drawing tool selection. + _widget.removeDrawing(drawingId); + _widget.clearDrawingToolSelection(); + return; + } else { + /// If the initial point and the final point are not the same, + /// draw the final point and the whole line. + drawingParts.addAll([ + RayLineDrawing( + drawingPart: DrawingParts.marker, + endEdgePoint: edgePoints[currentTap], + ), + RayLineDrawing( + drawingPart: DrawingParts.line, + startEdgePoint: edgePoints[previousTap], + endEdgePoint: edgePoints[currentTap], + exceedEnd: true, + ) + ]); + } + } + widget.onAddDrawing( + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, + ); + }); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart new file mode 100644 index 000000000..25f851c3b --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart @@ -0,0 +1,93 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Ray drawing tool. Ray is a vector defined by two points that is +/// infinite in end direction. +class RayLineDrawing extends Drawing { + /// Initializes + RayLineDrawing({ + required DrawingParts drawingPart, + EdgePoint startEdgePoint = const EdgePoint(), + EdgePoint endEdgePoint = const EdgePoint(), + bool exceedStart = false, + bool exceedEnd = false, + }) : _lineDrawing = LineDrawing( + drawingPart: drawingPart, + startEdgePoint: startEdgePoint, + endEdgePoint: endEdgePoint, + exceedStart: exceedStart, + exceedEnd: exceedEnd); + + final LineDrawing _lineDrawing; + + /// Paint the line + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final RayDrawingToolConfig config = + drawingData.config as RayDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; + + _lineDrawing.onPaint( + canvas, + size, + theme, + epochToX, + quoteToY, + DrawingData( + id: drawingData.id, + config: LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), + drawingParts: drawingData.drawingParts, + isDrawingFinished: drawingData.isDrawingFinished, + isSelected: drawingData.isSelected, + ), + updatePositionCallback, + draggableStartPoint, + draggableEndPoint: draggableEndPoint); + } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen, for any of the edge points + /// it will call "setIsEdgeDragged" callback function to determine which + /// point is clicked + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, + }) => + _lineDrawing.hitTest(position, epochToX, quoteToY, config, + draggableStartPoint, setIsStartPointDragged, + draggableEndPoint: draggableEndPoint, + setIsEndPointDragged: setIsEndPointDragged); +} From dcae40af06eb38ef30150bfdac9a93d075afcec0 Mon Sep 17 00:00:00 2001 From: Bahar Date: Wed, 6 Sep 2023 17:36:43 +0800 Subject: [PATCH 11/29] bahar/Add fibfan drawing tool (#238) * feat: add_continuous_drawing_tool * a little refactor * cleanup * refactor * refactor the code * add comment * fix_review_comments * extracting drawing tools logic from deriv chart * fix the issue with dragging line edge points * feat: add_fibfan_drawing_tool * fix_draggable_edge_points_bug * fix_draggable_edge_points_bug * add_typedef_for_onadddrawing * add_labels * fix_issue * fix_label_issue * fix review comments * fix review comments * constants added * fix issue with markers and shadow overlapping --- lib/generated/intl/messages_en.dart | 2 +- lib/l10n/intl_en.arb | 1 - .../drawing_tools_ui/drawing_tool_config.dart | 3 + .../drawing_tools_dialog.dart | 6 +- .../fibfan/fibfan_drawing_tool_config.dart | 47 +++ .../fibfan/fibfan_drawing_tool_config.g.dart | 25 ++ .../fibfan/fibfan_drawing_tool_item.dart | 80 +++++ .../data_model/drawing_paint_style.dart | 7 +- .../drawing_tools/drawing.dart | 21 -- .../drawing_tools/drawing_tool_widget.dart | 8 + .../drawing_tools/fibfan/fibfan_drawing.dart | 308 ++++++++++++++++++ .../fibfan/fibfan_drawing_creator.dart | 107 ++++++ .../drawing_tools/fibfan/label.dart | 110 +++++++ .../drawing_tools/line/line_drawing.dart | 78 +---- .../line_vector_drawing_mixin.dart | 106 ++++++ .../drawing_tools/trend/trend_drawing.dart | 6 +- 16 files changed, 812 insertions(+), 103 deletions(-) create mode 100644 lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 7b43c5db8..349e6d4ee 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -23,7 +23,7 @@ class MessageLookup extends MessageLookupByLibrary { static String m0(text) => "No results for \"${text}\""; final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { + static _notInlinedMessages(_) => { "informNoResult": m0, "labelBandsCount": MessageLookupByLibrary.simpleMessage("Bands Count"), "labelBaseLinePeriod": diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 91eacc1bd..d7b286d2f 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -46,5 +46,4 @@ "selectDrawingTool": "Select drawing tool", "labelColor": "Color", "labelFillColor": "Fill Color" - } diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index 3f8e22c7b..8ad46703a 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:flutter/material.dart'; @@ -24,6 +25,8 @@ abstract class DrawingToolConfig extends AddOnConfig { switch (json[nameKey]) { case ContinuousDrawingToolConfig.name: return ContinuousDrawingToolConfig.fromJson(json); + case FibfanDrawingToolConfig.name: + return FibfanDrawingToolConfig.fromJson(json); case LineDrawingToolConfig.name: return LineDrawingToolConfig.fromJson(json); case RayDrawingToolConfig.name: diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index f2aeb8cc8..d83112332 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; @@ -49,6 +50,10 @@ class _DrawingToolsDialogState extends State { child: Text('Continuous'), value: ContinuousDrawingToolConfig(), ), + DropdownMenuItem( + child: Text('Fib Fan'), + value: FibfanDrawingToolConfig(), + ), DropdownMenuItem( child: Text('Line'), value: LineDrawingToolConfig(), @@ -65,7 +70,6 @@ class _DrawingToolsDialogState extends State { child: Text('Vertical'), value: VerticalDrawingToolConfig(), ), - // TODO(maryia-binary): add the rest of drawing tools above ], onChanged: (dynamic config) { setState(() { diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart new file mode 100644 index 000000000..d50c28003 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart @@ -0,0 +1,47 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'fibfan_drawing_tool_item.dart'; + +part 'fibfan_drawing_tool_config.g.dart'; + +/// Fibfan drawing tool config +@JsonSerializable() +class FibfanDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const FibfanDrawingToolConfig({ + this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + }) : super(); + + /// Initializes from JSON. + factory FibfanDrawingToolConfig.fromJson(Map json) => + _$FibfanDrawingToolConfigFromJson(json); + + /// Drawing tool name + static const String name = 'dt_fibfan'; + + @override + Map toJson() => _$FibfanDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool fill style + final LineStyle fillStyle; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + FibfanDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart new file mode 100644 index 000000000..d32fba2cd --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'fibfan_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +FibfanDrawingToolConfig _$FibfanDrawingToolConfigFromJson( + Map json) => + FibfanDrawingToolConfig( + fillStyle: json['fillStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.blue) + : LineStyle.fromJson(json['fillStyle'] as Map), + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + ); + +Map _$FibfanDrawingToolConfigToJson( + FibfanDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'fillStyle': instance.fillStyle, + }; diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart new file mode 100644 index 000000000..0ec379807 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart @@ -0,0 +1,80 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; + +/// Fibfan drawing tool item in the list of drawing tools +class FibfanDrawingToolItem extends DrawingToolItem { + /// Initializes + const FibfanDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + FibfanDrawingToolConfig config = const FibfanDrawingToolConfig(), + }) : super( + key: key, + title: 'Fib fan', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createDrawingToolItemState() => + FibfanDrawingToolItemState(); +} + +/// FibfanDrawingToolItem State class +class FibfanDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _fillStyle; + LineStyle? _lineStyle; + + @override + FibfanDrawingToolConfig createDrawingToolConfig() => FibfanDrawingToolConfig( + fillStyle: _currentFillStyle, + lineStyle: _currentLineStyle, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField( + ChartLocalization.of(context).labelColor, _currentLineStyle), + _buildColorField( + ChartLocalization.of(context).labelFillColor, _currentFillStyle), + ], + ); + + Widget _buildColorField(String label, LineStyle style) => Row( + children: [ + Text( + label, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: style.color, + onColorChanged: (Color selectedColor) { + setState(() { + final LineStyle newColor = style.copyWith(color: selectedColor); + if (label == ChartLocalization.of(context).labelColor) { + _lineStyle = newColor; + } else { + _fillStyle = newColor; + } + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentFillStyle => + _fillStyle ?? (widget.config as FibfanDrawingToolConfig).fillStyle; + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as FibfanDrawingToolConfig).lineStyle; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart index 077426001..223c78ae0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart @@ -13,9 +13,14 @@ class DrawingPaintStyle { ..color = color ..strokeWidth = thickness; + /// Returns the paint style of the the faded line + Paint fadedLinePaintStyle(Color color, double thickness) => Paint() + ..color = color.withOpacity(0.7) + ..strokeWidth = thickness; + /// Returns the paint style of the inner filling of container Paint fillPaintStyle(Color color, double thickness) => Paint() - ..color = color + ..color = color.withOpacity(0.2) ..style = PaintingStyle.fill ..strokeWidth = thickness; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index cd78ab683..eac75622d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -2,7 +2,6 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; @@ -25,26 +24,6 @@ abstract class Drawing { DraggableEdgePoint? draggableEndPoint, }); - /// Calculates y intersection based on vector points. - double? getYIntersection(Vector vector, double x) { - final double x1 = vector.x0, x2 = vector.x1, x3 = x, x4 = x; - final double y1 = vector.y0, y2 = vector.y1, y3 = 0, y4 = 10000; - final double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); - final double numerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); - - double mua = numerator / denominator; - if (denominator == 0) { - if (numerator == 0) { - mua = 1; - } else { - return null; - } - } - - final double y = y1 + mua * (y2 - y1); - return y; - } - /// Calculates whether a user's touch or click intersects /// with any of the painted areas on the screen bool hitTest( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index d4e8dbd78..ef0f24546 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart'; @@ -66,6 +67,13 @@ class DrawingToolWidget extends StatelessWidget { removeDrawing: removeDrawing, shouldStopDrawing: shouldStopDrawing!, ); + case 'dt_fibfan': + return FibfanDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, + ); case 'dt_line': return LineDrawingCreator( onAddDrawing: onAddDrawing, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart new file mode 100644 index 000000000..060bf240d --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart @@ -0,0 +1,308 @@ +import 'dart:math'; + +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Fibfan drawing tool. +class FibfanDrawing extends Drawing with LineVectorDrawingMixin { + /// Initializes + FibfanDrawing({ + required this.drawingPart, + this.startEdgePoint = const EdgePoint(), + this.endEdgePoint = const EdgePoint(), + this.exceedStart = false, + this.exceedEnd = false, + }); + + /// Part of a drawing: 'marker' or 'line' + final DrawingParts drawingPart; + + /// Starting point of drawing + final EdgePoint startEdgePoint; + + /// Ending point of drawing + final EdgePoint endEdgePoint; + + /// If the line pass the start point. + final bool exceedStart; + + /// If the line pass the end point. + final bool exceedEnd; + + /// Marker radius. + final double markerRadius = 10; + + Vector _zeroDegreeVector = const Vector.zero(); + Vector _initialInnerVector = const Vector.zero(); + Vector _middleInnerVector = const Vector.zero(); + Vector _finalInnerVector = const Vector.zero(); + Vector _baseVector = const Vector.zero(); + + /// Keeps the latest position of the start and end point of drawing + Point? _startPoint, _endPoint; + + /// Check if the vector is hit + bool isVectorHit( + Vector vector, + Offset position, + LineStyle lineStyle, + ) { + final double _lineLength = + sqrt(pow(vector.y1 - vector.y0, 2) + pow(vector.x1 - vector.x0, 2)); + + /// Computes the distance between a point and a line which should be less + /// than the line thickness + 6 to make sure the user can easily click on + final double _distance = ((vector.y1 - vector.y0) * position.dx - + (vector.x1 - vector.x0) * position.dy + + vector.x1 * vector.y0 - + vector.y1 * vector.x0) / + sqrt(pow(vector.y1 - vector.y0, 2) + pow(vector.x1 - vector.x0, 2)); + + final double _xDistToStart = position.dx - vector.x0; + final double _yDistToStart = position.dy - vector.y0; + + /// Limit the detection to start and end point of the line + final double _dotProduct = (_xDistToStart * (vector.x1 - vector.x0) + + _yDistToStart * (vector.y1 - vector.y0)) / + _lineLength; + + final bool _isWithinRange = _dotProduct > 0 && _dotProduct < _lineLength; + + return _isWithinRange && _distance.abs() <= lineStyle.thickness + 6; + } + + /// Returns the Triangle path + Path getTrianglePath( + Vector startVector, + Vector endVector, + ) => + Path() + ..moveTo(startVector.x0, startVector.y0) + ..lineTo(startVector.x1, startVector.y1) + ..lineTo(endVector.x1, endVector.y1) + ..close(); + + /// Draw the shaded area between two vectors + void _drawTriangle( + Canvas canvas, + DrawingPaintStyle paint, + FibfanDrawingToolConfig config, + Vector endVector, + ) { + final LineStyle fillStyle = config.fillStyle; + final Path path = getTrianglePath(_baseVector, endVector); + + canvas.drawPath( + path, + paint.fillPaintStyle( + fillStyle.color, + fillStyle.thickness, + )); + } + + /// Paint the line + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final DrawingPaintStyle paint = DrawingPaintStyle(); + final FibfanDrawingToolConfig config = + drawingData.config as FibfanDrawingToolConfig; + final LineStyle lineStyle = config.lineStyle; + final Paint linePaintStyle = + paint.linePaintStyle(lineStyle.color, lineStyle.thickness); + + _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + + final double startXCoord = _startPoint!.x; + final double startQuoteToY = _startPoint!.y; + + final double endXCoord = _endPoint!.x; + final double endQuoteToY = _endPoint!.y; + + if (drawingPart == DrawingParts.marker) { + if (endEdgePoint.epoch != 0 && endQuoteToY != 0) { + /// Draw second point + canvas.drawCircle( + Offset(endXCoord, endQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } else if (startEdgePoint.epoch != 0 && startQuoteToY != 0) { + /// Draw first point + canvas.drawCircle( + Offset(startXCoord, startQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } + } else if (drawingPart == DrawingParts.line) { + /// Draw the shaded area between two vectors + Vector _getLineVector( + double _endXCoord, + double _startQuoteToY, + ) => + getLineVector( + startXCoord, + startQuoteToY, + _endXCoord, + _startQuoteToY, + exceedEnd: true, + ); + + /// Defines the degree of initial inner vector from the base vector + const double _initialVectorDegree = 0.618; + + /// Defines the degree of middle inner vector from the base vector + const double _middleVectorDegree = 0.5; + + /// Defines the degree of final inner vector from the base vector + const double _finalVectorDegree = 0.382; + + /// Add vectors + _zeroDegreeVector = _getLineVector(endXCoord, endQuoteToY); + _initialInnerVector = _getLineVector( + endXCoord, + ((endQuoteToY - startQuoteToY) * _initialVectorDegree) + startQuoteToY, + ); + + _middleInnerVector = _getLineVector( + endXCoord, + ((endQuoteToY - startQuoteToY) * _middleVectorDegree) + startQuoteToY, + ); + _finalInnerVector = _getLineVector( + endXCoord, + ((endQuoteToY - startQuoteToY) * _finalVectorDegree) + startQuoteToY, + ); + _baseVector = _getLineVector(endXCoord, startQuoteToY); + + /// Draw shadows + _drawTriangle(canvas, paint, config, _zeroDegreeVector); + _drawTriangle(canvas, paint, config, _initialInnerVector); + _drawTriangle(canvas, paint, config, _middleInnerVector); + _drawTriangle(canvas, paint, config, _finalInnerVector); + + /// Draw markers again to hide their overlap with shadows + canvas + ..drawCircle( + Offset(startXCoord, startQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()) + ..drawCircle( + Offset(endXCoord, endQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()) + + /// Draw vectors + ..drawLine( + Offset(_baseVector.x0, _baseVector.y0), + Offset(_baseVector.x1, _baseVector.y1), + linePaintStyle, + ) + ..drawLine( + Offset(_finalInnerVector.x0, _finalInnerVector.y0), + Offset(_finalInnerVector.x1, _finalInnerVector.y1), + linePaintStyle, + ) + ..drawLine( + Offset(_middleInnerVector.x0, _middleInnerVector.y0), + Offset(_middleInnerVector.x1, _middleInnerVector.y1), + linePaintStyle, + ) + ..drawLine( + Offset(_initialInnerVector.x0, _initialInnerVector.y0), + Offset(_initialInnerVector.x1, _initialInnerVector.y1), + linePaintStyle, + ) + ..drawLine( + Offset(_zeroDegreeVector.x0, _zeroDegreeVector.y0), + Offset(_zeroDegreeVector.x1, _zeroDegreeVector.y1), + linePaintStyle, + ); + + /// Draw labels + Label( + startXCoord: startXCoord.toInt(), + endXCoord: endXCoord.toInt(), + ) + ..drawLabel( + canvas, lineStyle, zeroDegreeVectorPercentage, _zeroDegreeVector) + ..drawLabel(canvas, lineStyle, initialInnerVectorPercentage, + _initialInnerVector) + ..drawLabel( + canvas, lineStyle, middleInnerVectorPercentage, _middleInnerVector) + ..drawLabel( + canvas, lineStyle, finalInnerVectorPercentage, _finalInnerVector) + ..drawLabel(canvas, lineStyle, baseVectorPercentage, _baseVector); + } + } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen, for any of the edge points + /// it will set "isDragged" to determine which point is clicked + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, + }) { + final LineStyle lineStyle = config.toJson()['lineStyle']; + bool _isVectorHit(Vector vector) => + isVectorHit(vector, position, lineStyle); + + setIsStartPointDragged(isDragged: false); + setIsEndPointDragged!(isDragged: false); + + /// Check if start point clicked + if (_startPoint!.isClicked(position, markerRadius)) { + setIsStartPointDragged(isDragged: true); + } + + /// Check if end point clicked + if (_endPoint!.isClicked(position, markerRadius)) { + setIsEndPointDragged(isDragged: true); + } + return _isVectorHit(_baseVector) || + _isVectorHit(_finalInnerVector) || + _isVectorHit(_middleInnerVector) || + _isVectorHit(_initialInnerVector) || + _isVectorHit(_zeroDegreeVector) || + (_startPoint!.isClicked(position, markerRadius) || + _endPoint!.isClicked(position, markerRadius)); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart new file mode 100644 index 000000000..df63c3b7a --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart @@ -0,0 +1,107 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart'; +import 'package:flutter/material.dart'; + +/// Creates a Fibfan drawing piece by piece collected on every gesture +/// exists in a widget tree starting from selecting a line drawing tool and +/// until drawing is finished +class FibfanDrawingCreator extends DrawingCreator { + /// Initializes the fibfan drawing creator. + const FibfanDrawingCreator({ + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, + Key? key, + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + + @override + DrawingCreatorState createState() => + _FibfanDrawingCreatorState(); +} + +class _FibfanDrawingCreatorState extends DrawingCreatorState { + /// If drawing has been started. + bool _isPenDown = false; + + @override + void onTap(TapUpDetails details) { + super.onTap(details); + final FibfanDrawingCreator _widget = widget as FibfanDrawingCreator; + + if (isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + tapCount++; + + if (!_isPenDown) { + /// Draw the initial point of the line. + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + _isPenDown = true; + + drawingParts.add(FibfanDrawing( + drawingPart: DrawingParts.marker, + startEdgePoint: edgePoints.first, + )); + } else if (!isDrawingFinished) { + /// Draw final point and the whole line. + _isPenDown = false; + isDrawingFinished = true; + final int _currentTap = tapCount - 1; + final int _previousTap = tapCount - 2; + + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + /// Checks if the initial point and the final point are the same. + if (edgePoints[1] == edgePoints.first) { + /// If the initial point and the 2nd point are the same, + /// remove the drawing and clean the drawing tool selection. + _widget.removeDrawing(drawingId); + _widget.clearDrawingToolSelection(); + return; + } else { + /// If the initial point and the final point are not the same, + /// draw the final point and the whole line. + drawingParts.addAll([ + FibfanDrawing( + drawingPart: DrawingParts.marker, + endEdgePoint: edgePoints[_currentTap], + ), + FibfanDrawing( + drawingPart: DrawingParts.line, + startEdgePoint: edgePoints[_previousTap], + endEdgePoint: edgePoints[_currentTap], + exceedStart: true, + exceedEnd: true, + ) + ]); + } + } + widget.onAddDrawing( + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, + ); + }); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart new file mode 100644 index 000000000..624c73ab3 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; + +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; + +/// A constant for the zero degree vector label +const String zeroDegreeVectorPercentage = '0'; + +/// A constant for the initial inner vector label +const String initialInnerVectorPercentage = '38.2'; + +/// A constant for the middle vector label +const String middleInnerVectorPercentage = '50'; + +/// A constant for the final vector label +const String finalInnerVectorPercentage = '61.8'; + +/// A constant for the base vector label +const String baseVectorPercentage = '100'; + +/// A class for drawing the vector label +class Label { + /// Initializes the vector label class + Label({ + required this.startXCoord, + required this.endXCoord, + }); + + /// Start of the drawing + final int startXCoord; + + /// End of the drawing + final int endXCoord; + + /// Returns the x position of the label + double _getX( + String label, { + bool isRightSide = false, + }) { + switch (label) { + case zeroDegreeVectorPercentage: + return isRightSide ? 345 : 25; + case initialInnerVectorPercentage: + case finalInnerVectorPercentage: + return isRightSide ? 325 : 40; + case middleInnerVectorPercentage: + return isRightSide ? 335 : 30; + default: + return isRightSide ? 330 : 10; + } + } + + /// Returns the position of the label on the right side of the chart + Offset _getRightSideLabelsPosition(String label, Vector vector) { + final double x = _getX(label, isRightSide: true); + + final double y = (((vector.y1 - vector.y0) / (vector.x1 - vector.x0)) * + (x - vector.x0)) + + vector.y0; + return Offset(x, y); + } + + /// Returns the position of the label on the left side of the chart + Offset _getLeftSideLabelsPosition(String label, Vector vector) { + final double x = _getX(label); + + final double y = (((vector.y1 - vector.y0) / (vector.x1 - vector.x0)) * + (x - vector.x0)) + + vector.y0; + return Offset(5, y); + } + + /// Returns the text painter for adding labels + TextPainter getTextPainter(String label, Offset textOffset, Color color) { + final TextStyle textStyle = TextStyle( + color: color, + fontWeight: FontWeight.bold, + fontSize: 13, + ); + + final TextSpan textSpan = TextSpan( + text: '$label%', + style: textStyle, + ); + + return TextPainter( + text: textSpan, + textDirection: TextDirection.ltr, + ); + } + + /// Draw the vector label + void drawLabel( + Canvas canvas, + LineStyle lineStyle, + String label, + Vector endVector, + ) { + final Offset labelOffset = (startXCoord > endXCoord && startXCoord > 10) + ? _getLeftSideLabelsPosition(label, endVector) + : (startXCoord < endXCoord && startXCoord < 325) + ? _getRightSideLabelsPosition(label, endVector) + : Offset.zero; + if (labelOffset != Offset.zero) { + getTextPainter(label, labelOffset, lineStyle.color) + ..layout() + ..paint(canvas, labelOffset); + } + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index a6e369a31..97f590ffa 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -11,12 +11,13 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; /// Line drawing tool. A line is a vector defined by two points that is /// infinite in both directions. -class LineDrawing extends Drawing { +class LineDrawing extends Drawing with LineVectorDrawingMixin { /// Initializes LineDrawing({ required this.drawingPart, @@ -49,79 +50,6 @@ class LineDrawing extends Drawing { /// Keeps the latest position of the start and end point of drawing Point? _startPoint, _endPoint; - /// Vector of the line - Vector getLineVector( - double startXCoord, - double startQuoteToY, - double endXCoord, - double endQuoteToY, - ) { - Vector vec = Vector( - x0: startXCoord, - y0: startQuoteToY, - x1: endXCoord, - y1: endQuoteToY, - ); - - late double earlier, later; - if (exceedEnd && !exceedStart) { - earlier = vec.x0; - if (vec.x0 > vec.x1) { - later = vec.x1 - 1000; - } else { - later = vec.x1 + 1000; - } - } - if (exceedStart && !exceedEnd) { - later = vec.x1; - - if (vec.x0 > vec.x1) { - earlier = vec.x0 + 1000; - } else { - earlier = vec.x0 - 1000; - } - } - - if (exceedStart && exceedEnd) { - if (vec.x0 > vec.x1) { - vec = Vector( - x0: endXCoord, - y0: endQuoteToY, - x1: startXCoord, - y1: startQuoteToY, - ); - } - - earlier = vec.x0 - 1000; - later = vec.x1 + 1000; - } - - if (!exceedEnd && !exceedStart) { - if (vec.x0 > vec.x1) { - vec = Vector( - x0: endXCoord, - y0: endQuoteToY, - x1: startXCoord, - y1: startQuoteToY, - ); - } - earlier = vec.x0; - later = vec.x1; - } - - final double startY = getYIntersection(vec, earlier) ?? 0, - endingY = getYIntersection(vec, later) ?? 0, - startX = earlier, - endingX = later; - - return Vector( - x0: startX, - y0: startY, - x1: endingX, - y1: endingY, - ); - } - /// Paint the line @override void onPaint( @@ -180,6 +108,8 @@ class LineDrawing extends Drawing { startQuoteToY, endXCoord, endQuoteToY, + exceedStart: exceedStart, + exceedEnd: exceedEnd, ); if (pattern == DrawingPatterns.solid) { diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart new file mode 100644 index 000000000..aeb140f22 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart @@ -0,0 +1,106 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/distance_constants.dart'; + +import 'data_model/vector.dart'; + +/// Mixin to calculate the vector of a line and related functionalities which +/// a drawing tool with a line should have. +mixin LineVectorDrawingMixin { + /// Gets the vector of a line which is defined by two points. (4 values) + /// + /// If [exceedStart] is true, the vector line will be extended further than + /// the start coordinates. Same thing is there for [exceedEnd] and end + /// coordinates. + Vector getLineVector( + double startXCoord, + double startYCoord, + double endXCoord, + double endYCoord, { + bool exceedStart = false, + bool exceedEnd = false, + }) { + Vector vec = Vector( + x0: startXCoord, + y0: startYCoord, + x1: endXCoord, + y1: endYCoord, + ); + + late double earlier, later; + if (exceedEnd && !exceedStart) { + earlier = vec.x0; + if (vec.x0 > vec.x1) { + later = vec.x1 - DrawingToolDistance.horizontalDistance; + } else { + later = vec.x1 + DrawingToolDistance.horizontalDistance; + } + } + if (exceedStart && !exceedEnd) { + later = vec.x1; + + if (vec.x0 > vec.x1) { + earlier = vec.x0 + DrawingToolDistance.horizontalDistance; + } else { + earlier = vec.x0 - DrawingToolDistance.horizontalDistance; + } + } + + if (exceedStart && exceedEnd) { + if (vec.x0 > vec.x1) { + vec = Vector( + x0: endXCoord, + y0: endYCoord, + x1: startXCoord, + y1: startYCoord, + ); + } + + earlier = vec.x0 - DrawingToolDistance.horizontalDistance; + later = vec.x1 + DrawingToolDistance.horizontalDistance; + } + + if (!exceedEnd && !exceedStart) { + if (vec.x0 > vec.x1) { + vec = Vector( + x0: endXCoord, + y0: endYCoord, + x1: startXCoord, + y1: startYCoord, + ); + } + earlier = vec.x0; + later = vec.x1; + } + + final double startY = _getYIntersection(vec, earlier) ?? 0, + endingY = _getYIntersection(vec, later) ?? 0, + startX = earlier, + endingX = later; + + return Vector( + x0: startX, + y0: startY, + x1: endingX, + y1: endingY, + ); + } + + /// Calculates y intersection based on vector points. + double? _getYIntersection(Vector vector, double x) { + final double x1 = vector.x0, x2 = vector.x1, x3 = x, x4 = x; + final double y1 = vector.y0, y2 = vector.y1, y3 = 0, y4 = 10000; + final double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + final double numerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); + + double mua = numerator / denominator; + if (denominator == 0) { + if (numerator == 0) { + mua = 1; + } else { + return null; + } + } + + final double y = y1 + mua * (y2 - y1); + return y; + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart index a7c85ad35..ea26d15d7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart @@ -225,8 +225,7 @@ class TrendDrawing extends Drawing { drawingData.isSelected ? paint.glowyLinePaintStyle( fillStyle.color.withOpacity(0.2), lineStyle.thickness) - : paint.fillPaintStyle( - fillStyle.color.withOpacity(0.2), lineStyle.thickness), + : paint.fillPaintStyle(fillStyle.color, lineStyle.thickness), ) ..drawRect( _mainRect, @@ -237,8 +236,7 @@ class TrendDrawing extends Drawing { drawingData.isSelected ? paint.glowyLinePaintStyle( fillStyle.color.withOpacity(0.2), lineStyle.thickness) - : paint.fillPaintStyle( - fillStyle.color.withOpacity(0.2), lineStyle.thickness), + : paint.fillPaintStyle(fillStyle.color, lineStyle.thickness), ) ..drawRect( _middleRect, From 55e6d83334f5d793bad39ff7ec1828626b4d936f Mon Sep 17 00:00:00 2001 From: Bahar Date: Thu, 7 Sep 2023 10:57:43 +0800 Subject: [PATCH 12/29] bahar/Add channel drawing tool (#237) - add channel drawing --- .../channel/channel_drawing_tool_config.dart | 53 ++++ .../channel_drawing_tool_config.g.dart | 34 +++ .../channel/channel_drawing_tool_item.dart | 88 ++++++ .../drawing_tools_ui/drawing_tool_config.dart | 3 + .../drawing_tools_dialog.dart | 5 + .../channel/channel_drawing.dart | 289 ++++++++++++++++++ .../channel/channel_drawing_creator.dart | 124 ++++++++ .../continuous/continuous_line_drawing.dart | 3 + .../drawing_tools/drawing.dart | 3 + .../drawing_tools/drawing_painter.dart | 35 ++- .../drawing_tools/drawing_tool_widget.dart | 9 + .../drawing_tools/fibfan/fibfan_drawing.dart | 3 + .../drawing_tools/line/line_drawing.dart | 6 +- .../drawing_tools/ray/ray_line_drawing.dart | 3 + .../drawing_tools/trend/trend_drawing.dart | 3 + .../vertical/vertical_drawing.dart | 3 + 16 files changed, 661 insertions(+), 3 deletions(-) create mode 100644 lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart new file mode 100644 index 000000000..9fbb20b95 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart @@ -0,0 +1,53 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'channel_drawing_tool_item.dart'; + +part 'channel_drawing_tool_config.g.dart'; + +/// Channel drawing tool config +@JsonSerializable() +class ChannelDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const ChannelDrawingToolConfig({ + this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = DrawingPatterns.solid, + }) : super(); + + /// Initializes from JSON. + factory ChannelDrawingToolConfig.fromJson(Map json) => + _$ChannelDrawingToolConfigFromJson(json); + + /// Drawing tool name + static const String name = 'dt_channel'; + + @override + Map toJson() => _$ChannelDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool fill style + final LineStyle fillStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + // TODO(maryia-binary): implement 'dotted' and 'dashed' patterns + final DrawingPatterns pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + ChannelDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart new file mode 100644 index 000000000..1632d4e76 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'channel_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ChannelDrawingToolConfig _$ChannelDrawingToolConfigFromJson( + Map json) => + ChannelDrawingToolConfig( + fillStyle: json['fillStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.blue) + : LineStyle.fromJson(json['fillStyle'] as Map), + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); + +Map _$ChannelDrawingToolConfigToJson( + ChannelDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'fillStyle': instance.fillStyle, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart new file mode 100644 index 000000000..a5858c80c --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart @@ -0,0 +1,88 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; +import 'channel_drawing_tool_config.dart'; +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; + +/// Channel drawing tool item in the list of drawing tools +class ChannelDrawingToolItem extends DrawingToolItem { + /// Initializes + const ChannelDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + ChannelDrawingToolConfig config = const ChannelDrawingToolConfig(), + }) : super( + key: key, + title: 'Channel', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createDrawingToolItemState() => + ChannelDrawingToolItemState(); +} + +/// ChannelDrawingToolItem State class +class ChannelDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _fillStyle; + LineStyle? _lineStyle; + DrawingPatterns? _pattern; + + @override + ChannelDrawingToolConfig createDrawingToolConfig() => + ChannelDrawingToolConfig( + fillStyle: _currentFillStyle, + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField( + ChartLocalization.of(context).labelColor, _currentLineStyle), + _buildColorField( + ChartLocalization.of(context).labelFillColor, _currentFillStyle), + // TODO(maryia-binary): implement _buildPatternField() to set pattern + ], + ); + + Widget _buildColorField(String label, LineStyle style) => Row( + children: [ + Text( + label, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: style.color, + onColorChanged: (Color selectedColor) { + setState(() { + final LineStyle newColor = style.copyWith(color: selectedColor); + if (label == ChartLocalization.of(context).labelColor) { + _lineStyle = newColor; + } else { + _fillStyle = newColor; + } + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentFillStyle => + _fillStyle ?? (widget.config as ChannelDrawingToolConfig).fillStyle; + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as ChannelDrawingToolConfig).lineStyle; + + DrawingPatterns get _currentPattern => + _pattern ?? (widget.config as ChannelDrawingToolConfig).pattern; +} diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index 8ad46703a..cd1b77042 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; @@ -23,6 +24,8 @@ abstract class DrawingToolConfig extends AddOnConfig { } switch (json[nameKey]) { + case ChannelDrawingToolConfig.name: + return ChannelDrawingToolConfig.fromJson(json); case ContinuousDrawingToolConfig.name: return ContinuousDrawingToolConfig.fromJson(json); case FibfanDrawingToolConfig.name: diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index d83112332..a9f05dca1 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; @@ -46,6 +47,10 @@ class _DrawingToolsDialogState extends State { value: _selectedDrawingTool, hint: Text(ChartLocalization.of(context).selectDrawingTool), items: const >[ + DropdownMenuItem( + child: Text('Channel'), + value: ChannelDrawingToolConfig(), + ), DropdownMenuItem( child: Text('Continuous'), value: ContinuousDrawingToolConfig(), diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart new file mode 100644 index 000000000..58de3c3ea --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart @@ -0,0 +1,289 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/vector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Channel drawing tool. A channel is 2 parallel lines that +/// created with 3 points. +class ChannelDrawing extends Drawing with LineVectorDrawingMixin { + /// Initializes + ChannelDrawing({ + required this.drawingPart, + this.startEdgePoint = const EdgePoint(), + this.middleEdgePoint = const EdgePoint(), + this.endEdgePoint = const EdgePoint(), + this.isDrawingFinished = false, + }); + + /// Part of a drawing: 'marker' or 'line' + final DrawingParts drawingPart; + + /// Starting point of drawing which is used as the start point of initial + /// vector + final EdgePoint startEdgePoint; + + /// Second point of drawing which is used to draw the end point of initial + /// vector + final EdgePoint middleEdgePoint; + + /// Ending point of drawing which is used to draw the second(final) vector + final EdgePoint endEdgePoint; + + /// Marker radius. + final double markerRadius = 10; + + /// Flag to show if drawing is finished. + final bool isDrawingFinished; + + Vector _initialVector = const Vector.zero(); + Vector _finalVector = const Vector.zero(); + + /// Keeps the latest position of the start and end point of drawing + Point? _startPoint, _middlePoint, _endPoint; + + /// Returns the Parallelogram path + Path getParallelogramPath( + Vector startVector, + Vector endVector, + ) => + Path() + ..moveTo(startVector.x0, startVector.y0) + ..lineTo(startVector.x1, startVector.y1) + ..lineTo(endVector.x1, endVector.y1) + ..lineTo(endVector.x0, endVector.y0) + ..close(); + + /// Draw the shaded area between two vectors + void drawParallelogram( + Canvas canvas, + ChannelDrawingToolConfig config, + DrawingPaintStyle paint, + Vector startVector, + Vector endVector, + ) { + final LineStyle fillStyle = config.fillStyle; + + /// The path for the shaded area between two lines + final Path path = getParallelogramPath(startVector, endVector); + + canvas.drawPath( + path, paint.fillPaintStyle(fillStyle.color, fillStyle.thickness)); + } + + /// Paint the line + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, + DraggableEdgePoint? draggableEndPoint, + }) { + final DrawingPaintStyle paint = DrawingPaintStyle(); + + /// Get the latest config of any drawing tool which is used to draw the line + final ChannelDrawingToolConfig config = + drawingData.config as ChannelDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; + + _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); + _middlePoint = + updatePositionCallback(middleEdgePoint, draggableMiddlePoint!); + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + + final double startXCoord = _startPoint!.x; + final double startQuoteToY = _startPoint!.y; + + final double middleXCoord = _middlePoint!.x; + final double middleQuoteToY = _middlePoint!.y; + + final double height = middleQuoteToY - _endPoint!.y; + + final double endXCoord = middleXCoord; + final double endQuoteToY = middleQuoteToY - height; + + _initialVector = getLineVector( + startXCoord, + startQuoteToY, + middleXCoord, + middleQuoteToY, + ); + + _finalVector = getLineVector( + endXCoord, + endQuoteToY, + startXCoord, + startQuoteToY - height, + ); + + if (drawingPart == DrawingParts.marker) { + if (endEdgePoint.epoch != 0 && endQuoteToY != 0) { + /// Draw final point + canvas.drawCircle( + Offset(middleXCoord, middleQuoteToY - height), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } else if (startEdgePoint.epoch != 0 && startQuoteToY != 0) { + /// Draw first point + canvas.drawCircle( + Offset(startXCoord, startQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } else if (middleEdgePoint.epoch != 0 && middleQuoteToY != 0) { + /// Draw second point + canvas.drawCircle( + Offset(middleXCoord, middleQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } + } else if (drawingPart == DrawingParts.line) { + if (endEdgePoint.epoch != 0 && endQuoteToY != 0) { + /// Draw second line + + drawParallelogram( + canvas, + config, + paint, + _initialVector, + _finalVector, + ); + if (pattern == DrawingPatterns.solid) { + /// Drawing the markers in the final spte again to hide the overlap + /// of fill coler and the markers + canvas + ..drawCircle( + Offset(startXCoord, startQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()) + ..drawCircle( + Offset(middleXCoord, middleQuoteToY), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()) + ..drawCircle( + Offset(middleXCoord, middleQuoteToY - height), + markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()) + + /// Draw first line again to hide the overlap of fill color and line + ..drawLine( + Offset(_initialVector.x0, _initialVector.y0), + Offset(_initialVector.x1, _initialVector.y1), + drawingData.isSelected + ? paint.glowyLinePaintStyle( + lineStyle.color, lineStyle.thickness) + : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), + ) + ..drawLine( + Offset(_finalVector.x0, _finalVector.y0), + Offset(_finalVector.x1, _finalVector.y1), + drawingData.isSelected + ? paint.glowyLinePaintStyle( + lineStyle.color, lineStyle.thickness) + : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), + ); + } + } else if (startEdgePoint.epoch != 0 && startQuoteToY != 0) { + /// Draw first line + if (pattern == DrawingPatterns.solid) { + canvas.drawLine( + Offset(_initialVector.x0, _initialVector.y0), + Offset(_initialVector.x1, _initialVector.y1), + drawingData.isSelected + ? paint.glowyLinePaintStyle( + lineStyle.color, lineStyle.thickness) + : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), + ); + } + } + } + } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen, for any of the edge points + /// it will call "setIsEdgeDragged" callback function to determine which + /// point is clicked + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, + DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, + void Function({required bool isDragged})? setIsEndPointDragged, + }) { + setIsStartPointDragged(isDragged: false); + setIsMiddlePointDragged!(isDragged: false); + setIsEndPointDragged!(isDragged: false); + + final double middleXCoord = _middlePoint!.x; + final double middleQuoteToY = _middlePoint!.y; + + final double height = middleQuoteToY - _endPoint!.y; + + final double endXCoord = middleXCoord; + final double endQuoteToY = middleQuoteToY - height; + + /// Check if start point clicked + if (_startPoint!.isClicked(position, markerRadius)) { + setIsStartPointDragged(isDragged: true); + } + + /// Check if middle point clicked + if (_middlePoint!.isClicked(position, markerRadius)) { + setIsMiddlePointDragged(isDragged: true); + } + + /// Check if end point clicked, since the endPoint position is dependendat + /// to middle point position, we need to check it differently + final Point endPoint = Point(x: endXCoord, y: endQuoteToY); + + /// Check if end point clicked + if (endPoint.isClicked(position, markerRadius)) { + setIsEndPointDragged(isDragged: true); + } + + /// Detect the area between 2 parallel lines + final Path path = getParallelogramPath(_initialVector, _finalVector); + + return (isDrawingFinished && path.contains(position)) || + (_startPoint!.isClicked(position, markerRadius) || + _middlePoint!.isClicked(position, markerRadius) || + endPoint.isClicked(position, markerRadius)); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart new file mode 100644 index 000000000..54ab510f2 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart @@ -0,0 +1,124 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:flutter/material.dart'; +import '../data_model/drawing_parts.dart'; + +/// Creates a Channel drawing piece by piece collected on every gesture +/// exists in a widget tree starting from selecting the channel drawing tool +/// and until drawing should be finished. +class ChannelDrawingCreator extends DrawingCreator { + /// Initializes the channel drawing creator. + const ChannelDrawingCreator({ + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, + required this.shouldStopDrawing, + Key? key, + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + + /// A flag to show when to stop drawing only for drawings which don't have + /// fixed number of points like continuous drawing + final bool shouldStopDrawing; + + @override + DrawingCreatorState createState() => + _ChannelDrawingCreatorState(); +} + +class _ChannelDrawingCreatorState extends DrawingCreatorState { + @override + void onTap(TapUpDetails details) { + super.onTap(details); + final ChannelDrawingCreator _widget = widget as ChannelDrawingCreator; + + if (isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + tapCount++; + + if (edgePoints.isEmpty) { + /// Draw the initial point of the line. + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + drawingParts.add(ChannelDrawing( + drawingPart: DrawingParts.marker, + startEdgePoint: edgePoints.first, + )); + } else if (edgePoints.length == 1) { + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + /// Checks if the initial point and the final point are the same. + if (edgePoints[1] == edgePoints.first) { + /// If the initial point and the 2nd point are the same, + /// remove the drawing and clean the drawing tool selection. + _widget.removeDrawing(drawingId); + _widget.clearDrawingToolSelection(); + return; + } else { + /// If the initial point and the final point are not the same, + /// draw the final point and the whole line. + drawingParts.addAll([ + ChannelDrawing( + drawingPart: DrawingParts.marker, + middleEdgePoint: edgePoints[1], + ), + ChannelDrawing( + drawingPart: DrawingParts.line, + startEdgePoint: edgePoints[0], + middleEdgePoint: edgePoints[1], + ) + ]); + } + } else if (edgePoints.length == 2) { + /// Draw final point and the whole line. + isDrawingFinished = true; + edgePoints.add(EdgePoint( + epoch: edgePoints[1].epoch, + quote: widget.quoteFromCanvasY(position!.dy), + )); + + /// If the initial point and the final point are not the same, + /// draw the final point and the whole line. + drawingParts.addAll([ + ChannelDrawing( + drawingPart: DrawingParts.marker, + middleEdgePoint: edgePoints[1], + endEdgePoint: edgePoints[2], + ), + ChannelDrawing( + drawingPart: DrawingParts.line, + startEdgePoint: edgePoints[0], + middleEdgePoint: edgePoints[1], + endEdgePoint: edgePoints[2], + isDrawingFinished: isDrawingFinished, + ) + ]); + } + widget.onAddDrawing( + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, + ); + }); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart index 1b79306e4..5716c6e56 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -45,6 +45,7 @@ class ContinuousLineDrawing extends Drawing { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final ContinuousDrawingToolConfig config = @@ -83,7 +84,9 @@ class ContinuousLineDrawing extends Drawing { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) => _lineDrawing.hitTest(position, epochToX, quoteToY, config, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index eac75622d..3c5dbe59d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -21,6 +21,7 @@ abstract class Drawing { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }); @@ -33,7 +34,9 @@ abstract class Drawing { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index 6aa151fae..9c057a8d0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -50,6 +50,7 @@ class DrawingPainter extends StatefulWidget { class _DrawingPainterState extends State { bool _isDrawingDragged = false; DraggableEdgePoint _draggableStartPoint = DraggableEdgePoint(); + DraggableEdgePoint _draggableMiddlePoint = DraggableEdgePoint(); DraggableEdgePoint _draggableEndPoint = DraggableEdgePoint(); Offset? _previousPosition; @@ -70,7 +71,18 @@ class _DrawingPainterState extends State { xAxis, widget.quoteFromCanvasY, widget.quoteToCanvasY, - isOtherEndDragged: _draggableEndPoint.isDragged, + isOtherEndDragged: _draggableEndPoint.isDragged || + _draggableMiddlePoint.isDragged, + ); + _draggableMiddlePoint = _draggableMiddlePoint.copyWith( + isDrawingDragged: _isDrawingDragged, + )..updatePositionWithLocalPositions( + details.delta, + xAxis, + widget.quoteFromCanvasY, + widget.quoteToCanvasY, + isOtherEndDragged: _draggableEndPoint.isDragged || + _draggableStartPoint.isDragged, ); _draggableEndPoint = _draggableEndPoint.copyWith( @@ -80,7 +92,8 @@ class _DrawingPainterState extends State { xAxis, widget.quoteFromCanvasY, widget.quoteToCanvasY, - isOtherEndDragged: _draggableStartPoint.isDragged, + isOtherEndDragged: _draggableStartPoint.isDragged || + _draggableMiddlePoint.isDragged, ); }); } @@ -117,6 +130,9 @@ class _DrawingPainterState extends State { _draggableStartPoint = _draggableStartPoint.copyWith( isDragged: false, ); + _draggableMiddlePoint = _draggableMiddlePoint.copyWith( + isDragged: false, + ); _draggableEndPoint = _draggableEndPoint.copyWith( isDragged: false, ); @@ -132,6 +148,9 @@ class _DrawingPainterState extends State { _draggableStartPoint = _draggableStartPoint.copyWith( isDragged: false, ); + _draggableMiddlePoint = _draggableMiddlePoint.copyWith( + isDragged: false, + ); _draggableEndPoint = _draggableEndPoint.copyWith( isDragged: false, ); @@ -145,6 +164,7 @@ class _DrawingPainterState extends State { epochToX: xAxis.xFromEpoch, quoteToY: widget.quoteToCanvasY, draggableStartPoint: _draggableStartPoint, + draggableMiddlePoint: _draggableMiddlePoint, isDrawingToolSelected: widget.selectedDrawingTool != null, draggableEndPoint: _draggableEndPoint, updatePositionCallback: ( @@ -161,6 +181,10 @@ class _DrawingPainterState extends State { _draggableStartPoint = _draggableStartPoint.copyWith(isDragged: isDragged); }, + setIsMiddlePointDragged: ({required bool isDragged}) { + _draggableMiddlePoint = + _draggableMiddlePoint.copyWith(isDragged: isDragged); + }, setIsEndPointDragged: ({required bool isDragged}) { _draggableEndPoint = _draggableEndPoint.copyWith(isDragged: isDragged); @@ -183,7 +207,9 @@ class _DrawingPainter extends CustomPainter { required this.setIsStartPointDragged, required this.updatePositionCallback, this.isDrawingToolSelected = false, + this.draggableMiddlePoint, this.draggableEndPoint, + this.setIsMiddlePointDragged, this.setIsEndPointDragged, }); @@ -193,8 +219,10 @@ class _DrawingPainter extends CustomPainter { final double Function(int x) epochToX; final double Function(double y) quoteToY; final DraggableEdgePoint draggableStartPoint; + final DraggableEdgePoint? draggableMiddlePoint; final DraggableEdgePoint? draggableEndPoint; final void Function({required bool isDragged}) setIsStartPointDragged; + final void Function({required bool isDragged})? setIsMiddlePointDragged; final void Function({required bool isDragged})? setIsEndPointDragged; final Point Function( EdgePoint edgePoint, @@ -213,6 +241,7 @@ class _DrawingPainter extends CustomPainter { drawingData, updatePositionCallback, draggableStartPoint, + draggableMiddlePoint: draggableMiddlePoint, draggableEndPoint: draggableEndPoint, ); } @@ -234,7 +263,9 @@ class _DrawingPainter extends CustomPainter { drawingData.config, draggableStartPoint, setIsStartPointDragged, + draggableMiddlePoint: draggableMiddlePoint, draggableEndPoint: draggableEndPoint, + setIsMiddlePointDragged: setIsMiddlePointDragged, setIsEndPointDragged: setIsEndPointDragged, )) { if (isDrawingToolSelected) { diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index ef0f24546..e578cda25 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart'; import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; @@ -59,6 +60,14 @@ class DrawingToolWidget extends StatelessWidget { final String drawingToolType = selectedDrawingTool.toJson()['name']; switch (drawingToolType) { + case 'dt_channel': + return ChannelDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, + shouldStopDrawing: shouldStopDrawing!, + ); case 'dt_continuous': return ContinuousDrawingCreator( onAddDrawing: onAddDrawing, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart index 060bf240d..652e26166 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart @@ -126,6 +126,7 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); @@ -278,7 +279,9 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) { final LineStyle lineStyle = config.toJson()['lineStyle']; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index 97f590ffa..5435aeefc 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -64,6 +64,7 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); @@ -136,7 +137,9 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) { setIsStartPointDragged(isDragged: false); @@ -188,6 +191,7 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { final bool isWithinRange = dotProduct > 0 && dotProduct < lineLength; return isWithinRange && distance.abs() <= lineStyle.thickness + 6 || - (draggableStartPoint.isDragged || draggableEndPoint!.isDragged); + (_startPoint!.isClicked(position, markerRadius) || + _endPoint!.isClicked(position, markerRadius)); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart index 25f851c3b..b9d2b7afe 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart @@ -45,6 +45,7 @@ class RayLineDrawing extends Drawing { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final RayDrawingToolConfig config = @@ -83,7 +84,9 @@ class RayLineDrawing extends Drawing { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) => _lineDrawing.hitTest(position, epochToX, quoteToY, config, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart index ea26d15d7..f444486d9 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart @@ -99,6 +99,7 @@ class TrendDrawing extends Drawing { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); @@ -267,7 +268,9 @@ class TrendDrawing extends Drawing { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) { setIsStartPointDragged(isDragged: false); diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index d78c76fe8..0532679cf 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -43,6 +43,7 @@ class VerticalDrawing extends Drawing { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); @@ -83,7 +84,9 @@ class VerticalDrawing extends Drawing { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) { final LineStyle lineStyle = config.toJson()['lineStyle']; From 9dd250009c2f7a7dc3a687570d69846d583f65ff Mon Sep 17 00:00:00 2001 From: ahmadtaimoor-deriv <129935294+ahmadtaimoor-deriv@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:09:38 +0800 Subject: [PATCH 13/29] Ahmad/Add rectangle drawing (#232) - add rectangle drawing tool --- .../drawing_tools_ui/drawing_tool_config.dart | 4 + .../drawing_tools_dialog.dart | 5 + .../rectangle_drawing_tool_config.dart | 53 +++++ .../rectangle_drawing_tool_config.g.dart | 34 +++ .../rectangle_drawing_tool_item.dart | 93 ++++++++ .../drawing_tools/drawing_tool_widget.dart | 13 +- .../rectangle/rectangle_drawing.dart | 213 ++++++++++++++++++ .../rectangle/rectangle_drawing_creator.dart | 105 +++++++++ .../drawing_tool_chart.dart | 1 + 9 files changed, 519 insertions(+), 2 deletions(-) create mode 100644 lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index cd1b77042..1dd6b4640 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -3,9 +3,11 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; + import 'package:flutter/material.dart'; import 'line/line_drawing_tool_config.dart'; import 'vertical/vertical_drawing_tool_config.dart'; @@ -34,6 +36,8 @@ abstract class DrawingToolConfig extends AddOnConfig { return LineDrawingToolConfig.fromJson(json); case RayDrawingToolConfig.name: return RayDrawingToolConfig.fromJson(json); + case RectangleDrawingToolConfig.name: + return RectangleDrawingToolConfig.fromJson(json); case TrendDrawingToolConfig.name: return TrendDrawingToolConfig.fromJson(json); case VerticalDrawingToolConfig.name: diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index a9f05dca1..44b255c83 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -3,6 +3,7 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; @@ -67,6 +68,9 @@ class _DrawingToolsDialogState extends State { child: Text('Ray'), value: RayDrawingToolConfig(), ), + DropdownMenuItem( + child: Text('Rectangle'), + value: RectangleDrawingToolConfig()), DropdownMenuItem( child: Text('Trend'), value: TrendDrawingToolConfig(), @@ -75,6 +79,7 @@ class _DrawingToolsDialogState extends State { child: Text('Vertical'), value: VerticalDrawingToolConfig(), ), + // TODO(maryia-binary): add the rest of drawing tools above ], onChanged: (dynamic config) { setState(() { diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart new file mode 100644 index 000000000..dbc4ecb15 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart @@ -0,0 +1,53 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../callbacks.dart'; + +part 'rectangle_drawing_tool_config.g.dart'; + +/// Rectangle drawing tool configurations. +@JsonSerializable() +class RectangleDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const RectangleDrawingToolConfig({ + this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = DrawingPatterns.solid, + }) : super(); + + /// Initializes from JSON. + factory RectangleDrawingToolConfig.fromJson(Map json) => + _$RectangleDrawingToolConfigFromJson(json); + + /// Unique name for this drawing tool. + static const String name = 'dt_rectangle'; + + @override + Map toJson() => _$RectangleDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool fill style + final LineStyle fillStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + final DrawingPatterns pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + RectangleDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart new file mode 100644 index 000000000..ad1651f15 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'rectangle_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RectangleDrawingToolConfig _$RectangleDrawingToolConfigFromJson( + Map json) => + RectangleDrawingToolConfig( + fillStyle: json['fillStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.blue) + : LineStyle.fromJson(json['fillStyle'] as Map), + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); + +Map _$RectangleDrawingToolConfigToJson( + RectangleDrawingToolConfig instance) => + { + 'fillStyle': instance.fillStyle, + 'lineStyle': instance.lineStyle, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart new file mode 100644 index 000000000..99bc3ca47 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart @@ -0,0 +1,93 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; + +import 'package:flutter/material.dart'; + +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; +import '../drawing_tool_item.dart'; + +/// Rectangle drawing tool item in the list of drawing tool which provide this +/// drawing tools options menu. +class RectangleDrawingToolItem extends DrawingToolItem { + /// Initializes + const RectangleDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + RectangleDrawingToolConfig config = const RectangleDrawingToolConfig(), + }) : super( + key: key, + title: 'Rectangle', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createDrawingToolItemState() => + RectangleDrawingToolItemState(); +} + +/// Rectangle drawing tool Item State class +class RectangleDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _fillStyle; + LineStyle? _lineStyle; + DrawingPatterns? _pattern; + + @override + RectangleDrawingToolConfig createDrawingToolConfig() => + RectangleDrawingToolConfig( + fillStyle: _currentFillStyle, + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField( + ChartLocalization.of(context).labelColor, _currentLineStyle), + _buildColorField( + ChartLocalization.of(context).labelFillColor, _currentFillStyle), + // TODO(maryia-deriv): implement _buildPatternField() to set pattern + ], + ); + Widget _buildColorField(String label, LineStyle style) => Row( + children: [ + Text( + label, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: style.color, + onColorChanged: (Color selectedColor) { + setState( + () { + final LineStyle newColor = + style.copyWith(color: selectedColor); + if (label == ChartLocalization.of(context).labelColor) { + _lineStyle = newColor; + } else { + _fillStyle = newColor; + } + }, + ); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentFillStyle => + _fillStyle ?? (widget.config as RectangleDrawingToolConfig).fillStyle; + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as RectangleDrawingToolConfig).lineStyle; + + DrawingPatterns get _currentPattern => + _pattern ?? (widget.config as RectangleDrawingToolConfig).pattern; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index e578cda25..5212c2f62 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -4,8 +4,10 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart'; + import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; @@ -97,10 +99,12 @@ class DrawingToolWidget extends StatelessWidget { clearDrawingToolSelection: clearDrawingToolSelection, removeDrawing: removeDrawing, ); - case 'dt_vertical': - return VerticalDrawingCreator( + case 'dt_rectangle': + return RectangleDrawingCreator( onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, + clearDrawingToolSelection: clearDrawingToolSelection, + removeDrawing: removeDrawing, ); case 'dt_trend': return TrendDrawingCreator( @@ -110,6 +114,11 @@ class DrawingToolWidget extends StatelessWidget { removeDrawing: removeDrawing, series: series, ); + case 'dt_vertical': + return VerticalDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); // TODO(maryia-binary): add the rest of drawing tools here default: diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart new file mode 100644 index 000000000..56163a6ee --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart @@ -0,0 +1,213 @@ +import 'dart:math'; + +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Rectangle drawing tool. +class RectangleDrawing extends Drawing { + /// Initializes + RectangleDrawing({ + required this.drawingPart, + this.startEdgePoint = const EdgePoint(), + this.endEdgePoint = const EdgePoint(), + }); + + /// Instance of enum including all possible drawing parts(marker,rectangle) + final DrawingParts drawingPart; + + /// Marker radius. + final double _markerRadius = 10; + + /// Keeps the latest position of the start and end point of drawing + Point? _startPoint, _endPoint; + + /// Store the created rectangle in this variable + /// (so it can be used for hitTest as well). + Rect _rect = Rect.zero; + + /// Store the starting X Coordinate + double startXCoord = 0; + + /// Store the starting Y Coordinate + double startYCoord = 0; + + /// Store the ending X Coordinate + double endXCoord = 0; + + /// Store the ending Y Coordinate + double endYCoord = 0; + + /// Starting point of drawing + EdgePoint startEdgePoint; + + /// Ending point of drawing + EdgePoint endEdgePoint; + + /// Function to check if the clicked position (Offset) is on + /// boundary of the rectangle + bool _isClickedOnRectangleBoundary(Rect rect, Offset position) { + /// Width of the rectangle line + const double lineWidth = 3; + const int touchTolerance = 10; + + final List rectangleLinesBoundaries = [ + Rect.fromLTWH( + rect.left - touchTolerance, + rect.top - touchTolerance, + rect.width + touchTolerance * 2, + lineWidth + touchTolerance * 2, + ), + Rect.fromLTWH( + rect.left - touchTolerance, + rect.top - touchTolerance, + lineWidth + touchTolerance * 2, + rect.height + touchTolerance * 2, + ), + Rect.fromLTWH( + rect.right - lineWidth - touchTolerance * 2, + rect.top - touchTolerance, + lineWidth + touchTolerance * 2, + rect.height + touchTolerance * 2, + ), + Rect.fromLTWH( + rect.left - touchTolerance, + rect.bottom - lineWidth - touchTolerance * 2, + rect.width + touchTolerance * 2 + 2, + lineWidth + touchTolerance * 2 + 2, + ), + ]; + + return rectangleLinesBoundaries + .any((Rect lineBound) => lineBound.inflate(2).contains(position)); + } + + /// Paint the rectangle + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final DrawingPaintStyle paint = DrawingPaintStyle(); + final RectangleDrawingToolConfig config = + drawingData.config as RectangleDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final LineStyle fillStyle = config.fillStyle; + + final DrawingPatterns pattern = config.pattern; + + _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + + startXCoord = _startPoint!.x; + startYCoord = _startPoint!.y; + + endXCoord = _endPoint!.x; + endYCoord = _endPoint!.y; + + if (drawingPart == DrawingParts.marker) { + if (endEdgePoint.epoch != 0 && endYCoord != 0) { + /// Draw first marker + canvas.drawCircle( + Offset(endXCoord, endYCoord), + _markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } else if (startEdgePoint.epoch != 0 && startYCoord != 0) { + /// Draw second marker + canvas.drawCircle( + Offset(startXCoord, startYCoord), + _markerRadius, + drawingData.isSelected + ? paint.glowyCirclePaintStyle(lineStyle.color) + : paint.transparentCirclePaintStyle()); + } + } else if (drawingPart == DrawingParts.rectangle) { + if (pattern == DrawingPatterns.solid) { + _rect = Rect.fromPoints( + Offset(startXCoord, startYCoord), Offset(endXCoord, endYCoord)); + + canvas + ..drawRect( + _rect, + drawingData.isSelected + ? paint.glowyLinePaintStyle( + fillStyle.color.withOpacity(0.3), lineStyle.thickness) + : paint.fillPaintStyle( + fillStyle.color.withOpacity(0.3), lineStyle.thickness)) + ..drawRect( + _rect, paint.strokeStyle(lineStyle.color, lineStyle.thickness)); + } + } + } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted lines on the screen, + /// the drawing is selected on clicking on any boundary(line) and markers of + /// the drawing + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, + }) { + setIsStartPointDragged(isDragged: false); + setIsEndPointDragged!(isDragged: false); + + // Calculate the difference between the start marker and the tap point. + final double startDx = position.dx - startXCoord; + final double startDy = position.dy - startYCoord; + + // Calculate the difference between the end marker and the tap point. + final double endDx = position.dx - endXCoord; + final double endDy = position.dy - endYCoord; + + // Getting the distance from end marker + final double endPointDistance = sqrt(endDx * endDx + endDy * endDy); + + // Getting the distance from start marker + final double startPointDistance = + sqrt(startDx * startDx + startDy * startDy); + + /// Check if end point clicked + if (endPointDistance <= _markerRadius) { + setIsEndPointDragged(isDragged: true); + } + + /// Check if start point clicked + if (startPointDistance <= _markerRadius) { + setIsStartPointDragged(isDragged: true); + } + + return draggableStartPoint.isDragged || + draggableEndPoint!.isDragged || + (_isClickedOnRectangleBoundary(_rect, position) && + endEdgePoint.epoch != 0); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart new file mode 100644 index 000000000..6fe453853 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart @@ -0,0 +1,105 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart'; +import 'package:flutter/material.dart'; +import '../data_model/drawing_parts.dart'; + +/// Creates a Rectangle drawing piece by piece collected on every gesture +/// exists in a widget tree starting from selecting a rectangle drawing tool and +/// until drawing is finished +class RectangleDrawingCreator extends DrawingCreator { + /// Initializes the rectangle drawing creator. + const RectangleDrawingCreator({ + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, + required this.clearDrawingToolSelection, + required this.removeDrawing, + Key? key, + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + ); + + /// Callback to clean drawing tool selection. + final VoidCallback clearDrawingToolSelection; + + /// Callback to remove specific drawing from the list of drawings. + final void Function(String drawingId) removeDrawing; + + @override + _RectangleDrawingCreatorState createState() => + _RectangleDrawingCreatorState(); +} + +class _RectangleDrawingCreatorState + extends DrawingCreatorState { + /// If drawing has been started. + bool _isPenDown = false; + + @override + void onTap(TapUpDetails details) { + super.onTap(details); + + final RectangleDrawingCreator _widget = widget as RectangleDrawingCreator; + + if (isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + if (!_isPenDown) { + /// Draw the initial point. + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + _isPenDown = true; + + drawingParts.add( + RectangleDrawing( + drawingPart: DrawingParts.marker, + startEdgePoint: edgePoints.first, + ), + ); + } else if (!isDrawingFinished) { + /// Draw second point and the rectangle. + _isPenDown = false; + isDrawingFinished = true; + + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + final EdgePoint startEdgePoint = edgePoints.first; + final EdgePoint endEdgePoint = edgePoints[1]; + + if (endEdgePoint == startEdgePoint) { + _widget.removeDrawing(drawingId); + _widget.clearDrawingToolSelection(); + return; + } else { + drawingParts.addAll([ + RectangleDrawing( + drawingPart: DrawingParts.marker, + endEdgePoint: endEdgePoint, + ), + RectangleDrawing( + drawingPart: DrawingParts.rectangle, + startEdgePoint: startEdgePoint, + endEdgePoint: endEdgePoint, + ) + ]); + } + } + + widget.onAddDrawing( + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, + ); + }); + } +} diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index 116f2a63b..bbbb1296f 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -34,6 +34,7 @@ class DrawingToolChart extends StatefulWidget { class _DrawingToolChartState extends State { /// Sets drawing as selected and unselects the rest of drawings + /// if any of the drawing is not finished , it selects the unfinished drawing void _setIsDrawingSelected(DrawingData drawing) { drawing.isSelected = !drawing.isSelected; From f1516f346fe669b82725b2228ac4f301187f8ce8 Mon Sep 17 00:00:00 2001 From: ahmadtaimoor-deriv <129935294+ahmadtaimoor-deriv@users.noreply.github.com> Date: Thu, 7 Sep 2023 13:53:51 +0800 Subject: [PATCH 14/29] Ahmad /Add Horizontal Drawing Tool (#239) - add Horizontal Drawing tool --- .../drawing_tools_ui/distance_constants.dart | 7 +- .../drawing_tools_ui/drawing_tool_config.dart | 5 +- .../drawing_tools_dialog.dart | 7 +- .../horizontal_drawing_tool_config.dart | 49 ++++++++ .../horizontal_drawing_tool_config.g.dart | 30 +++++ .../horizontal_drawing_tool_item.dart | 77 ++++++++++++ .../continuous/continuous_line_drawing.dart | 21 +++- .../drawing_tools/drawing_creator.dart | 5 + .../drawing_tools/drawing_painter.dart | 3 + .../drawing_tools/drawing_tool_widget.dart | 12 ++ .../horizontal/horizontal_drawing.dart | 114 ++++++++++++++++++ .../horizontal_drawing_creator.dart | 59 +++++++++ .../drawing_tools/line/line_drawing.dart | 4 +- .../drawing_tools/paint_drawing_label.dart | 90 ++++++++++++++ .../drawing_tools/ray/ray_line_drawing.dart | 52 +++++--- .../vertical/vertical_drawing.dart | 24 +++- .../vertical/vertical_drawing_creator.dart | 5 + .../drawing_tool_chart.dart | 5 +- 18 files changed, 535 insertions(+), 34 deletions(-) create mode 100644 lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart create mode 100644 lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart diff --git a/lib/src/add_ons/drawing_tools_ui/distance_constants.dart b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart index 4d03d8808..9b144b0e4 100644 --- a/lib/src/add_ons/drawing_tools_ui/distance_constants.dart +++ b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart @@ -1,6 +1,7 @@ -/// This class include the distance/value used in the drawing tools . -/// (i.e. Horizontal , line chart) -/// + +/// This involves calculating the distance from the marker to its +/// edges for the purpose of creating lines ( horizontal , line drawing tool) + class DrawingToolDistance { /// horizontal distance static const double horizontalDistance = 999999; diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index 1dd6b4640..77ac2c2da 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -3,6 +3,7 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; @@ -31,7 +32,9 @@ abstract class DrawingToolConfig extends AddOnConfig { case ContinuousDrawingToolConfig.name: return ContinuousDrawingToolConfig.fromJson(json); case FibfanDrawingToolConfig.name: - return FibfanDrawingToolConfig.fromJson(json); + return FibfanDrawingToolConfig.fromJson(json); + case HorizontalDrawingToolConfig.name: + return HorizontalDrawingToolConfig.fromJson(json); case LineDrawingToolConfig.name: return LineDrawingToolConfig.fromJson(json); case RayDrawingToolConfig.name: diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index 44b255c83..ed22de64e 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; @@ -60,6 +61,10 @@ class _DrawingToolsDialogState extends State { child: Text('Fib Fan'), value: FibfanDrawingToolConfig(), ), + DropdownMenuItem( + child: Text('Horizontal'), + value: HorizontalDrawingToolConfig(), + ), DropdownMenuItem( child: Text('Line'), value: LineDrawingToolConfig(), @@ -78,7 +83,7 @@ class _DrawingToolsDialogState extends State { DropdownMenuItem( child: Text('Vertical'), value: VerticalDrawingToolConfig(), - ), + ) // TODO(maryia-binary): add the rest of drawing tools above ], onChanged: (dynamic config) { diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart new file mode 100644 index 000000000..d93bab3d0 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart @@ -0,0 +1,49 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../callbacks.dart'; + +part 'horizontal_drawing_tool_config.g.dart'; + +/// Horizontal drawing tool configurations. +@JsonSerializable() +class HorizontalDrawingToolConfig extends DrawingToolConfig { + /// Initializes + const HorizontalDrawingToolConfig({ + this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), + this.pattern = DrawingPatterns.solid, + }) : super(); + + /// Initializes from JSON. + factory HorizontalDrawingToolConfig.fromJson(Map json) => + _$HorizontalDrawingToolConfigFromJson(json); + + /// Unique name for this drawing tool. + static const String name = 'dt_horizontal'; + + @override + Map toJson() => _$HorizontalDrawingToolConfigToJson(this) + ..putIfAbsent(DrawingToolConfig.nameKey, () => name); + + /// Drawing tool line style + final LineStyle lineStyle; + + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' + final DrawingPatterns pattern; + + @override + DrawingToolItem getItem( + UpdateDrawingTool updateDrawingTool, + VoidCallback deleteDrawingTool, + ) => + HorizontalDrawingToolItem( + config: this, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); +} diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart new file mode 100644 index 000000000..32a704f16 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'horizontal_drawing_tool_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HorizontalDrawingToolConfig _$HorizontalDrawingToolConfigFromJson( + Map json) => + HorizontalDrawingToolConfig( + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? + DrawingPatterns.solid, + ); + +Map _$HorizontalDrawingToolConfigToJson( + HorizontalDrawingToolConfig instance) => + { + 'lineStyle': instance.lineStyle, + 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + }; + +const _$DrawingPatternsEnumMap = { + DrawingPatterns.solid: 'solid', + DrawingPatterns.dotted: 'dotted', + DrawingPatterns.dashed: 'dashed', +}; diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart new file mode 100644 index 000000000..5cbb7ee55 --- /dev/null +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart @@ -0,0 +1,77 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; + +import 'package:flutter/material.dart'; + +import '../callbacks.dart'; +import '../drawing_tool_config.dart'; +import '../drawing_tool_item.dart'; + +/// Horizontal drawing tool item in the list of drawing tool which provide this +/// drawing tools options menu. +class HorizontalDrawingToolItem extends DrawingToolItem { + /// Initializes + const HorizontalDrawingToolItem({ + required UpdateDrawingTool updateDrawingTool, + required VoidCallback deleteDrawingTool, + Key? key, + HorizontalDrawingToolConfig config = const HorizontalDrawingToolConfig(), + }) : super( + key: key, + title: 'Horizontal', + config: config, + updateDrawingTool: updateDrawingTool, + deleteDrawingTool: deleteDrawingTool, + ); + + @override + DrawingToolItemState createDrawingToolItemState() => + HorizontalDrawingToolItemState(); +} + +/// Vertival drawing tool Item State class +class HorizontalDrawingToolItemState + extends DrawingToolItemState { + LineStyle? _lineStyle; + DrawingPatterns? _pattern; + + @override + HorizontalDrawingToolConfig createDrawingToolConfig() => + HorizontalDrawingToolConfig( + lineStyle: _currentLineStyle, + pattern: _currentPattern, + ); + + @override + Widget getDrawingToolOptions() => Column( + children: [ + _buildColorField(), + ], + ); + + Widget _buildColorField() => Row( + children: [ + Text( + ChartLocalization.of(context).labelColor, + style: const TextStyle(fontSize: 16), + ), + ColorSelector( + currentColor: _currentLineStyle.color, + onColorChanged: (Color selectedColor) { + setState(() { + _lineStyle = _currentLineStyle.copyWith(color: selectedColor); + }); + updateDrawingTool(); + }, + ) + ], + ); + + LineStyle get _currentLineStyle => + _lineStyle ?? (widget.config as HorizontalDrawingToolConfig).lineStyle; + + DrawingPatterns get _currentPattern => + _pattern ?? (widget.config as HorizontalDrawingToolConfig).pattern; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart index 5716c6e56..d2b65f064 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -88,9 +88,20 @@ class ContinuousLineDrawing extends Drawing { DraggableEdgePoint? draggableEndPoint, void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, - }) => - _lineDrawing.hitTest(position, epochToX, quoteToY, config, - draggableStartPoint, setIsStartPointDragged, - draggableEndPoint: draggableEndPoint, - setIsEndPointDragged: setIsEndPointDragged); + }) { + config as ContinuousDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; + + return _lineDrawing.hitTest( + position, + epochToX, + quoteToY, + LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), + draggableStartPoint, + setIsStartPointDragged, + draggableEndPoint: draggableEndPoint, + setIsEndPointDragged: setIsEndPointDragged); + } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart index 69504f76d..cf0833cba 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart @@ -4,6 +4,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -24,9 +25,13 @@ abstract class DrawingCreator extends StatefulWidget { const DrawingCreator({ required this.onAddDrawing, required this.quoteFromCanvasY, + this.chartConfig, Key? key, }) : super(key: key); + /// Chart config to get pipSize + final ChartConfig? chartConfig; + /// A required callback property of type [OnAddDrawing] that is used to /// pass the newly created drawing to the parent. final OnAddDrawing onAddDrawing; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index 9c057a8d0..558bd3817 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -163,6 +163,7 @@ class _DrawingPainterState extends State { theme: context.watch(), epochToX: xAxis.xFromEpoch, quoteToY: widget.quoteToCanvasY, + quoteFromY: widget.quoteFromCanvasY, draggableStartPoint: _draggableStartPoint, draggableMiddlePoint: _draggableMiddlePoint, isDrawingToolSelected: widget.selectedDrawingTool != null, @@ -203,6 +204,7 @@ class _DrawingPainter extends CustomPainter { required this.theme, required this.epochToX, required this.quoteToY, + required this.quoteFromY, required this.draggableStartPoint, required this.setIsStartPointDragged, required this.updatePositionCallback, @@ -229,6 +231,7 @@ class _DrawingPainter extends CustomPainter { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback; + double Function(double) quoteFromY; @override void paint(Canvas canvas, Size size) { for (final Drawing drawingPart in drawingData.drawingParts) { diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index 5212c2f62..b28232164 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart'; @@ -9,6 +10,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; @@ -23,11 +25,15 @@ class DrawingToolWidget extends StatelessWidget { required this.quoteFromCanvasY, required this.clearDrawingToolSelection, required this.removeDrawing, + required this.chartConfig, required this.series, this.shouldStopDrawing, Key? key, }) : super(key: key); + /// ChartConfig for getting pipsize + final ChartConfig chartConfig; + /// Selected drawing tool. final DrawingToolConfig selectedDrawingTool; @@ -85,6 +91,12 @@ class DrawingToolWidget extends StatelessWidget { clearDrawingToolSelection: clearDrawingToolSelection, removeDrawing: removeDrawing, ); + case 'dt_horizontal': + return HorizontalDrawingCreator( + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + chartConfig: chartConfig, + ); case 'dt_line': return LineDrawingCreator( onAddDrawing: onAddDrawing, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart new file mode 100644 index 000000000..7c97ccc75 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart @@ -0,0 +1,114 @@ +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/distance_constants.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_chart/deriv_chart.dart'; + +/// Horizontal drawing tool. +/// A tool used to draw straight infinite horizontal line on the chart +class HorizontalDrawing extends Drawing { + /// Initializes + HorizontalDrawing({ + required this.drawingPart, + required this.quoteFromCanvasY, + required this.chartConfig, + required this.edgePoint, + }); + + /// Chart config to get pipSize + final ChartConfig? chartConfig; + + /// Part of a drawing: 'horizontal' + final DrawingParts drawingPart; + + /// Starting point of drawing + final EdgePoint edgePoint; + + /// Keeps the latest position of the horizontal line + Point? startPoint; + + /// Conversion function for converting quote from chart's canvas' Y position. + final double Function(double)? quoteFromCanvasY; + + /// Paint + @override + void onPaint( + Canvas canvas, + Size size, + ChartTheme theme, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingData drawingData, + Point Function( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) updatePositionCallback, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + final DrawingPaintStyle paint = DrawingPaintStyle(); + final HorizontalDrawingToolConfig config = + drawingData.config as HorizontalDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; + + startPoint = updatePositionCallback(edgePoint, draggableStartPoint); + + final double pointYCoord = startPoint!.y; + final double pointXCoord = startPoint!.x; + + final double startX = pointXCoord - DrawingToolDistance.horizontalDistance, + endingX = pointXCoord + DrawingToolDistance.horizontalDistance; + + if (pattern == DrawingPatterns.solid) { + canvas.drawLine( + Offset(startX, pointYCoord), + Offset(endingX, pointYCoord), + drawingData.isSelected + ? paint.glowyLinePaintStyle(lineStyle.color, lineStyle.thickness) + : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), + ); + paintDrawingLabel( + canvas, + size, + pointYCoord, + 'horizontal', + theme, + chartConfig!, + quoteFromY: quoteFromCanvasY, + ); + } + } + + /// Calculation for detemining whether a user's touch or click intersects + /// with any of the painted areas on the screen + @override + bool hitTest( + Offset position, + double Function(int x) epochToX, + double Function(double y) quoteToY, + DrawingToolConfig config, + DraggableEdgePoint draggableStartPoint, + void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsEndPointDragged, + }) { + config as HorizontalDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + + return position.dy > startPoint!.y - lineStyle.thickness - 5 && + position.dy < startPoint!.y + lineStyle.thickness + 5; + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart new file mode 100644 index 000000000..e690aa606 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart @@ -0,0 +1,59 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:flutter/material.dart'; +import './horizontal_drawing.dart'; + +/// Creates a Horizontal line drawing +class HorizontalDrawingCreator extends DrawingCreator { + /// Initializes the horizontal drawing creator. + const HorizontalDrawingCreator({ + required OnAddDrawing onAddDrawing, + required double Function(double) quoteFromCanvasY, + required ChartConfig chartConfig, + Key? key, + }) : super( + key: key, + onAddDrawing: onAddDrawing, + quoteFromCanvasY: quoteFromCanvasY, + chartConfig: chartConfig, + ); + + @override + DrawingCreatorState createState() => + _HorizontalDrawingCreatorState(); +} + +class _HorizontalDrawingCreatorState + extends DrawingCreatorState { + @override + void onTap(TapUpDetails details) { + super.onTap(details); + if (isDrawingFinished) { + return; + } + setState(() { + position = details.localPosition; + + edgePoints.add(EdgePoint( + epoch: epochFromX!(position!.dx), + quote: widget.quoteFromCanvasY(position!.dy), + )); + + isDrawingFinished = true; + + drawingParts.add(HorizontalDrawing( + drawingPart: DrawingParts.line, + edgePoint: edgePoints.first, + chartConfig: widget.chartConfig, + quoteFromCanvasY: widget.quoteFromCanvasY)); + + widget.onAddDrawing( + drawingId, + drawingParts, + isDrawingFinished: isDrawingFinished, + ); + }); + } +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index 5435aeefc..4424155ce 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -145,7 +145,9 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { setIsStartPointDragged(isDragged: false); setIsEndPointDragged!(isDragged: false); - final LineStyle lineStyle = config.toJson()['lineStyle']; + config as LineDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; double startXCoord = _startPoint!.x; double startQuoteToY = _startPoint!.y; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart new file mode 100644 index 000000000..9e5d88097 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart @@ -0,0 +1,90 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'dart:ui' as ui; + +/// Function is responsible for creating the label for vertical and +/// horizontal drawing +void paintDrawingLabel( + Canvas canvas, + Size size, + double coord, + String drawingType, + ChartTheme theme, + ChartConfig config, { + int Function(double x)? epochFromX, + double Function(double)? quoteFromY, +}) { + /// Outline Rectangle of the label + Rect _labelRect = Rect.zero; + + /// Offset from where the text starts + Offset _textOffset = Offset.zero; + + /// Name of the of label + String _labelString = ''; + + /// Width of the rectangle + const double _width = 46; + + /// Height of the rectangle + const double _height = 24; + + final HorizontalBarrierStyle horizontalBarrierStyle = + theme.horizontalBarrierStyle; + + if (drawingType == 'horizontal') { + _labelRect = Rect.fromCenter( + center: Offset(size.width - 26, coord), + width: _width, + height: _height, + ); + + _labelString = quoteFromY!(coord).toStringAsFixed(config.pipSize); + + _textOffset = Offset( + size.width - 44, + coord - 5, + ); + } else { + _labelRect = Rect.fromCenter( + center: Offset( + coord, + size.height - 6, + ), + width: _width, + height: _height, + ); + + final DateTime _dateTime = + DateTime.fromMillisecondsSinceEpoch(epochFromX!(coord), isUtc: true); + + _labelString = DateFormat('HH:mm:ss').format(_dateTime); + + _textOffset = Offset( + coord - 19, + size.height - 13, + ); + } + + final TextPainter textPainter = TextPainter( + text: TextSpan( + text: _labelString, + style: TextStyle( + color: horizontalBarrierStyle.titleBackgroundColor, + fontSize: 10, + ), + ), + textDirection: ui.TextDirection.ltr, + )..layout(maxWidth: size.width); + + final RRect roundedRect = + RRect.fromRectAndRadius(_labelRect, const Radius.circular(4)); + + canvas.drawRRect( + roundedRect, + Paint()..color = horizontalBarrierStyle.color, + ); + textPainter.paint(canvas, _textOffset); +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart index b9d2b7afe..24175489a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart @@ -55,21 +55,22 @@ class RayLineDrawing extends Drawing { final DrawingPatterns pattern = config.pattern; _lineDrawing.onPaint( - canvas, - size, - theme, - epochToX, - quoteToY, - DrawingData( - id: drawingData.id, - config: LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), - drawingParts: drawingData.drawingParts, - isDrawingFinished: drawingData.isDrawingFinished, - isSelected: drawingData.isSelected, - ), - updatePositionCallback, - draggableStartPoint, - draggableEndPoint: draggableEndPoint); + canvas, + size, + theme, + epochToX, + quoteToY, + DrawingData( + id: drawingData.id, + config: LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), + drawingParts: drawingData.drawingParts, + isDrawingFinished: drawingData.isDrawingFinished, + isSelected: drawingData.isSelected, + ), + updatePositionCallback, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + ); } /// Calculation for detemining whether a user's touch or click intersects @@ -88,9 +89,20 @@ class RayLineDrawing extends Drawing { DraggableEdgePoint? draggableEndPoint, void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, - }) => - _lineDrawing.hitTest(position, epochToX, quoteToY, config, - draggableStartPoint, setIsStartPointDragged, - draggableEndPoint: draggableEndPoint, - setIsEndPointDragged: setIsEndPointDragged); + }) { + config as RayDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + final DrawingPatterns pattern = config.pattern; + + return _lineDrawing.hitTest( + position, + epochToX, + quoteToY, + LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), + draggableStartPoint, + setIsStartPointDragged, + draggableEndPoint: draggableEndPoint, + setIsEndPointDragged: setIsEndPointDragged); + } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index 0532679cf..91361411e 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -8,6 +8,8 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; @@ -17,9 +19,17 @@ class VerticalDrawing extends Drawing { /// Initializes VerticalDrawing({ required this.drawingPart, + required this.chartConfig, required this.edgePoint, + required this.epochFromX, }); + /// Chart config to get pipSize + final ChartConfig? chartConfig; + + /// Get epoch from x. + int Function(double x)? epochFromX; + /// Part of a drawing: 'vertical' final DrawingParts drawingPart; @@ -57,7 +67,6 @@ class VerticalDrawing extends Drawing { final double xCoord = startPoint!.x; final double startQuoteToY = startPoint!.y; - if (drawingPart == DrawingParts.line) { final double startY = startQuoteToY - 10000, endingY = startQuoteToY + 10000; @@ -70,6 +79,15 @@ class VerticalDrawing extends Drawing { ? paint.glowyLinePaintStyle(lineStyle.color, lineStyle.thickness) : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), ); + paintDrawingLabel( + canvas, + size, + xCoord, + 'vertical', + theme, + chartConfig!, + epochFromX: epochFromX, + ); } } } @@ -89,7 +107,9 @@ class VerticalDrawing extends Drawing { void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) { - final LineStyle lineStyle = config.toJson()['lineStyle']; + config as VerticalDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; return position.dx > startPoint!.x - lineStyle.thickness - 5 && position.dx < startPoint!.x + lineStyle.thickness + 5; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart index 1ba24040f..676f0171e 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import './vertical_drawing.dart'; @@ -10,11 +11,13 @@ class VerticalDrawingCreator extends DrawingCreator { const VerticalDrawingCreator({ required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, + required ChartConfig chartConfig, Key? key, }) : super( key: key, onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, + chartConfig: chartConfig, ); @override @@ -43,6 +46,8 @@ class _VerticalDrawingCreatorState drawingParts.add(VerticalDrawing( drawingPart: DrawingParts.line, edgePoint: edgePoints.first, + chartConfig: widget.chartConfig, + epochFromX: epochFromX, )); widget.onAddDrawing( diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index bbbb1296f..a069d0fcc 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -2,8 +2,10 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; -import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; /// A wigdet for encapsulating drawing tools related business logic class DrawingToolChart extends StatefulWidget { @@ -79,6 +81,7 @@ class _DrawingToolChartState extends State { onAddDrawing: widget.drawingTools.onAddDrawing, selectedDrawingTool: widget.drawingTools.selectedDrawingTool!, quoteFromCanvasY: widget.chartQuoteFromCanvasY, + chartConfig: context.watch(), clearDrawingToolSelection: widget.drawingTools.clearDrawingToolSelection, series: widget.series, From 14d0abe94dde92fa459b328debcb00faf28f2974 Mon Sep 17 00:00:00 2001 From: ahmadtaimoor-deriv <129935294+ahmadtaimoor-deriv@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:46:14 +0800 Subject: [PATCH 15/29] Drawing tool merge fix (#249) * Ahmad/78749/Drawing tool- Horizontal * Ahmad/78749/Drawing tool- Horizontal * adding constant file * yCood, _startingYPoint to quote,_startingQuote * Adding label * drawing Label class * PR fixes * testing branch * removing comment * PR Fixes * Adding descriptive comment * merge commit fix * sequence of drawing tools in dialog * label design fix * merge fixes reopened --------- Co-authored-by: Ahmad Taimoor --- .../data_visualization/drawing_tools/drawing_tool_widget.dart | 1 + .../drawing_tools/horizontal/horizontal_drawing.dart | 3 +++ .../drawing_tools/rectangle/rectangle_drawing.dart | 3 +++ 3 files changed, 7 insertions(+) diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index b28232164..e079abbdb 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -130,6 +130,7 @@ class DrawingToolWidget extends StatelessWidget { return VerticalDrawingCreator( onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, + chartConfig: chartConfig, ); // TODO(maryia-binary): add the rest of drawing tools here diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart index 7c97ccc75..393d652bc 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart @@ -54,6 +54,7 @@ class HorizontalDrawing extends Drawing { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); @@ -101,7 +102,9 @@ class HorizontalDrawing extends Drawing { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) { config as HorizontalDrawingToolConfig; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart index 56163a6ee..044cec118 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart @@ -105,6 +105,7 @@ class RectangleDrawing extends Drawing { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); @@ -174,7 +175,9 @@ class RectangleDrawing extends Drawing { DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, void Function({required bool isDragged}) setIsStartPointDragged, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, + void Function({required bool isDragged})? setIsMiddlePointDragged, void Function({required bool isDragged})? setIsEndPointDragged, }) { setIsStartPointDragged(isDragged: false); From 091d00a58ec7de608f10478354ccb515d387a93e Mon Sep 17 00:00:00 2001 From: ramin-deriv <55975218+ramin-deriv@users.noreply.github.com> Date: Mon, 11 Sep 2023 10:41:26 +0800 Subject: [PATCH 16/29] ramin/drawing_tools_performance_improvement (#246) - drawing tools performance improvement --- .../channel/channel_drawing.dart | 10 + .../continuous/continuous_line_drawing.dart | 14 ++ .../data_model/draggable_edge_point.dart | 6 + .../drawing_tools/data_model/extensions.dart | 26 +++ .../drawing_tools/drawing.dart | 32 +++ .../drawing_tools/drawing_data.dart | 25 +++ .../drawing_tools/drawing_painter.dart | 183 +++++++++++------- .../drawing_tools/fibfan/fibfan_drawing.dart | 10 + .../horizontal/horizontal_drawing.dart | 10 + .../drawing_tools/line/line_drawing.dart | 10 + .../drawing_tools/ray/ray_line_drawing.dart | 14 ++ .../rectangle/rectangle_drawing.dart | 10 + .../drawing_tools/trend/trend_drawing.dart | 40 +++- .../trend/trend_drawing_creator.dart | 28 ++- .../vertical/vertical_drawing.dart | 13 ++ lib/src/deriv_chart/chart/main_chart.dart | 20 +- .../drawing_tool_chart.dart | 20 +- 17 files changed, 375 insertions(+), 96 deletions(-) create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart index 58de3c3ea..e2145f31e 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart @@ -286,4 +286,14 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { _middlePoint!.isClicked(position, markerRadius) || endPoint.isClicked(position, markerRadius)); } + + // TODO(NA): return true if the channel drawing is in epoch range. + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + true; } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart index d2b65f064..f06d99615 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -31,6 +31,20 @@ class ContinuousLineDrawing extends Drawing { final LineDrawing _lineDrawing; + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + _lineDrawing.needsRepaint( + leftEpoch, + rightEpoch, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + ); + /// Paint the line @override void onPaint( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart index d363ce869..8c9cd142b 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart @@ -27,6 +27,12 @@ class DraggableEdgePoint extends EdgePoint { /// Holds the current position of the edge point when it is being dragged. Offset _draggedPosition = Offset.zero; + /// Updated position of the edge point when it is being dragged. + /// + /// This would be obsolete when we keep [epoch] and [quote] fields updated + /// with user's dragging. And we can use them instead of this field. + Offset get draggedPosition => _draggedPosition; + /// A callback method that takes the relative x and y positions as parameter, /// sets the draggedPosition field to its value and return epoch and quote /// values. diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart new file mode 100644 index 000000000..e5469400b --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart @@ -0,0 +1,26 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; + +// TODO(NA): consider EdgePoint radius as well in this calculation. +/// The distance from the edge point to the edge of the screen to be considered +/// as off screen for a [DraggableEdgePoint]. +/// +/// This is to make sure the [DraggableEdgePoint] is fully out of the view port. +/// +/// Since the position of a [DraggableEdgePoint] is its center point, we should +/// also consider its radius as well if we want to improve this value and make +/// it accurate. +/// +/// When we know how visually can represent the edge point (circle, square, etc) +/// we can improve this value. +const double _edgePointOffScreenSafeDistance = 1000; + +/// An extension on DraggableEdgePoint class that adds some helper methods. +extension DraggableEdgePointExtension on DraggableEdgePoint { + /// Checks if the edge point is on the view port range. + /// + /// The view port range is defined by the left and right epoch values. + /// returns true if the edge point is on the view port range. + bool isInViewPortRange(int leftEpoch, int rightEpoch) => + draggedPosition.dx >= (leftEpoch - _edgePointOffScreenSafeDistance) && + draggedPosition.dx <= (rightEpoch + _edgePointOffScreenSafeDistance); +} diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index 3c5dbe59d..463d774ce 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -8,6 +8,38 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dar /// Base class to draw a particular drawing abstract class Drawing { + /// Will be called when the drawing is moved by the user gesture. + /// + /// Some drawing tools might required to handle some logic after the drawing + /// is moved and we don't want this logic be done in the [onPaint] method + /// because it runs more often and it might cause performance issues. + /// + /// The method has an empty implementation so only the [Drawing] subclasses + /// that require this life-cycle method can override it. + void onDrawingMoved( + List ticks, + EdgePoint startPoint, { + EdgePoint? endPoint, + }) {} + + /// Is called before repaint the drawing to check if it needs to be + /// repainted. + /// + /// Returns true if the drawing needs to be repainted. + /// + /// Since the [Drawing] class instances are mutable and live across the + /// painting frames, there is no previous instance of it provided in this + /// method to compare with and decide for repainting. + /// + /// Repainting condition for drawing usually is based on whether they are + /// in the chart visible area or not. + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }); + /// Paint void onPaint( Canvas canvas, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart index c66f99a03..ccdf8c869 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; /// A class that hold drawing data. @@ -43,4 +44,28 @@ class DrawingData { /// Updates drawing list. DrawingData updateDrawingPartList(List drawingParts) => DrawingData(id: id, config: config, drawingParts: drawingParts); + + /// Determines if this [DrawingData] needs to be repainted. + /// + /// Returns `true` if any of the [drawingParts] needs to be repainted. + bool shouldRepaint( + DrawingData oldDrawingData, + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + for (final Drawing drawing in drawingParts) { + if (drawing.needsRepaint( + leftEpoch, + rightEpoch, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + )) { + return true; + } + } + + return false; + } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index 558bd3817..bbe3c4960 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -110,41 +110,27 @@ class _DrawingPainterState extends State { } return widget.drawingData != null - ? GestureDetector( - onTapUp: (TapUpDetails details) { - widget.setIsDrawingSelected(widget.drawingData!); - }, - onLongPressDown: (LongPressDownDetails details) { - widget.onMoveDrawing(isDrawingMoved: true); - _previousPosition = details.localPosition; - }, - onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { - final DragUpdateDetails dragDetails = - convertLongPressToDrag(details, _previousPosition); - _previousPosition = details.localPosition; + ? RepaintBoundary( + child: GestureDetector( + onTapUp: (TapUpDetails details) { + widget.setIsDrawingSelected(widget.drawingData!); + _updateDrawingsMovement(); + }, + onLongPressDown: (LongPressDownDetails details) { + widget.onMoveDrawing(isDrawingMoved: true); + _previousPosition = details.localPosition; + _updateDrawingsMovement(); + }, + onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { + final DragUpdateDetails dragDetails = + convertLongPressToDrag(details, _previousPosition); + _previousPosition = details.localPosition; - _onPanUpdate(dragDetails); - }, - onLongPressUp: () { - widget.onMoveDrawing(isDrawingMoved: false); - _draggableStartPoint = _draggableStartPoint.copyWith( - isDragged: false, - ); - _draggableMiddlePoint = _draggableMiddlePoint.copyWith( - isDragged: false, - ); - _draggableEndPoint = _draggableEndPoint.copyWith( - isDragged: false, - ); - }, - onPanStart: (DragStartDetails details) { - widget.onMoveDrawing(isDrawingMoved: true); - }, - onPanUpdate: (DragUpdateDetails details) { - _onPanUpdate(details); - }, - onPanEnd: (DragEndDetails details) { - setState(() { + _onPanUpdate(dragDetails); + _updateDrawingsMovement(); + }, + onLongPressUp: () { + widget.onMoveDrawing(isDrawingMoved: false); _draggableStartPoint = _draggableStartPoint.copyWith( isDragged: false, ); @@ -154,48 +140,88 @@ class _DrawingPainterState extends State { _draggableEndPoint = _draggableEndPoint.copyWith( isDragged: false, ); - }); - widget.onMoveDrawing(isDrawingMoved: false); - }, - child: CustomPaint( - foregroundPainter: _DrawingPainter( - drawingData: widget.drawingData!, - theme: context.watch(), - epochToX: xAxis.xFromEpoch, - quoteToY: widget.quoteToCanvasY, - quoteFromY: widget.quoteFromCanvasY, - draggableStartPoint: _draggableStartPoint, - draggableMiddlePoint: _draggableMiddlePoint, - isDrawingToolSelected: widget.selectedDrawingTool != null, - draggableEndPoint: _draggableEndPoint, - updatePositionCallback: ( - EdgePoint edgePoint, - DraggableEdgePoint draggableEdgePoint, - ) => - draggableEdgePoint.updatePosition( - edgePoint.epoch, - edgePoint.quote, - xAxis.xFromEpoch, - widget.quoteToCanvasY, + _updateDrawingsMovement(); + }, + onPanStart: (DragStartDetails details) { + widget.onMoveDrawing(isDrawingMoved: true); + _updateDrawingsMovement(); + }, + onPanUpdate: (DragUpdateDetails details) { + _onPanUpdate(details); + _updateDrawingsMovement(); + }, + onPanEnd: (DragEndDetails details) { + setState(() { + _draggableStartPoint = _draggableStartPoint.copyWith( + isDragged: false, + ); + _draggableMiddlePoint = _draggableMiddlePoint.copyWith( + isDragged: false, + ); + _draggableEndPoint = _draggableEndPoint.copyWith( + isDragged: false, + ); + }); + widget.onMoveDrawing(isDrawingMoved: false); + _updateDrawingsMovement(); + }, + child: CustomPaint( + foregroundPainter: _DrawingPainter( + drawingData: widget.drawingData!, + theme: context.watch(), + epochToX: xAxis.xFromEpoch, + quoteToY: widget.quoteToCanvasY, + quoteFromY: widget.quoteFromCanvasY, + draggableStartPoint: _draggableStartPoint, + draggableMiddlePoint: _draggableMiddlePoint, + isDrawingToolSelected: widget.selectedDrawingTool != null, + draggableEndPoint: _draggableEndPoint, + leftEpoch: xAxis.leftBoundEpoch, + rightEpoch: xAxis.rightBoundEpoch, + updatePositionCallback: ( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) => + draggableEdgePoint.updatePosition( + edgePoint.epoch, + edgePoint.quote, + xAxis.xFromEpoch, + widget.quoteToCanvasY, + ), + setIsStartPointDragged: ({required bool isDragged}) { + _draggableStartPoint = + _draggableStartPoint.copyWith(isDragged: isDragged); + }, + setIsMiddlePointDragged: ({required bool isDragged}) { + _draggableMiddlePoint = + _draggableMiddlePoint.copyWith(isDragged: isDragged); + }, + setIsEndPointDragged: ({required bool isDragged}) { + _draggableEndPoint = + _draggableEndPoint.copyWith(isDragged: isDragged); + }, ), - setIsStartPointDragged: ({required bool isDragged}) { - _draggableStartPoint = - _draggableStartPoint.copyWith(isDragged: isDragged); - }, - setIsMiddlePointDragged: ({required bool isDragged}) { - _draggableMiddlePoint = - _draggableMiddlePoint.copyWith(isDragged: isDragged); - }, - setIsEndPointDragged: ({required bool isDragged}) { - _draggableEndPoint = - _draggableEndPoint.copyWith(isDragged: isDragged); - }, ), - size: const Size(double.infinity, double.infinity), ), ) : const SizedBox(); } + + void _updateDrawingsMovement() { + if (widget.drawingData == null) { + return; + } + + for (final Drawing drawing in widget.drawingData!.drawingParts) { + drawing.onDrawingMoved( + widget.drawingData!.series!, + _draggableStartPoint, + endPoint: _draggableEndPoint, + ); + } + + setState(() {}); + } } class _DrawingPainter extends CustomPainter { @@ -208,6 +234,8 @@ class _DrawingPainter extends CustomPainter { required this.draggableStartPoint, required this.setIsStartPointDragged, required this.updatePositionCallback, + required this.leftEpoch, + required this.rightEpoch, this.isDrawingToolSelected = false, this.draggableMiddlePoint, this.draggableEndPoint, @@ -231,7 +259,14 @@ class _DrawingPainter extends CustomPainter { DraggableEdgePoint draggableEdgePoint, ) updatePositionCallback; + /// Current left epoch of the chart. + final int leftEpoch; + + /// Current right epoch of the chart. + final int rightEpoch; + double Function(double) quoteFromY; + @override void paint(Canvas canvas, Size size) { for (final Drawing drawingPart in drawingData.drawingParts) { @@ -251,7 +286,13 @@ class _DrawingPainter extends CustomPainter { } @override - bool shouldRepaint(_DrawingPainter oldDelegate) => true; + bool shouldRepaint(_DrawingPainter oldDelegate) => drawingData.shouldRepaint( + oldDelegate.drawingData, + leftEpoch, + rightEpoch, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + ); @override bool shouldRebuildSemantics(_DrawingPainter oldDelegate) => false; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart index 652e26166..97bcb357c 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart @@ -112,6 +112,16 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { )); } + // TODO(NA): Return true if FibfanDrawing's on chart view port. + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + true; + /// Paint the line @override void onPaint( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart index 393d652bc..504efdc85 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart @@ -40,6 +40,16 @@ class HorizontalDrawing extends Drawing { /// Conversion function for converting quote from chart's canvas' Y position. final double Function(double)? quoteFromCanvasY; + // TODO(NA): Return true when the horizontal drawing is in the epoch range. + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + true; + /// Paint @override void onPaint( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index 4424155ce..96a2e1acf 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -50,6 +50,16 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { /// Keeps the latest position of the start and end point of drawing Point? _startPoint, _endPoint; + // TODO(NA): Return true when the line drawing is in epoch range. + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + true; + /// Paint the line @override void onPaint( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart index 24175489a..36db25e92 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart @@ -31,6 +31,20 @@ class RayLineDrawing extends Drawing { final LineDrawing _lineDrawing; + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + _lineDrawing.needsRepaint( + leftEpoch, + rightEpoch, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + ); + /// Paint the line @override void onPaint( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart index 044cec118..95a2b6e12 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart @@ -213,4 +213,14 @@ class RectangleDrawing extends Drawing { (_isClickedOnRectangleBoundary(_rect, position) && endEdgePoint.epoch != 0); } + + // TODO(NA): return when the rectangle drawing is inside the epoch range. + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + true; } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart index f444486d9..719b322ea 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -85,6 +86,40 @@ class TrendDrawing extends Drawing { /// side is dragged to the right of the right side bool _isRectangleSwapped = false; + @override + void onDrawingMoved( + List ticks, + EdgePoint startPoint, { + EdgePoint? endPoint, + }) { + final int minimumEpoch = + startXCoord == 0 ? startEdgePoint.epoch : epochFromX!(startXCoord); + + // Minimum epoch of the drawing + final int maximumEpoch = + endXCoord == 0 ? endEdgePoint.epoch : epochFromX!(endXCoord); + + if (maximumEpoch != 0 && minimumEpoch != 0) { + _calculator = setCalculator(minimumEpoch, maximumEpoch, ticks); + } + } + + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) { + if (draggableStartPoint.isInViewPortRange(leftEpoch, rightEpoch) || + (draggableEndPoint == null || + draggableEndPoint.isInViewPortRange(leftEpoch, rightEpoch))) { + return true; + } + + return false; + } + /// Paint the trend drawing tools @override void onPaint( @@ -103,7 +138,7 @@ class TrendDrawing extends Drawing { DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); - final List? series = drawingData.series; + // Maximum epoch of the drawing final int minimumEpoch = startXCoord == 0 ? startEdgePoint.epoch : epochFromX!(startXCoord); @@ -113,9 +148,6 @@ class TrendDrawing extends Drawing { endXCoord == 0 ? endEdgePoint.epoch : epochFromX!(endXCoord); if (maximumEpoch != 0 && minimumEpoch != 0) { - // setting calculator - _calculator = setCalculator(minimumEpoch, maximumEpoch, series); - // center of rectangle _rectCenter = quoteToY(_calculator!.min) + ((quoteToY(_calculator!.max) - quoteToY(_calculator!.min)) / 2); diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart index 597cd2240..2d038c6ff 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart @@ -93,7 +93,10 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { /// Setting the minmax calculator between the range of /// start and end epoch MinMaxCalculator? _setCalculator( - int minimumEpoch, int maximumEpoch, List? series) { + int minimumEpoch, + int maximumEpoch, + List? series, + ) { if (prevMaximumEpoch != maximumEpoch || prevMinimumEpoch != minimumEpoch) { prevMaximumEpoch = maximumEpoch; prevMinimumEpoch = minimumEpoch; @@ -196,7 +199,10 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { setCalculator: _setCalculator, isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, touchTolerance: _touchTolerance, - ), + )..onDrawingMoved( + _widget.series.input, + edgePoints.first, + ), ); } else if (!isDrawingFinished) { edgePoints.add( @@ -233,7 +239,11 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { setCalculator: _setCalculator, isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, touchTolerance: _touchTolerance, - ), + )..onDrawingMoved( + _widget.series.input, + startingEdgePoint, + endPoint: endingEdgePoint, + ), TrendDrawing( epochFromX: epochFromX, drawingPart: DrawingParts.line, @@ -242,7 +252,11 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { setCalculator: _setCalculator, isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, touchTolerance: _touchTolerance, - ), + )..onDrawingMoved( + _widget.series.input, + startingEdgePoint, + endPoint: endingEdgePoint, + ), TrendDrawing( epochFromX: epochFromX, drawingPart: DrawingParts.marker, @@ -251,7 +265,11 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { setCalculator: _setCalculator, isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, touchTolerance: _touchTolerance, - ) + )..onDrawingMoved( + _widget.series.input, + startingEdgePoint, + endPoint: endingEdgePoint, + ) ]); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index 91361411e..339c18101 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -39,6 +40,18 @@ class VerticalDrawing extends Drawing { /// Keeps the latest position of the start of drawing Point? startPoint; + @override + bool needsRepaint( + int leftEpoch, + int rightEpoch, + DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableEndPoint, + }) => + draggableStartPoint.isInViewPortRange( + leftEpoch, + rightEpoch, + ); + /// Paint @override void onPaint( diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index f4110474b..394b7a9e4 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -274,12 +274,7 @@ class _ChartImplementationState extends BasicChartState { quoteToCanvasY: chartQuoteToCanvasY, ), ), - DrawingToolChart( - series: widget.mainSeries as DataSeries, - chartQuoteToCanvasY: chartQuoteToCanvasY, - chartQuoteFromCanvasY: chartQuoteFromCanvasY, - drawingTools: widget.drawingTools, - ), + _buildDrawingToolChart(), if (!widget.drawingTools.isDrawingMoving) _buildCrosshairArea(), if (_isScrollToLastTickAvailable) Positioned( @@ -300,6 +295,19 @@ class _ChartImplementationState extends BasicChartState { }, ); + Widget _buildDrawingToolChart() => MultipleAnimatedBuilder( + animations: [ + topBoundQuoteAnimationController, + bottomBoundQuoteAnimationController, + ], + builder: (_, Widget? child) => DrawingToolChart( + series: widget.mainSeries as DataSeries, + chartQuoteToCanvasY: chartQuoteToCanvasY, + chartQuoteFromCanvasY: chartQuoteFromCanvasY, + drawingTools: widget.drawingTools, + ), + ); + Widget _buildLoadingAnimation() => LoadingAnimationArea( loadingRightBoundX: widget._mainSeries.input.isEmpty ? xAxis.width! diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index a069d0fcc..7acf79585 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -66,16 +66,16 @@ class _DrawingToolChartState extends State { child: Stack( fit: StackFit.expand, children: [ - ...widget.drawingTools.drawings - .map((DrawingData drawingData) => DrawingPainter( - drawingData: drawingData, - quoteToCanvasY: widget.chartQuoteToCanvasY, - quoteFromCanvasY: widget.chartQuoteFromCanvasY, - onMoveDrawing: widget.drawingTools.onMoveDrawing, - setIsDrawingSelected: _setIsDrawingSelected, - selectedDrawingTool: - widget.drawingTools.selectedDrawingTool, - )), + ...widget.drawingTools.drawings.map( + (DrawingData drawingData) => DrawingPainter( + drawingData: drawingData, + quoteToCanvasY: widget.chartQuoteToCanvasY, + quoteFromCanvasY: widget.chartQuoteFromCanvasY, + onMoveDrawing: widget.drawingTools.onMoveDrawing, + setIsDrawingSelected: _setIsDrawingSelected, + selectedDrawingTool: widget.drawingTools.selectedDrawingTool, + ), + ), if (widget.drawingTools.selectedDrawingTool != null) DrawingToolWidget( onAddDrawing: widget.drawingTools.onAddDrawing, From 56414d0b489187df5b8004481c92aae1cb90d882 Mon Sep 17 00:00:00 2001 From: ramin-deriv <55975218+ramin-deriv@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:11:03 +0800 Subject: [PATCH 17/29] fix market_selector_test.dart (#251) --- lib/generated/intl/messages_en.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 349e6d4ee..7b43c5db8 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -23,7 +23,7 @@ class MessageLookup extends MessageLookupByLibrary { static String m0(text) => "No results for \"${text}\""; final messages = _notInlinedMessages(_notInlinedMessages); - static _notInlinedMessages(_) => { + static Map _notInlinedMessages(_) => { "informNoResult": m0, "labelBandsCount": MessageLookupByLibrary.simpleMessage("Bands Count"), "labelBaseLinePeriod": From ec840ad1b24549eba3e8b882464d353f09b5dadb Mon Sep 17 00:00:00 2001 From: balakrishna-deriv <56330681+balakrishna-deriv@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:38:21 +0800 Subject: [PATCH 18/29] Bala/Add web support (#208) - add web support --- example/lib/main.dart | 2 +- lib/deriv_chart.dart | 56 ++++ lib/src/add_ons/add_ons_repository.dart | 85 +++--- .../continuous_drawing_tool_config.dart | 2 +- .../continuous_drawing_tool_item.dart | 3 +- .../drawing_tools_ui/drawing_tool_item.dart | 7 +- .../drawing_tools_dialog.dart | 10 +- .../line/line_drawing_tool_config.dart | 2 +- .../line/line_drawing_tool_item.dart | 3 +- .../vertical_drawing_tool_config.dart | 2 +- .../vertical/vertical_drawing_tool_item.dart | 3 +- .../adx/adx_indicator_config.dart | 37 ++- .../adx/adx_indicator_config.g.dart | 43 ++- .../alligator/alligator_indicator_config.dart | 30 +- .../alligator_indicator_config.g.dart | 39 ++- .../aroon/aroon_indicator_config.dart | 19 +- .../aroon/aroon_indicator_config.g.dart | 24 +- .../awesome_oscillator_indicator_config.dart | 17 +- ...awesome_oscillator_indicator_config.g.dart | 15 +- .../bollinger_bands_indicator_config.dart | 31 ++ .../bollinger_bands_indicator_config.g.dart | 66 ++--- .../cci_indicator_config.dart | 12 +- .../cci_indicator_config.g.dart | 28 +- .../cci_indicator_item.dart | 3 +- .../donchian_channel_indicator_config.dart | 11 +- .../donchian_channel_indicator_config.g.dart | 36 ++- .../dpo_indicator/dpo_indicator_config.dart | 12 + .../dpo_indicator/dpo_indicator_config.g.dart | 56 ++-- .../fcb_indicator/fcb_indicator_config.dart | 32 ++- .../fcb_indicator/fcb_indicator_config.g.dart | 27 +- .../fcb_indicator/fcb_indicator_item.dart | 4 +- .../gator/gator_indicator_config.dart | 14 +- .../gator/gator_indicator_config.g.dart | 28 +- .../ichimoku_cloud_indicator_config.dart | 31 +- .../ichimoku_cloud_indicator_config.g.dart | 41 ++- .../indicators_ui/indicator_config.dart | 16 +- .../add_ons/indicators_ui/indicator_item.dart | 6 +- .../indicators_ui/indicators_dialog.dart | 56 ++-- .../ma_env_indicator_config.dart | 31 ++ .../ma_env_indicator_config.g.dart | 72 +++-- .../ma_indicator/ma_indicator_config.dart | 16 +- .../ma_indicator/ma_indicator_config.g.dart | 70 ++--- .../ma_indicator/ma_indicator_item.dart | 4 +- .../macd_indicator/macd_indicator_config.dart | 45 ++- .../macd_indicator_config.g.dart | 31 +- .../oscillator_lines_config.g.dart | 21 +- .../parabolic_sar_indicator_config.dart | 3 +- .../parabolic_sar_indicator_config.g.dart | 20 +- .../rainbow_indicator_config.dart | 24 +- .../rainbow_indicator_config.g.dart | 52 ++-- .../roc/roc_indicator_config.dart | 22 +- .../roc/roc_indicator_config.g.dart | 21 +- .../rsi/rsi_indicator_config.dart | 19 +- .../rsi/rsi_indicator_config.g.dart | 31 +- .../indicators_ui/rsi/rsi_indicator_item.dart | 3 +- .../smi/smi_indicator_config.dart | 23 +- .../smi/smi_indicator_config.g.dart | 66 ++--- .../indicators_ui/smi/smi_indicator_item.dart | 5 +- ...tochastic_oscillator_indicator_config.dart | 21 +- ...chastic_oscillator_indicator_config.g.dart | 46 ++- .../williams_r_indicator_config.dart | 19 +- .../williams_r_indicator_config.g.dart | 34 ++- .../zigzag_indicator_config.dart | 6 +- .../zigzag_indicator_config.g.dart | 15 +- lib/src/add_ons/repository.dart | 22 ++ lib/src/deriv_chart/chart/basic_chart.dart | 58 +++- lib/src/deriv_chart/chart/bottom_chart.dart | 226 ++++++++++++--- lib/src/deriv_chart/chart/chart.dart | 241 ++++++++++++++-- .../chart/crosshair/crosshair_area.dart | 2 +- .../chart/crosshair/crosshair_area_web.dart | 92 ++++++ .../custom_painters/chart_data_painter.dart | 25 +- .../custom_painters/loading_painter.dart | 5 + .../accumulators_entry_spot_barrier.dart | 5 +- ...cumulators_entry_spot_barrier_painter.dart | 5 +- .../candle_indicator_painter.dart | 4 +- .../horizontal_barrier.dart | 4 +- .../horizontal_barrier_painter.dart | 7 +- .../horizontal_barrier/tick_indicator.dart | 6 +- .../chart/data_visualization/chart_data.dart | 6 + .../chart_series/data_series.dart | 6 +- .../indicators_series/adx_series.dart | 110 ++++++-- .../indicators_series/alligator_series.dart | 100 ++++--- .../indicators_series/aroon_series.dart | 54 ++-- .../bollinger_bands_series.dart | 126 ++++++--- .../indicators_series/cci_series.dart | 9 + .../donchian_channels_series.dart | 187 +++++------- .../indicators_series/dpo_series.dart | 27 +- .../indicators_series/fcb_series.dart | 86 ++++-- .../fractals_series/arrow_painter.dart | 4 +- .../indicators_series/gator_series.dart | 44 +-- .../ichimoku_cloud_series.dart | 128 ++++++--- .../indicators_series/ma_env_series.dart | 90 ++++-- .../indicators_series/ma_rainbow_series.dart | 65 +++-- .../indicators_series/ma_series.dart | 18 ++ .../indicators_series/macd_series.dart | 76 +++-- .../models/alligator_options.dart | 36 ++- .../models/bollinger_bands_options.dart | 40 ++- .../indicators_series/models/dpo_options.dart | 15 +- .../models/ichimoku_clouds_options.dart | 3 +- .../models/indicator_options.dart | 18 +- .../models/ma_env_options.dart | 40 ++- .../models/macd_options.dart | 23 +- .../models/rainbow_options.dart | 7 +- .../indicators_series/models/roc_options.dart | 9 +- .../indicators_series/models/rsi_options.dart | 9 +- .../indicators_series/models/smi_options.dart | 16 +- .../models/stochastic_oscillator_options.dart | 10 +- .../models/williams_r_options.dart | 9 +- .../parabolic_sar/parabolic_sar_series.dart | 11 + .../indicators_series/roc_series.dart | 18 +- .../indicators_series/rsi_series.dart | 8 +- .../sample_multi_series.dart | 4 +- .../indicators_series/smi_series.dart | 54 ++-- .../stochastic_oscillator_series.dart | 73 +++-- .../indicators_series/williams_r_series.dart | 25 +- .../indicators_series/zigzag_series.dart | 12 +- .../line_series/channel_fill_painter.dart | 4 +- .../line_series/line_painter.dart | 4 +- .../chart_series/line_series/line_series.dart | 6 +- .../line_series/oscillator_line_painter.dart | 5 +- .../line_series/oscillator_line_series.dart | 6 +- .../chart_series/series.dart | 2 +- .../chart_series/series_painter.dart | 4 +- .../continuous/continuous_line_drawing.dart | 3 +- .../drawing_tools/drawing_painter.dart | 1 - .../drawing_tools/line/line_drawing.dart | 3 +- .../vertical/vertical_drawing.dart | 3 +- .../markers/marker_area.dart | 5 +- .../markers/marker_series.dart | 7 +- .../deriv_chart/chart/helpers/indicator.dart | 27 ++ .../paint_functions/paint_loading.dart | 3 +- .../deriv_chart/chart/loading_animation.dart | 5 + lib/src/deriv_chart/chart/main_chart.dart | 109 ++++++- .../chart/x_axis/grid/calc_time_grid.dart | 3 + .../chart/x_axis/grid/check_new_day.dart | 8 + .../chart/x_axis/grid/paint_x_grid.dart | 54 ++-- .../chart/x_axis/grid/x_grid_painter.dart | 18 +- lib/src/deriv_chart/chart/x_axis/x_axis.dart | 48 +++- .../chart/x_axis/x_axis_model.dart | 77 ++--- lib/src/deriv_chart/deriv_chart.dart | 266 ++++++++++++------ .../drawing_tool_chart/drawing_tools.dart | 4 +- lib/src/misc/callbacks.dart | 43 +++ lib/src/misc/chart_controller.dart | 68 +++++ lib/src/theme/colors.dart | 16 +- .../theme/painting_styles/bar_style.g.dart | 16 +- .../theme/painting_styles/line_style.g.dart | 14 +- .../painting_styles/scatter_style.g.dart | 12 +- lib/src/widgets/bottom_indicator_title.dart | 19 ++ .../helpers/functions/conversion_test.dart | 64 +++++ 149 files changed, 3501 insertions(+), 1333 deletions(-) create mode 100644 lib/src/add_ons/repository.dart create mode 100644 lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart create mode 100644 lib/src/deriv_chart/chart/helpers/indicator.dart create mode 100644 lib/src/deriv_chart/chart/x_axis/grid/check_new_day.dart create mode 100644 lib/src/widgets/bottom_indicator_title.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index c03683f98..275107208 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -370,7 +370,7 @@ class _FullscreenChartState extends State { } DataSeries _getDataSeries(ChartStyle style) { - if (ticks is List) { + if (ticks is List && style != ChartStyle.line) { switch (style) { case ChartStyle.hollow: return HollowCandleSeries(ticks as List); diff --git a/lib/deriv_chart.dart b/lib/deriv_chart.dart index 13fb8b497..e093cb542 100644 --- a/lib/deriv_chart.dart +++ b/lib/deriv_chart.dart @@ -13,14 +13,37 @@ export 'src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal export 'src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/tick_indicator.dart'; export 'src/deriv_chart/chart/data_visualization/annotations/barriers/vertical_barrier/vertical_barrier.dart'; export 'src/deriv_chart/chart/data_visualization/annotations/chart_annotation.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_data.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/awesome_oscillator_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/aroon_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/cci_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/dpo_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fcb_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/gator_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/macd_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_rainbow_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/parabolic_sar/parabolic_sar_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/roc_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/rsi_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/smi_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/stochastic_oscillator_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/williams_r_series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/indicators_series/zigzag_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/line_series/line_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/candle/candle_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/hollow_candle/hollow_candle_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/ohlc_series/ohlc_candle/ohlc_candle_series.dart'; export 'src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; +export 'src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; export 'src/deriv_chart/chart/data_visualization/markers/active_marker.dart'; export 'src/deriv_chart/chart/data_visualization/markers/marker.dart'; export 'src/deriv_chart/chart/data_visualization/markers/marker_icon_painters/accumulators_marker_icon_painter.dart'; @@ -28,16 +51,49 @@ export 'src/deriv_chart/chart/data_visualization/markers/marker_icon_painters/ma export 'src/deriv_chart/chart/data_visualization/markers/marker_icon_painters/multipliers_marker_icon_painter.dart'; export 'src/deriv_chart/chart/data_visualization/markers/marker_icon_painters/options_marker_icon_painter.dart'; export 'src/deriv_chart/chart/data_visualization/markers/marker_series.dart'; +export 'src/deriv_chart/chart/data_visualization/models/animation_info.dart'; export 'src/deriv_chart/chart/data_visualization/models/chart_object.dart'; +export 'src/deriv_chart/chart/helpers/functions/helper_functions.dart'; +export 'src/deriv_chart/chart/helpers/paint_functions/paint_text.dart'; export 'src/deriv_chart/chart/helpers/paint_functions/paint_line.dart'; export 'src/deriv_chart/chart/worm_chart/worm_chart.dart'; export 'src/deriv_chart/chart/x_axis/min_candle_duration_for_data_fit.dart'; export 'src/deriv_chart/deriv_chart.dart'; export 'src/misc/chart_controller.dart'; +export 'src/misc/callbacks.dart'; export 'src/models/candle.dart'; export 'src/models/chart_axis_config.dart'; export 'src/models/chart_style.dart'; +export 'src/models/indicator_input.dart'; export 'src/models/tick.dart'; +export 'src/add_ons/add_on_config.dart'; +export 'src/add_ons/add_ons_repository.dart'; +export 'src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +export 'src/add_ons/indicators_ui/adx/adx_indicator_config.dart'; +export 'src/add_ons/indicators_ui/alligator/alligator_indicator_config.dart'; +export 'src/add_ons/indicators_ui/aroon/aroon_indicator_config.dart'; +export 'src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart'; +export 'src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.dart'; +export 'src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.dart'; +export 'src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart'; +export 'src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.dart'; +export 'src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.dart'; +export 'src/add_ons/indicators_ui/gator/gator_indicator_config.dart'; +export 'src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.dart'; +export 'src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.dart'; +export 'src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.dart'; +export 'src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.dart'; +export 'src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.dart'; +export 'src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.dart'; +export 'src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.dart'; +export 'src/add_ons/indicators_ui/roc/roc_indicator_config.dart'; +export 'src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart'; +export 'src/add_ons/indicators_ui/smi/smi_indicator_config.dart'; +export 'src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.dart'; +export 'src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.dart'; +export 'src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart'; +export 'src/add_ons/indicators_ui/indicator_config.dart'; +export 'src/add_ons/repository.dart'; export 'src/theme/chart_default_dark_theme.dart'; export 'src/theme/chart_default_light_theme.dart'; export 'src/theme/chart_theme.dart'; diff --git a/lib/src/add_ons/add_ons_repository.dart b/lib/src/add_ons/add_ons_repository.dart index d767f8683..1ca1c6b70 100644 --- a/lib/src/add_ons/add_ons_repository.dart +++ b/lib/src/add_ons/add_ons_repository.dart @@ -1,28 +1,37 @@ import 'dart:convert'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; -import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:deriv_chart/src/add_ons/add_on_config.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:shared_preferences/shared_preferences.dart'; -/// Storage key of saved indicators/drawing tools. -const String addOnsKey = 'addOns'; +/// Called to create an AddOnConfig object from a map. +typedef CreateAddOn = T Function( + Map map); /// Holds indicators/drawing tools that were added to the Chart during runtime. -class AddOnsRepository extends ChangeNotifier { +class AddOnsRepository extends ChangeNotifier + implements Repository { /// Initializes - AddOnsRepository(this._addOnConfig) : _addOns = []; - - final dynamic _addOnConfig; + AddOnsRepository({required this.createAddOn, this.onEditCallback}) + : _addOns = []; /// List containing addOns - final List _addOns; + final List _addOns; SharedPreferences? _prefs; /// List of indicators. - List get addOns => getAddOns(); + @override + List get items => _addOns; + + /// Storage key of saved indicators/drawing tools. + String get addOnsKey => 'addOns_${T.toString()}'; + + /// Called to create an AddOnConfig object from a map. + CreateAddOn createAddOn; - /// Getter for the list of addOns indicators or drawing tools. - List getAddOns() => _addOns; + /// Called when the edit icon is clicked. + VoidCallback? onEditCallback; /// Loads user selected indicators or drawing tools from shared preferences. void loadFromPrefs(SharedPreferences prefs) { @@ -34,50 +43,56 @@ class AddOnsRepository extends ChangeNotifier { } final List encodedAddOns = prefs.getStringList(addOnsKey)!; - getAddOns().clear(); + items.clear(); for (final String encodedAddOn in encodedAddOns) { - dynamic addOnConfig; - if (_addOnConfig is IndicatorConfig) { - addOnConfig = - IndicatorConfig.fromJson(jsonDecode(encodedAddOn)) as AddOnConfig; - } else if (_addOnConfig is DrawingToolConfig) { - addOnConfig = - DrawingToolConfig.fromJson(jsonDecode(encodedAddOn)) as AddOnConfig; - } - if (addOnConfig == null) { - continue; - } else { - getAddOns().add(addOnConfig); - } + final T addOnConfig = createAddOn.call(jsonDecode(encodedAddOn)); + items.add(addOnConfig); } - notifyListeners(); } /// Adds a new indicator or drawing tool and updates storage. - void add(AddOnConfig addOnConfig) { - getAddOns().add(addOnConfig); + @override + void add(T addOnConfig) { + items.add(addOnConfig); _writeToPrefs(); notifyListeners(); } + /// Called when the edit icon is clicked. + @override + void editAt(int index) { + onEditCallback?.call(); + } + /// Updates indicator or drawing tool at [index] and updates storage. - void updateAt(int index, AddOnConfig addOnConfig) { - if (index < 0 || index >= getAddOns().length) { + @override + void updateAt(int index, T addOnConfig) { + if (index < 0 || index >= items.length) { return; } - getAddOns()[index] = addOnConfig; + items[index] = addOnConfig; _writeToPrefs(); notifyListeners(); } /// Removes indicator/drawing tool at [index] from repository and /// updates storage. + @override void removeAt(int index) { - if (index < 0 || index >= getAddOns().length) { + if (index < 0 || index >= items.length) { return; } - getAddOns().removeAt(index); + items.removeAt(index); + _writeToPrefs(); + notifyListeners(); + } + + /// Swaps two elements of a list and updates storage. + @override + void swap(int index1, int index2) { + items.swap(index1, index2); + _writeToPrefs(); notifyListeners(); } @@ -85,7 +100,7 @@ class AddOnsRepository extends ChangeNotifier { if (_prefs != null) { await _prefs!.setStringList( addOnsKey, - getAddOns().map((AddOnConfig config) => jsonEncode(config)).toList(), + items.map((AddOnConfig config) => jsonEncode(config)).toList(), ); } } diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart index b7cfcf0da..f5b85da47 100644 --- a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart @@ -1,8 +1,8 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'continuous_drawing_tool_item.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart index 4cc4561be..c201c1c3a 100644 --- a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart @@ -1,8 +1,9 @@ +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'continuous_drawing_tool_config.dart'; import '../callbacks.dart'; import '../drawing_tool_config.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart index 3a09c640f..03d2f357a 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; import 'callbacks.dart'; import 'drawing_tool_config.dart'; @@ -41,14 +41,13 @@ abstract class DrawingToolItemState extends State { /// Drawing tools repository @protected - late AddOnsRepository drawingToolsRepo; + late Repository drawingToolsRepo; @override void didChangeDependencies() { super.didChangeDependencies(); - drawingToolsRepo = - Provider.of>(context); + drawingToolsRepo = Provider.of>(context); } @override diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index ed22de64e..9738ec50e 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -13,7 +13,7 @@ import 'package:deriv_chart/src/widgets/animated_popup.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'drawing_tool_config.dart'; -import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; /// Drawing tools dialog with available drawing tools. class DrawingToolsDialog extends StatefulWidget { @@ -36,8 +36,8 @@ class _DrawingToolsDialogState extends State { @override Widget build(BuildContext context) { - final AddOnsRepository repo = - context.watch>(); + final Repository repo = + context.watch>(); return AnimatedPopupDialog( child: Column( @@ -109,9 +109,9 @@ class _DrawingToolsDialogState extends State { Expanded( child: ListView.builder( shrinkWrap: true, - itemCount: repo.getAddOns().length, + itemCount: repo.items.length, itemBuilder: (BuildContext context, int index) => - repo.getAddOns()[index].getItem( + repo.items[index].getItem( (DrawingToolConfig updatedConfig) { widget.drawingTools.onDrawingToolUpdate(index, updatedConfig); repo.updateAt(index, updatedConfig); diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart index ede15ed14..3d9a4b68c 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart @@ -1,8 +1,8 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'line_drawing_tool_item.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart index f72d4425e..8244e5e73 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart @@ -1,8 +1,9 @@ +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'line_drawing_tool_config.dart'; import '../callbacks.dart'; import '../drawing_tool_config.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart index 83c62c354..6545e328e 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart @@ -1,8 +1,8 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart index 6f331c891..1da592498 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart @@ -1,7 +1,8 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.dart b/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.dart index 73c6c17d9..5d80dd0c1 100644 --- a/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.dart @@ -2,6 +2,8 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/adx_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/bar_style.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -22,7 +24,20 @@ class ADXIndicatorConfig extends IndicatorConfig { this.showSeries = true, this.showChannelFill = false, this.showHistogram = false, - }) : super(isOverlay: false); + this.showShading = false, + this.lineStyle = const LineStyle(color: Colors.white), + this.positiveLineStyle = const LineStyle(color: Colors.green), + this.negativeLineStyle = const LineStyle(color: Colors.red), + this.barStyle = const BarStyle(), + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + showLastIndicator: showLastIndicator, + pipSize: pipSize, + title: title ?? ADXIndicatorConfig.name, + ); /// Initializes from JSON. factory ADXIndicatorConfig.fromJson(Map json) => @@ -41,15 +56,31 @@ class ADXIndicatorConfig extends IndicatorConfig { /// The period value for smoothing the ADX series. final int smoothingPeriod; - /// Wether to add channel fill between the Positive and Negative DI Indicator. + /// Whether to add channel fill between the Positive and Negative DI + /// Indicator. final bool showChannelFill; - /// Wether to show the histogram Series or not. + /// Whether to show the histogram Series or not. final bool showHistogram; + /// Whether to show the shading or not. + final bool showShading; + /// Wether to show the Series or not. final bool showSeries; + /// Line style. + final LineStyle lineStyle; + + /// Positive line style. + final LineStyle positiveLineStyle; + + /// Negative line style. + final LineStyle negativeLineStyle; + + /// Histogram bar style + final BarStyle barStyle; + @override Series getSeries(IndicatorInput indicatorInput) => ADXSeries( indicatorInput, diff --git a/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.g.dart index bef041b6d..15973a030 100644 --- a/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/adx/adx_indicator_config.g.dart @@ -6,21 +6,46 @@ part of 'adx_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -ADXIndicatorConfig _$ADXIndicatorConfigFromJson(Map json) { - return ADXIndicatorConfig( - period: json['period'] as int, - smoothingPeriod: json['smoothingPeriod'] as int, - showSeries: json['showSeries'] as bool, - showChannelFill: json['showChannelFill'] as bool, - showHistogram: json['showHistogram'] as bool, - ); -} +ADXIndicatorConfig _$ADXIndicatorConfigFromJson(Map json) => + ADXIndicatorConfig( + period: json['period'] as int? ?? 14, + smoothingPeriod: json['smoothingPeriod'] as int? ?? 14, + showSeries: json['showSeries'] as bool? ?? true, + showChannelFill: json['showChannelFill'] as bool? ?? false, + showHistogram: json['showHistogram'] as bool? ?? false, + showShading: json['showShading'] as bool? ?? false, + lineStyle: json['lineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + positiveLineStyle: json['positiveLineStyle'] == null + ? const LineStyle(color: Colors.green) + : LineStyle.fromJson( + json['positiveLineStyle'] as Map), + negativeLineStyle: json['negativeLineStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson( + json['negativeLineStyle'] as Map), + barStyle: json['barStyle'] == null + ? const BarStyle() + : BarStyle.fromJson(json['barStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$ADXIndicatorConfigToJson(ADXIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'smoothingPeriod': instance.smoothingPeriod, 'showChannelFill': instance.showChannelFill, 'showHistogram': instance.showHistogram, + 'showShading': instance.showShading, 'showSeries': instance.showSeries, + 'lineStyle': instance.lineStyle, + 'positiveLineStyle': instance.positiveLineStyle, + 'negativeLineStyle': instance.negativeLineStyle, + 'barStyle': instance.barStyle, }; diff --git a/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.dart b/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.dart index cdf9d9de2..26c50f3d7 100644 --- a/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/alligator_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -25,7 +26,15 @@ class AlligatorIndicatorConfig extends IndicatorConfig { this.lipsOffset = 3, this.showLines = true, this.showFractal = false, - }) : super(); + this.jawLineStyle = const LineStyle(color: Colors.blue), + this.teethLineStyle = const LineStyle(color: Colors.red), + this.lipsLineStyle = const LineStyle(color: Colors.green), + bool showLastIndicator = false, + String? title, + }) : super( + showLastIndicator: showLastIndicator, + title: title ?? AlligatorIndicatorConfig.name, + ); /// Initializes from JSON. factory AlligatorIndicatorConfig.fromJson(Map json) => @@ -62,18 +71,31 @@ class AlligatorIndicatorConfig extends IndicatorConfig { /// show fractal indicator or not final bool showFractal; + /// Jaw line style. + final LineStyle jawLineStyle; + + /// Teeth line style. + final LineStyle teethLineStyle; + + /// Lips line style. + final LineStyle lipsLineStyle; + @override Series getSeries(IndicatorInput indicatorInput) => AlligatorSeries( indicatorInput, - jawOffset: jawOffset, - teethOffset: teethOffset, - lipsOffset: lipsOffset, alligatorOptions: AlligatorOptions( jawPeriod: jawPeriod, teethPeriod: teethPeriod, lipsPeriod: lipsPeriod, showLines: showLines, showFractal: showFractal, + jawOffset: jawOffset, + teethOffset: teethOffset, + lipsOffset: lipsOffset, + jawLineStyle: jawLineStyle, + teethLineStyle: teethLineStyle, + lipsLineStyle: lipsLineStyle, + showLastIndicator: showLastIndicator, ), ); diff --git a/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.g.dart index 0adbe0fe7..375f5aca7 100644 --- a/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_config.g.dart @@ -7,22 +7,34 @@ part of 'alligator_indicator_config.dart'; // ************************************************************************** AlligatorIndicatorConfig _$AlligatorIndicatorConfigFromJson( - Map json) { - return AlligatorIndicatorConfig( - jawPeriod: json['jawPeriod'] as int, - teethPeriod: json['teethPeriod'] as int, - lipsPeriod: json['lipsPeriod'] as int, - jawOffset: json['jawOffset'] as int, - teethOffset: json['teethOffset'] as int, - lipsOffset: json['lipsOffset'] as int, - showLines: json['showLines'] as bool, - showFractal: json['showFractal'] as bool, - ); -} + Map json) => + AlligatorIndicatorConfig( + jawPeriod: json['jawPeriod'] as int? ?? 13, + teethPeriod: json['teethPeriod'] as int? ?? 8, + lipsPeriod: json['lipsPeriod'] as int? ?? 5, + jawOffset: json['jawOffset'] as int? ?? 8, + teethOffset: json['teethOffset'] as int? ?? 5, + lipsOffset: json['lipsOffset'] as int? ?? 3, + showLines: json['showLines'] as bool? ?? true, + showFractal: json['showFractal'] as bool? ?? false, + jawLineStyle: json['jawLineStyle'] == null + ? const LineStyle(color: Colors.blue) + : LineStyle.fromJson(json['jawLineStyle'] as Map), + teethLineStyle: json['teethLineStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson(json['teethLineStyle'] as Map), + lipsLineStyle: json['lipsLineStyle'] == null + ? const LineStyle(color: Colors.green) + : LineStyle.fromJson(json['lipsLineStyle'] as Map), + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$AlligatorIndicatorConfigToJson( AlligatorIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, 'jawOffset': instance.jawOffset, 'jawPeriod': instance.jawPeriod, 'teethOffset': instance.teethOffset, @@ -31,4 +43,7 @@ Map _$AlligatorIndicatorConfigToJson( 'lipsPeriod': instance.lipsPeriod, 'showLines': instance.showLines, 'showFractal': instance.showFractal, + 'jawLineStyle': instance.jawLineStyle, + 'teethLineStyle': instance.teethLineStyle, + 'lipsLineStyle': instance.lipsLineStyle, }; diff --git a/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.dart b/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.dart index 0d831f72a..81c057d26 100644 --- a/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.dart @@ -5,6 +5,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/aroon_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -18,7 +19,17 @@ class AroonIndicatorConfig extends IndicatorConfig { /// Initializes const AroonIndicatorConfig({ this.period = 14, - }) : super(isOverlay: false); + this.upLineStyle = const LineStyle(color: Colors.green), + this.downLineStyle = const LineStyle(color: Colors.red), + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? AroonIndicatorConfig.name, + ); /// Initializes from JSON. factory AroonIndicatorConfig.fromJson(Map json) => @@ -34,6 +45,12 @@ class AroonIndicatorConfig extends IndicatorConfig { /// The period final int period; + /// Up line style. + final LineStyle upLineStyle; + + /// Down line style. + final LineStyle downLineStyle; + @override Series getSeries(IndicatorInput indicatorInput) => AroonSeries( indicatorInput, diff --git a/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.g.dart index c2b89ad47..d38c5bf33 100644 --- a/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_config.g.dart @@ -6,14 +6,28 @@ part of 'aroon_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -AroonIndicatorConfig _$AroonIndicatorConfigFromJson(Map json) { - return AroonIndicatorConfig( - period: json['period'] as int, - ); -} +AroonIndicatorConfig _$AroonIndicatorConfigFromJson( + Map json) => + AroonIndicatorConfig( + period: json['period'] as int? ?? 14, + upLineStyle: json['upLineStyle'] == null + ? const LineStyle(color: Colors.green) + : LineStyle.fromJson(json['upLineStyle'] as Map), + downLineStyle: json['downLineStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson(json['downLineStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$AroonIndicatorConfigToJson( AroonIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, + 'upLineStyle': instance.upLineStyle, + 'downLineStyle': instance.downLineStyle, }; diff --git a/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart b/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart index d363a3b60..f1906272d 100644 --- a/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart @@ -16,8 +16,15 @@ part 'awesome_oscillator_indicator_config.g.dart'; @JsonSerializable() class AwesomeOscillatorIndicatorConfig extends IndicatorConfig { /// Initializes - const AwesomeOscillatorIndicatorConfig({this.barStyle = const BarStyle()}) - : super(isOverlay: false); + const AwesomeOscillatorIndicatorConfig({ + this.barStyle = const BarStyle(), + int pipSize = 4, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + title: title ?? AwesomeOscillatorIndicatorConfig.name, + ); /// Initializes from JSON. factory AwesomeOscillatorIndicatorConfig.fromJson( @@ -28,8 +35,10 @@ class AwesomeOscillatorIndicatorConfig extends IndicatorConfig { final BarStyle barStyle; @override - Series getSeries(IndicatorInput indicatorInput) => - AwesomeOscillatorSeries(indicatorInput, barStyle: barStyle); + Series getSeries(IndicatorInput indicatorInput) => AwesomeOscillatorSeries( + indicatorInput, + barStyle: barStyle, + ); /// Unique name for this indicator. static const String name = 'AwesomeOscillator'; diff --git a/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.g.dart index a2e86a40d..3616667f3 100644 --- a/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.g.dart @@ -7,14 +7,19 @@ part of 'awesome_oscillator_indicator_config.dart'; // ************************************************************************** AwesomeOscillatorIndicatorConfig _$AwesomeOscillatorIndicatorConfigFromJson( - Map json) { - return AwesomeOscillatorIndicatorConfig( - barStyle: BarStyle.fromJson(json['barStyle'] as Map), - ); -} + Map json) => + AwesomeOscillatorIndicatorConfig( + barStyle: json['barStyle'] == null + ? const BarStyle() + : BarStyle.fromJson(json['barStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + title: json['title'] as String?, + ); Map _$AwesomeOscillatorIndicatorConfigToJson( AwesomeOscillatorIndicatorConfig instance) => { + 'title': instance.title, + 'pipSize': instance.pipSize, 'barStyle': instance.barStyle, }; diff --git a/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.dart b/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.dart index 7d86a31fa..c98205a9c 100644 --- a/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.dart @@ -3,7 +3,9 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/bollinger_bands_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/color_converter.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -16,6 +18,7 @@ part 'bollinger_bands_indicator_config.g.dart'; /// Bollinger Bands Indicator Config @JsonSerializable() +@ColorConverter() class BollingerBandsIndicatorConfig extends MAIndicatorConfig { /// Initializes const BollingerBandsIndicatorConfig({ @@ -23,10 +26,17 @@ class BollingerBandsIndicatorConfig extends MAIndicatorConfig { MovingAverageType movingAverageType = MovingAverageType.simple, String fieldType = 'close', this.standardDeviation = 2, + this.upperLineStyle = const LineStyle(color: Colors.white), + this.middleLineStyle = const LineStyle(color: Colors.white), + this.lowerLineStyle = const LineStyle(color: Colors.white), + this.fillColor = Colors.white12, + this.showChannelFill = true, + bool showLastIndicator = false, }) : super( period: period, movingAverageType: movingAverageType, fieldType: fieldType, + showLastIndicator: showLastIndicator, ); /// Initializes from JSON. @@ -43,6 +53,21 @@ class BollingerBandsIndicatorConfig extends MAIndicatorConfig { /// Standard Deviation value final double standardDeviation; + /// Upper line style. + final LineStyle upperLineStyle; + + /// Middle line style. + final LineStyle middleLineStyle; + + /// Lower line style. + final LineStyle lowerLineStyle; + + /// Fill color. + final Color fillColor; + + /// Whether the area between upper and lower channel is filled. + final bool showChannelFill; + @override Series getSeries(IndicatorInput indicatorInput) => BollingerBandSeries.fromIndicator( @@ -51,6 +76,12 @@ class BollingerBandsIndicatorConfig extends MAIndicatorConfig { period: period, movingAverageType: movingAverageType, standardDeviationFactor: standardDeviation, + upperLineStyle: upperLineStyle, + middleLineStyle: middleLineStyle, + lowerLineStyle: lowerLineStyle, + fillColor: fillColor, + showChannelFill: showChannelFill, + showLastIndicator: showLastIndicator, ), ); diff --git a/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.g.dart index 4de336912..7ce855ac5 100644 --- a/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_config.g.dart @@ -7,52 +7,46 @@ part of 'bollinger_bands_indicator_config.dart'; // ************************************************************************** BollingerBandsIndicatorConfig _$BollingerBandsIndicatorConfigFromJson( - Map json) { - return BollingerBandsIndicatorConfig( - period: json['period'] as int, - movingAverageType: - _$enumDecode(_$MovingAverageTypeEnumMap, json['movingAverageType']), - fieldType: json['fieldType'] as String, - standardDeviation: (json['standardDeviation'] as num).toDouble(), - ); -} + Map json) => + BollingerBandsIndicatorConfig( + period: json['period'] as int? ?? 50, + movingAverageType: $enumDecodeNullable( + _$MovingAverageTypeEnumMap, json['movingAverageType']) ?? + MovingAverageType.simple, + fieldType: json['fieldType'] as String? ?? 'close', + standardDeviation: (json['standardDeviation'] as num?)?.toDouble() ?? 2, + upperLineStyle: json['upperLineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['upperLineStyle'] as Map), + middleLineStyle: json['middleLineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['middleLineStyle'] as Map), + lowerLineStyle: json['lowerLineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['lowerLineStyle'] as Map), + fillColor: json['fillColor'] == null + ? Colors.white12 + : const ColorConverter().fromJson(json['fillColor'] as int), + showChannelFill: json['showChannelFill'] as bool? ?? true, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + ); Map _$BollingerBandsIndicatorConfigToJson( BollingerBandsIndicatorConfig instance) => { + 'showLastIndicator': instance.showLastIndicator, 'period': instance.period, 'movingAverageType': - _$MovingAverageTypeEnumMap[instance.movingAverageType], + _$MovingAverageTypeEnumMap[instance.movingAverageType]!, 'fieldType': instance.fieldType, 'standardDeviation': instance.standardDeviation, + 'upperLineStyle': instance.upperLineStyle, + 'middleLineStyle': instance.middleLineStyle, + 'lowerLineStyle': instance.lowerLineStyle, + 'fillColor': const ColorConverter().toJson(instance.fillColor), + 'showChannelFill': instance.showChannelFill, }; -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', - ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} - const _$MovingAverageTypeEnumMap = { MovingAverageType.simple: 'simple', MovingAverageType.exponential: 'exponential', diff --git a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.dart b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.dart index a9ed4216d..28f508bca 100644 --- a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.dart @@ -26,7 +26,15 @@ class CCIIndicatorConfig extends IndicatorConfig { ), this.showZones = true, this.lineStyle = const LineStyle(color: Colors.white), - }) : super(isOverlay: false); + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? CCIIndicatorConfig.name, + ); /// Initializes from JSON. factory CCIIndicatorConfig.fromJson(Map json) => @@ -60,6 +68,8 @@ class CCIIndicatorConfig extends IndicatorConfig { overboughtLineStyle: oscillatorLinesConfig.overboughtStyle, oversoldLineStyle: oscillatorLinesConfig.oversoldStyle, showZones: showZones, + cciLineStyle: lineStyle, + showLastIndicator: showLastIndicator, ); @override diff --git a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.g.dart index ddd730537..36ec29a18 100644 --- a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_config.g.dart @@ -6,18 +6,28 @@ part of 'cci_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -CCIIndicatorConfig _$CCIIndicatorConfigFromJson(Map json) { - return CCIIndicatorConfig( - period: json['period'] as int, - oscillatorLinesConfig: OscillatorLinesConfig.fromJson( - json['oscillatorLinesConfig'] as Map), - showZones: json['showZones'] as bool, - lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), - ); -} +CCIIndicatorConfig _$CCIIndicatorConfigFromJson(Map json) => + CCIIndicatorConfig( + period: json['period'] as int? ?? 20, + oscillatorLinesConfig: json['oscillatorLinesConfig'] == null + ? const OscillatorLinesConfig( + overboughtValue: 100, oversoldValue: -100) + : OscillatorLinesConfig.fromJson( + json['oscillatorLinesConfig'] as Map), + showZones: json['showZones'] as bool? ?? true, + lineStyle: json['lineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$CCIIndicatorConfigToJson(CCIIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'oscillatorLinesConfig': instance.oscillatorLinesConfig, 'lineStyle': instance.lineStyle, diff --git a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart index 5461ae23b..24bf55534 100644 --- a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart @@ -1,7 +1,8 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/field_widget.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/oscillator_limit.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart index 46257ad3c..51a261bee 100644 --- a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart @@ -1,8 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/callbacks.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/color_converter.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -26,7 +28,12 @@ class DonchianChannelIndicatorConfig extends IndicatorConfig { this.middleLineStyle = const LineStyle(color: Colors.white), this.lowerLineStyle = const LineStyle(color: Colors.green), this.fillColor = Colors.white12, - }) : super(); + bool showLastIndicator = false, + String? title, + }) : super( + title: title ?? DonchianChannelIndicatorConfig.name, + showLastIndicator: showLastIndicator, + ); /// Initializes from JSON. factory DonchianChannelIndicatorConfig.fromJson(Map json) => diff --git a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.g.dart index e1e770ca6..9ad774ddb 100644 --- a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.g.dart @@ -7,24 +7,32 @@ part of 'donchian_channel_indicator_config.dart'; // ************************************************************************** DonchianChannelIndicatorConfig _$DonchianChannelIndicatorConfigFromJson( - Map json) { - return DonchianChannelIndicatorConfig( - highPeriod: json['highPeriod'] as int, - lowPeriod: json['lowPeriod'] as int, - showChannelFill: json['showChannelFill'] as bool, - upperLineStyle: - LineStyle.fromJson(json['upperLineStyle'] as Map), - middleLineStyle: - LineStyle.fromJson(json['middleLineStyle'] as Map), - lowerLineStyle: - LineStyle.fromJson(json['lowerLineStyle'] as Map), - fillColor: const ColorConverter().fromJson(json['fillColor'] as int), - ); -} + Map json) => + DonchianChannelIndicatorConfig( + highPeriod: json['highPeriod'] as int? ?? 10, + lowPeriod: json['lowPeriod'] as int? ?? 10, + showChannelFill: json['showChannelFill'] as bool? ?? true, + upperLineStyle: json['upperLineStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson(json['upperLineStyle'] as Map), + middleLineStyle: json['middleLineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['middleLineStyle'] as Map), + lowerLineStyle: json['lowerLineStyle'] == null + ? const LineStyle(color: Colors.green) + : LineStyle.fromJson(json['lowerLineStyle'] as Map), + fillColor: json['fillColor'] == null + ? Colors.white12 + : const ColorConverter().fromJson(json['fillColor'] as int), + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$DonchianChannelIndicatorConfigToJson( DonchianChannelIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, 'highPeriod': instance.highPeriod, 'lowPeriod': instance.lowPeriod, 'showChannelFill': instance.showChannelFill, diff --git a/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.dart b/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.dart index 1cdbb429c..08dd7ebba 100644 --- a/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.dart @@ -4,6 +4,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/dpo_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -23,11 +24,19 @@ class DPOIndicatorConfig extends MAIndicatorConfig { MovingAverageType movingAverageType = MovingAverageType.simple, String fieldType = 'close', this.isCentered = true, + LineStyle? lineStyle, + int pipSize = 4, + bool showLastIndicator = false, + String? title, }) : super( period: period, movingAverageType: movingAverageType, fieldType: fieldType, isOverlay: false, + lineStyle: lineStyle, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? DPOIndicatorConfig.name, ); /// Initializes from JSON. @@ -51,6 +60,9 @@ class DPOIndicatorConfig extends MAIndicatorConfig { period: period, movingAverageType: movingAverageType, isCentered: isCentered, + lineStyle: lineStyle, + pipSize: pipSize, + showLastIndicator: showLastIndicator, ), ); diff --git a/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.g.dart index 82372ef4e..ee5a1cde2 100644 --- a/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/dpo_indicator/dpo_indicator_config.g.dart @@ -6,51 +6,35 @@ part of 'dpo_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -DPOIndicatorConfig _$DPOIndicatorConfigFromJson(Map json) { - return DPOIndicatorConfig( - period: json['period'] as int, - movingAverageType: - _$enumDecode(_$MovingAverageTypeEnumMap, json['movingAverageType']), - fieldType: json['fieldType'] as String, - isCentered: json['isCentered'] as bool, - ); -} +DPOIndicatorConfig _$DPOIndicatorConfigFromJson(Map json) => + DPOIndicatorConfig( + period: json['period'] as int? ?? 14, + movingAverageType: $enumDecodeNullable( + _$MovingAverageTypeEnumMap, json['movingAverageType']) ?? + MovingAverageType.simple, + fieldType: json['fieldType'] as String? ?? 'close', + isCentered: json['isCentered'] as bool? ?? true, + lineStyle: json['lineStyle'] == null + ? null + : LineStyle.fromJson(json['lineStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$DPOIndicatorConfigToJson(DPOIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'movingAverageType': - _$MovingAverageTypeEnumMap[instance.movingAverageType], + _$MovingAverageTypeEnumMap[instance.movingAverageType]!, 'fieldType': instance.fieldType, + 'lineStyle': instance.lineStyle, 'isCentered': instance.isCentered, }; -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', - ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} - const _$MovingAverageTypeEnumMap = { MovingAverageType.simple: 'simple', MovingAverageType.exponential: 'exponential', diff --git a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.dart b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.dart index d3957b0bd..4f1adfdb6 100644 --- a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.dart @@ -1,6 +1,8 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fcb_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/color_converter.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -13,20 +15,42 @@ part 'fcb_indicator_config.g.dart'; /// Fractal Chaos Band Indicator Config @JsonSerializable() +@ColorConverter() class FractalChaosBandIndicatorConfig extends IndicatorConfig { /// Initializes - const FractalChaosBandIndicatorConfig({this.channelFill = false}) : super(); + const FractalChaosBandIndicatorConfig({ + this.highLineStyle = const LineStyle(color: Colors.blue), + this.lowLineStyle = const LineStyle(color: Colors.blue), + this.fillColor = Colors.white12, + this.showChannelFill = false, + bool showLastIndicator = false, + String? title, + }) : super( + showLastIndicator: showLastIndicator, + title: title ?? FractalChaosBandIndicatorConfig.name, + ); /// Initializes from JSON. factory FractalChaosBandIndicatorConfig.fromJson(Map json) => _$FractalChaosBandIndicatorConfigFromJson(json); + /// Upper line style. + final LineStyle highLineStyle; + + /// Lower line style. + final LineStyle lowLineStyle; + + /// Fill color. + final Color fillColor; + /// if it's true the channel between two lines will be filled - final bool channelFill; + final bool showChannelFill; @override - Series getSeries(IndicatorInput indicatorInput) => - FractalChaosBandSeries(indicatorInput); + Series getSeries(IndicatorInput indicatorInput) => FractalChaosBandSeries( + indicatorInput, + config: this, + ); /// Unique name for this indicator. static const String name = 'fcb'; diff --git a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.g.dart index a00a8889b..cff249bcd 100644 --- a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.g.dart @@ -7,14 +7,29 @@ part of 'fcb_indicator_config.dart'; // ************************************************************************** FractalChaosBandIndicatorConfig _$FractalChaosBandIndicatorConfigFromJson( - Map json) { - return FractalChaosBandIndicatorConfig( - channelFill: json['channelFill'] as bool, - ); -} + Map json) => + FractalChaosBandIndicatorConfig( + highLineStyle: json['highLineStyle'] == null + ? const LineStyle(color: Colors.blue) + : LineStyle.fromJson(json['highLineStyle'] as Map), + lowLineStyle: json['lowLineStyle'] == null + ? const LineStyle(color: Colors.blue) + : LineStyle.fromJson(json['lowLineStyle'] as Map), + fillColor: json['fillColor'] == null + ? Colors.white12 + : const ColorConverter().fromJson(json['fillColor'] as int), + showChannelFill: json['showChannelFill'] as bool? ?? false, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$FractalChaosBandIndicatorConfigToJson( FractalChaosBandIndicatorConfig instance) => { - 'channelFill': instance.channelFill, + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'highLineStyle': instance.highLineStyle, + 'lowLineStyle': instance.lowLineStyle, + 'fillColor': const ColorConverter().toJson(instance.fillColor), + 'showChannelFill': instance.showChannelFill, }; diff --git a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart index 146010e03..5eccd97d0 100644 --- a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart @@ -39,7 +39,7 @@ class FractalChaosBandIndicatorItemState @override FractalChaosBandIndicatorConfig createIndicatorConfig() => FractalChaosBandIndicatorConfig( - channelFill: currentChannelFill, + showChannelFill: currentChannelFill, ); @override @@ -73,5 +73,5 @@ class FractalChaosBandIndicatorItemState @protected bool get currentChannelFill => _channelFill ?? - (widget.config as FractalChaosBandIndicatorConfig).channelFill; + (widget.config as FractalChaosBandIndicatorConfig).showChannelFill; } diff --git a/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.dart b/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.dart index de4e73ac1..e19773f4f 100644 --- a/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.dart @@ -2,6 +2,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/alligator_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/bar_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -23,7 +24,14 @@ class GatorIndicatorConfig extends IndicatorConfig { this.jawOffset = 8, this.teethOffset = 5, this.lipsOffset = 3, - }) : super(isOverlay: false); + this.barStyle = const BarStyle(), + int pipSize = 4, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + title: title ?? GatorIndicatorConfig.name, + ); /// Initializes from JSON. factory GatorIndicatorConfig.fromJson(Map json) => @@ -54,6 +62,9 @@ class GatorIndicatorConfig extends IndicatorConfig { /// Smoothing period for lips series final int lipsPeriod; + /// Histogram bar style + final BarStyle barStyle; + @override Series getSeries(IndicatorInput indicatorInput) => GatorSeries( indicatorInput, @@ -63,6 +74,7 @@ class GatorIndicatorConfig extends IndicatorConfig { teethPeriod: teethPeriod, lipsPeriod: lipsPeriod, ), + barStyle: barStyle, ); @override diff --git a/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.g.dart index c38f481f9..fcc41abc9 100644 --- a/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/gator/gator_indicator_config.g.dart @@ -6,24 +6,32 @@ part of 'gator_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -GatorIndicatorConfig _$GatorIndicatorConfigFromJson(Map json) { - return GatorIndicatorConfig( - jawPeriod: json['jawPeriod'] as int, - teethPeriod: json['teethPeriod'] as int, - lipsPeriod: json['lipsPeriod'] as int, - jawOffset: json['jawOffset'] as int, - teethOffset: json['teethOffset'] as int, - lipsOffset: json['lipsOffset'] as int, - ); -} +GatorIndicatorConfig _$GatorIndicatorConfigFromJson( + Map json) => + GatorIndicatorConfig( + jawPeriod: json['jawPeriod'] as int? ?? 13, + teethPeriod: json['teethPeriod'] as int? ?? 8, + lipsPeriod: json['lipsPeriod'] as int? ?? 5, + jawOffset: json['jawOffset'] as int? ?? 8, + teethOffset: json['teethOffset'] as int? ?? 5, + lipsOffset: json['lipsOffset'] as int? ?? 3, + barStyle: json['barStyle'] == null + ? const BarStyle() + : BarStyle.fromJson(json['barStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + title: json['title'] as String?, + ); Map _$GatorIndicatorConfigToJson( GatorIndicatorConfig instance) => { + 'title': instance.title, + 'pipSize': instance.pipSize, 'jawOffset': instance.jawOffset, 'jawPeriod': instance.jawPeriod, 'teethOffset': instance.teethOffset, 'teethPeriod': instance.teethPeriod, 'lipsOffset': instance.lipsOffset, 'lipsPeriod': instance.lipsPeriod, + 'barStyle': instance.barStyle, }; diff --git a/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.dart b/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.dart index 8e0f7ad69..6981d5bc3 100644 --- a/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.dart @@ -2,7 +2,8 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ichimoku_clouds_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; -import 'package:flutter/foundation.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import '../callbacks.dart'; @@ -21,7 +22,17 @@ class IchimokuCloudIndicatorConfig extends IndicatorConfig { this.conversionLinePeriod = 9, this.laggingSpanOffset = -26, this.spanBPeriod = 52, - }) : super(); + this.conversionLineStyle = const LineStyle(color: Colors.indigo), + this.baseLineStyle = const LineStyle(color: Colors.redAccent), + this.spanALineStyle = const LineStyle(color: Colors.green), + this.spanBLineStyle = const LineStyle(color: Colors.red), + this.laggingLineStyle = const LineStyle(color: Colors.lime), + bool showLastIndicator = false, + String? title, + }) : super( + showLastIndicator: showLastIndicator, + title: title ?? IchimokuCloudIndicatorConfig.name, + ); /// Initializes from JSON. factory IchimokuCloudIndicatorConfig.fromJson(Map json) => @@ -46,6 +57,21 @@ class IchimokuCloudIndicatorConfig extends IndicatorConfig { /// The period to calculate the Base Line value. final int laggingSpanOffset; + /// Conversion line style. + final LineStyle conversionLineStyle; + + /// Base line style. + final LineStyle baseLineStyle; + + /// Span A style. + final LineStyle spanALineStyle; + + /// Span B style. + final LineStyle spanBLineStyle; + + /// Lagging line style. + final LineStyle laggingLineStyle; + @override Series getSeries(IndicatorInput indicatorInput) => IchimokuCloudSeries(indicatorInput, @@ -54,6 +80,7 @@ class IchimokuCloudIndicatorConfig extends IndicatorConfig { baseLinePeriod: baseLinePeriod, conversionLinePeriod: conversionLinePeriod, leadingSpanBPeriod: spanBPeriod, + showLastIndicator: showLastIndicator, )); @override diff --git a/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.g.dart index b030f703a..d791ec386 100644 --- a/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.g.dart @@ -7,20 +7,45 @@ part of 'ichimoku_cloud_indicator_config.dart'; // ************************************************************************** IchimokuCloudIndicatorConfig _$IchimokuCloudIndicatorConfigFromJson( - Map json) { - return IchimokuCloudIndicatorConfig( - baseLinePeriod: json['baseLinePeriod'] as int, - conversionLinePeriod: json['conversionLinePeriod'] as int, - laggingSpanOffset: json['laggingSpanOffset'] as int, - spanBPeriod: json['spanBPeriod'] as int, - ); -} + Map json) => + IchimokuCloudIndicatorConfig( + baseLinePeriod: json['baseLinePeriod'] as int? ?? 26, + conversionLinePeriod: json['conversionLinePeriod'] as int? ?? 9, + laggingSpanOffset: json['laggingSpanOffset'] as int? ?? -26, + spanBPeriod: json['spanBPeriod'] as int? ?? 52, + conversionLineStyle: json['conversionLineStyle'] == null + ? const LineStyle(color: Colors.indigo) + : LineStyle.fromJson( + json['conversionLineStyle'] as Map), + baseLineStyle: json['baseLineStyle'] == null + ? const LineStyle(color: Colors.redAccent) + : LineStyle.fromJson(json['baseLineStyle'] as Map), + spanALineStyle: json['spanALineStyle'] == null + ? const LineStyle(color: Colors.green) + : LineStyle.fromJson(json['spanALineStyle'] as Map), + spanBLineStyle: json['spanBLineStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson(json['spanBLineStyle'] as Map), + laggingLineStyle: json['laggingLineStyle'] == null + ? const LineStyle(color: Colors.lime) + : LineStyle.fromJson( + json['laggingLineStyle'] as Map), + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$IchimokuCloudIndicatorConfigToJson( IchimokuCloudIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, 'conversionLinePeriod': instance.conversionLinePeriod, 'baseLinePeriod': instance.baseLinePeriod, 'spanBPeriod': instance.spanBPeriod, 'laggingSpanOffset': instance.laggingSpanOffset, + 'conversionLineStyle': instance.conversionLineStyle, + 'baseLineStyle': instance.baseLineStyle, + 'spanALineStyle': instance.spanALineStyle, + 'spanBLineStyle': instance.spanBLineStyle, + 'laggingLineStyle': instance.laggingLineStyle, }; diff --git a/lib/src/add_ons/indicators_ui/indicator_config.dart b/lib/src/add_ons/indicators_ui/indicator_config.dart index 450f41234..ecf5f2b44 100644 --- a/lib/src/add_ons/indicators_ui/indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/indicator_config.dart @@ -33,7 +33,12 @@ import 'zigzag_indicator/zigzag_indicator_config.dart'; @immutable abstract class IndicatorConfig extends AddOnConfig { /// Initializes - const IndicatorConfig({bool isOverlay = true}) : super(isOverlay: isOverlay); + const IndicatorConfig({ + required this.title, + bool isOverlay = true, + this.showLastIndicator = false, + this.pipSize = 4, + }) : super(isOverlay: isOverlay); /// Creates a concrete indicator config from JSON. factory IndicatorConfig.fromJson(Map json) { @@ -92,6 +97,15 @@ abstract class IndicatorConfig extends AddOnConfig { } } + /// The title of the indicator. + final String title; + + /// Whether to show last indicator or not. + final bool showLastIndicator; + + /// Number of digits after decimal point in price. + final int pipSize; + /// Key of indicator name property in JSON. static const String nameKey = 'name'; diff --git a/lib/src/add_ons/indicators_ui/indicator_item.dart b/lib/src/add_ons/indicators_ui/indicator_item.dart index f6cfa8667..14acf486b 100644 --- a/lib/src/add_ons/indicators_ui/indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/indicator_item.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; import 'callbacks.dart'; import 'indicator_config.dart'; @@ -42,13 +42,13 @@ abstract class IndicatorItemState extends State { /// Indicators repository @protected - late AddOnsRepository indicatorsRepo; + late Repository indicatorsRepo; @override void didChangeDependencies() { super.didChangeDependencies(); - indicatorsRepo = Provider.of>(context); + indicatorsRepo = Provider.of>(context); } @override diff --git a/lib/src/add_ons/indicators_ui/indicators_dialog.dart b/lib/src/add_ons/indicators_ui/indicators_dialog.dart index f364857cf..d61464d67 100644 --- a/lib/src/add_ons/indicators_ui/indicators_dialog.dart +++ b/lib/src/add_ons/indicators_ui/indicators_dialog.dart @@ -6,7 +6,7 @@ import 'package:deriv_chart/src/add_ons/indicators_ui/dpo_indicator/dpo_indicato import 'package:deriv_chart/src/add_ons/indicators_ui/gator/gator_indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/awesome_oscillator/awesome_oscillator_indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/smi/smi_indicator_config.dart'; -import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:deriv_chart/src/widgets/animated_popup.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -38,8 +38,8 @@ class _IndicatorsDialogState extends State { @override Widget build(BuildContext context) { - final AddOnsRepository repo = - context.watch>(); + final Repository repo = + context.watch>(); return AnimatedPopupDialog( child: Column( @@ -50,92 +50,92 @@ class _IndicatorsDialogState extends State { DropdownButton( value: _selectedIndicator, hint: const Text('Select indicator'), - items: >[ - const DropdownMenuItem( + items: const >[ + DropdownMenuItem( child: Text('Moving average'), value: MAIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Moving average envelope'), value: MAEnvIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Bollinger bands'), value: BollingerBandsIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Donchian channel'), value: DonchianChannelIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Alligator'), value: AlligatorIndicatorConfig(), ), DropdownMenuItem( - child: const Text('Rainbow'), + child: Text('Rainbow'), value: RainbowIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('ZigZag'), value: ZigZagIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Ichimoku Clouds'), value: IchimokuCloudIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Parabolic SAR'), value: ParabolicSARConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('RSI'), value: RSIIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Commodity Channel Index'), value: CCIIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('FCB'), value: FractalChaosBandIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('StochasticOscillator'), value: StochasticOscillatorIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('ADX'), value: ADXIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('DPO'), value: DPOIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Stochastic Momentum Index'), value: SMIIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Williams %R'), value: WilliamsRIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('AwesomeOscillator'), value: AwesomeOscillatorIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('MACD'), value: MACDIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Aroon'), value: AroonIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Price Rate Of Changes'), value: ROCIndicatorConfig(), ), - const DropdownMenuItem( + DropdownMenuItem( child: Text('Gator Oscillator'), value: GatorIndicatorConfig(), ) @@ -162,9 +162,9 @@ class _IndicatorsDialogState extends State { Expanded( child: ListView.builder( shrinkWrap: true, - itemCount: repo.addOns.length, + itemCount: repo.items.length, itemBuilder: (BuildContext context, int index) => - repo.addOns[index].getItem( + repo.items[index].getItem( (IndicatorConfig updatedConfig) => repo.updateAt(index, updatedConfig), () { diff --git a/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.dart b/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.dart index 27e075439..6a8c303e8 100644 --- a/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.dart @@ -3,7 +3,9 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ma_env_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/color_converter.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -17,6 +19,7 @@ part 'ma_env_indicator_config.g.dart'; /// Moving Average Envelope Indicator Config @JsonSerializable() +@ColorConverter() class MAEnvIndicatorConfig extends MAIndicatorConfig { /// Initializes const MAEnvIndicatorConfig({ @@ -25,10 +28,17 @@ class MAEnvIndicatorConfig extends MAIndicatorConfig { String fieldType = 'close', this.shift = 5, this.shiftType = ShiftType.percent, + this.upperLineStyle = const LineStyle(color: Colors.green), + this.middleLineStyle = const LineStyle(color: Colors.blue), + this.lowerLineStyle = const LineStyle(color: Colors.red), + this.fillColor = Colors.white12, + this.showChannelFill = true, + bool showLastIndicator = false, }) : super( period: period, movingAverageType: movingAverageType, fieldType: fieldType, + showLastIndicator: showLastIndicator, ); /// Initializes from JSON. @@ -48,6 +58,21 @@ class MAEnvIndicatorConfig extends MAIndicatorConfig { /// Moving Average Envelope shift final double shift; + /// Upper line style. + final LineStyle upperLineStyle; + + /// Middle line style. + final LineStyle middleLineStyle; + + /// Lower line style. + final LineStyle lowerLineStyle; + + /// Fill color. + final Color fillColor; + + /// Whether the area between upper and lower channel is filled. + final bool showChannelFill; + @override Series getSeries(IndicatorInput indicatorInput) => MAEnvSeries.fromIndicator( IndicatorConfig.supportedFieldTypes[fieldType]!(indicatorInput), @@ -56,6 +81,12 @@ class MAEnvIndicatorConfig extends MAIndicatorConfig { movingAverageType: movingAverageType, shift: shift, shiftType: shiftType, + upperLineStyle: upperLineStyle, + middleLineStyle: middleLineStyle, + lowerLineStyle: lowerLineStyle, + fillColor: fillColor, + showChannelFill: showChannelFill, + showLastIndicator: showLastIndicator, )); @override diff --git a/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.g.dart index 827686515..a44bfd28e 100644 --- a/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/ma_env_indicator/ma_env_indicator_config.g.dart @@ -6,54 +6,50 @@ part of 'ma_env_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -MAEnvIndicatorConfig _$MAEnvIndicatorConfigFromJson(Map json) { - return MAEnvIndicatorConfig( - period: json['period'] as int, - movingAverageType: - _$enumDecode(_$MovingAverageTypeEnumMap, json['movingAverageType']), - fieldType: json['fieldType'] as String, - shift: (json['shift'] as num).toDouble(), - shiftType: _$enumDecode(_$ShiftTypeEnumMap, json['shiftType']), - ); -} +MAEnvIndicatorConfig _$MAEnvIndicatorConfigFromJson( + Map json) => + MAEnvIndicatorConfig( + period: json['period'] as int? ?? 50, + movingAverageType: $enumDecodeNullable( + _$MovingAverageTypeEnumMap, json['movingAverageType']) ?? + MovingAverageType.simple, + fieldType: json['fieldType'] as String? ?? 'close', + shift: (json['shift'] as num?)?.toDouble() ?? 5, + shiftType: $enumDecodeNullable(_$ShiftTypeEnumMap, json['shiftType']) ?? + ShiftType.percent, + upperLineStyle: json['upperLineStyle'] == null + ? const LineStyle(color: Colors.green) + : LineStyle.fromJson(json['upperLineStyle'] as Map), + middleLineStyle: json['middleLineStyle'] == null + ? const LineStyle(color: Colors.blue) + : LineStyle.fromJson(json['middleLineStyle'] as Map), + lowerLineStyle: json['lowerLineStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson(json['lowerLineStyle'] as Map), + fillColor: json['fillColor'] == null + ? Colors.white12 + : const ColorConverter().fromJson(json['fillColor'] as int), + showChannelFill: json['showChannelFill'] as bool? ?? true, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + ); Map _$MAEnvIndicatorConfigToJson( MAEnvIndicatorConfig instance) => { + 'showLastIndicator': instance.showLastIndicator, 'period': instance.period, 'movingAverageType': - _$MovingAverageTypeEnumMap[instance.movingAverageType], + _$MovingAverageTypeEnumMap[instance.movingAverageType]!, 'fieldType': instance.fieldType, - 'shiftType': _$ShiftTypeEnumMap[instance.shiftType], + 'shiftType': _$ShiftTypeEnumMap[instance.shiftType]!, 'shift': instance.shift, + 'upperLineStyle': instance.upperLineStyle, + 'middleLineStyle': instance.middleLineStyle, + 'lowerLineStyle': instance.lowerLineStyle, + 'fillColor': const ColorConverter().toJson(instance.fillColor), + 'showChannelFill': instance.showChannelFill, }; -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', - ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} - const _$MovingAverageTypeEnumMap = { MovingAverageType.simple: 'simple', MovingAverageType.exponential: 'exponential', diff --git a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.dart b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.dart index aeae5fc4c..ab27d8ae3 100644 --- a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.dart @@ -24,13 +24,21 @@ class MAIndicatorConfig extends IndicatorConfig { LineStyle? lineStyle, int? offset, bool isOverlay = true, + int pipSize = 4, + bool showLastIndicator = false, + String? title, }) : period = period ?? 50, movingAverageType = movingAverageType ?? MovingAverageType.simple, fieldType = fieldType ?? 'close', offset = offset ?? 0, lineStyle = lineStyle ?? const LineStyle(color: Colors.yellow, thickness: 0.6), - super(isOverlay: isOverlay); + super( + isOverlay: isOverlay, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? MAIndicatorConfig.name, + ); /// Initializes from JSON. factory MAIndicatorConfig.fromJson(Map json) => @@ -61,7 +69,11 @@ class MAIndicatorConfig extends IndicatorConfig { @override Series getSeries(IndicatorInput indicatorInput) => MASeries.fromIndicator( IndicatorConfig.supportedFieldTypes[fieldType]!(indicatorInput), - MAOptions(period: period, type: movingAverageType), + MAOptions( + period: period, + type: movingAverageType, + showLastIndicator: showLastIndicator, + ), offset: offset, style: lineStyle, ); diff --git a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.g.dart index 2c28c1bd3..f7d015d44 100644 --- a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.g.dart @@ -6,68 +6,36 @@ part of 'ma_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -MAIndicatorConfig _$MAIndicatorConfigFromJson(Map json) { - return MAIndicatorConfig( - period: json['period'] as int?, - movingAverageType: _$enumDecodeNullable( - _$MovingAverageTypeEnumMap, json['movingAverageType']), - fieldType: json['fieldType'] as String?, - lineStyle: json['lineStyle'] == null - ? null - : LineStyle.fromJson(json['lineStyle'] as Map), - offset: json['offset'] as int?, - isOverlay: json['isOverlay'] as bool, - ); -} +MAIndicatorConfig _$MAIndicatorConfigFromJson(Map json) => + MAIndicatorConfig( + period: json['period'] as int?, + movingAverageType: $enumDecodeNullable( + _$MovingAverageTypeEnumMap, json['movingAverageType']), + fieldType: json['fieldType'] as String?, + lineStyle: json['lineStyle'] == null + ? null + : LineStyle.fromJson(json['lineStyle'] as Map), + offset: json['offset'] as int?, + isOverlay: json['isOverlay'] as bool? ?? true, + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$MAIndicatorConfigToJson(MAIndicatorConfig instance) => { 'isOverlay': instance.isOverlay, + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'movingAverageType': - _$MovingAverageTypeEnumMap[instance.movingAverageType], + _$MovingAverageTypeEnumMap[instance.movingAverageType]!, 'fieldType': instance.fieldType, 'lineStyle': instance.lineStyle, 'offset': instance.offset, }; -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', - ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} - -K? _$enumDecodeNullable( - Map enumValues, - dynamic source, { - K? unknownValue, -}) { - if (source == null) { - return null; - } - return _$enumDecode(enumValues, source, unknownValue: unknownValue); -} - const _$MovingAverageTypeEnumMap = { MovingAverageType.simple: 'simple', MovingAverageType.exponential: 'exponential', diff --git a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart index 9ac210b12..3a01521b5 100644 --- a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart @@ -1,5 +1,7 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.dart b/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.dart index 2b810bfee..322fa2c22 100644 --- a/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.dart @@ -2,6 +2,8 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/macd_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/bar_style.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -20,20 +22,38 @@ class MACDIndicatorConfig extends IndicatorConfig { this.fastMAPeriod = 12, this.slowMAPeriod = 26, this.signalPeriod = 9, - }) : super(isOverlay: false); + this.barStyle = const BarStyle(), + this.lineStyle = const LineStyle(color: Colors.white), + this.signalLineStyle = const LineStyle(color: Colors.redAccent), + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? MACDIndicatorConfig.name, + ); /// Initializes from JSON. factory MACDIndicatorConfig.fromJson(Map json) => _$MACDIndicatorConfigFromJson(json); @override - Series getSeries(IndicatorInput indicatorInput) => MACDSeries(indicatorInput, - config: this, - options: MACDOptions( - fastMAPeriod: fastMAPeriod, - slowMAPeriod: slowMAPeriod, - signalPeriod: signalPeriod, - )); + Series getSeries(IndicatorInput indicatorInput) => MACDSeries( + indicatorInput, + config: this, + options: MACDOptions( + fastMAPeriod: fastMAPeriod, + slowMAPeriod: slowMAPeriod, + signalPeriod: signalPeriod, + barStyle: barStyle, + lineStyle: lineStyle, + signalLineStyle: signalLineStyle, + showLastIndicator: showLastIndicator, + pipSize: pipSize, + ), + ); /// Unique name for this indicator. static const String name = 'macd'; @@ -51,6 +71,15 @@ class MACDIndicatorConfig extends IndicatorConfig { /// The period to calculate the Signal value. final int signalPeriod; + /// Histogram bar style + final BarStyle barStyle; + + /// Line style. + final LineStyle lineStyle; + + /// Signal line style. + final LineStyle signalLineStyle; + @override IndicatorItem getItem( UpdateIndicator updateIndicator, diff --git a/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.g.dart index 751ad3834..0c625e2ad 100644 --- a/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.g.dart @@ -6,18 +6,35 @@ part of 'macd_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -MACDIndicatorConfig _$MACDIndicatorConfigFromJson(Map json) { - return MACDIndicatorConfig( - fastMAPeriod: json['fastMAPeriod'] as int, - slowMAPeriod: json['slowMAPeriod'] as int, - signalPeriod: json['signalPeriod'] as int, - ); -} +MACDIndicatorConfig _$MACDIndicatorConfigFromJson(Map json) => + MACDIndicatorConfig( + fastMAPeriod: json['fastMAPeriod'] as int? ?? 12, + slowMAPeriod: json['slowMAPeriod'] as int? ?? 26, + signalPeriod: json['signalPeriod'] as int? ?? 9, + barStyle: json['barStyle'] == null + ? const BarStyle() + : BarStyle.fromJson(json['barStyle'] as Map), + lineStyle: json['lineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + signalLineStyle: json['signalLineStyle'] == null + ? const LineStyle(color: Colors.redAccent) + : LineStyle.fromJson(json['signalLineStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$MACDIndicatorConfigToJson( MACDIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'fastMAPeriod': instance.fastMAPeriod, 'slowMAPeriod': instance.slowMAPeriod, 'signalPeriod': instance.signalPeriod, + 'barStyle': instance.barStyle, + 'lineStyle': instance.lineStyle, + 'signalLineStyle': instance.signalLineStyle, }; diff --git a/lib/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.g.dart b/lib/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.g.dart index 08465b133..a48e2f784 100644 --- a/lib/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.g.dart +++ b/lib/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.g.dart @@ -7,16 +7,17 @@ part of 'oscillator_lines_config.dart'; // ************************************************************************** OscillatorLinesConfig _$OscillatorLinesConfigFromJson( - Map json) { - return OscillatorLinesConfig( - overboughtValue: (json['overboughtValue'] as num).toDouble(), - oversoldValue: (json['oversoldValue'] as num).toDouble(), - overboughtStyle: - LineStyle.fromJson(json['overboughtStyle'] as Map), - oversoldStyle: - LineStyle.fromJson(json['oversoldStyle'] as Map), - ); -} + Map json) => + OscillatorLinesConfig( + overboughtValue: (json['overboughtValue'] as num).toDouble(), + oversoldValue: (json['oversoldValue'] as num).toDouble(), + overboughtStyle: json['overboughtStyle'] == null + ? const LineStyle(thickness: 0.5) + : LineStyle.fromJson(json['overboughtStyle'] as Map), + oversoldStyle: json['oversoldStyle'] == null + ? const LineStyle(thickness: 0.5) + : LineStyle.fromJson(json['oversoldStyle'] as Map), + ); Map _$OscillatorLinesConfigToJson( OscillatorLinesConfig instance) => diff --git a/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.dart b/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.dart index acc7f87b8..9d1f48c3e 100644 --- a/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.dart @@ -21,7 +21,8 @@ class ParabolicSARConfig extends IndicatorConfig { this.minAccelerationFactor = 0.02, this.maxAccelerationFactor = 0.2, this.scatterStyle = const ScatterStyle(), - }) : super(); + String? title, + }) : super(title: title ?? ParabolicSARConfig.name); /// Initializes from JSON. factory ParabolicSARConfig.fromJson(Map json) => diff --git a/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.g.dart index 6773a01b0..62cde9dbd 100644 --- a/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/parabolic_sar/parabolic_sar_indicator_config.g.dart @@ -6,17 +6,21 @@ part of 'parabolic_sar_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -ParabolicSARConfig _$ParabolicSARConfigFromJson(Map json) { - return ParabolicSARConfig( - minAccelerationFactor: (json['minAccelerationFactor'] as num).toDouble(), - maxAccelerationFactor: (json['maxAccelerationFactor'] as num).toDouble(), - scatterStyle: - ScatterStyle.fromJson(json['scatterStyle'] as Map), - ); -} +ParabolicSARConfig _$ParabolicSARConfigFromJson(Map json) => + ParabolicSARConfig( + minAccelerationFactor: + (json['minAccelerationFactor'] as num?)?.toDouble() ?? 0.02, + maxAccelerationFactor: + (json['maxAccelerationFactor'] as num?)?.toDouble() ?? 0.2, + scatterStyle: json['scatterStyle'] == null + ? const ScatterStyle() + : ScatterStyle.fromJson(json['scatterStyle'] as Map), + title: json['title'] as String?, + ); Map _$ParabolicSARConfigToJson(ParabolicSARConfig instance) => { + 'title': instance.title, 'minAccelerationFactor': instance.minAccelerationFactor, 'maxAccelerationFactor': instance.maxAccelerationFactor, 'scatterStyle': instance.scatterStyle, diff --git a/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.dart b/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.dart index 12e218aa6..3a7ca59d9 100644 --- a/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.dart @@ -3,7 +3,9 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rainbow_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/color_converter.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -16,18 +18,26 @@ part 'rainbow_indicator_config.g.dart'; /// Rainbow Indicator Config @JsonSerializable() +@ColorConverter() class RainbowIndicatorConfig extends MAIndicatorConfig { /// Initializes - RainbowIndicatorConfig({ + const RainbowIndicatorConfig({ int period = 50, MovingAverageType movingAverageType = MovingAverageType.simple, String fieldType = 'close', this.bandsCount = 10, - }) : rainbowColors = _getRainbowColors(bandsCount), + this.rainbowLineStyles, + bool showLastIndicator = false, + }) : assert( + rainbowLineStyles == null || rainbowLineStyles.length == bandsCount, + '''If you provide [rainbowLineStyles] it should have the same length as [bandsCount]. + since the bands count is $bandsCount. the same number of lineStyles should be provided.''', + ), super( period: period, movingAverageType: movingAverageType, fieldType: fieldType, + showLastIndicator: showLastIndicator, ); /// Initializes from JSON. @@ -57,18 +67,22 @@ class RainbowIndicatorConfig extends MAIndicatorConfig { /// Rainbow Moving Averages bands count final int bandsCount; - /// List of colors for the different bands in the [RainbowSeries]. - final List rainbowColors; + /// List of line styles for the different bands in the [RainbowSeries]. + final List? rainbowLineStyles; @override Series getSeries(IndicatorInput indicatorInput) => RainbowSeries.fromIndicator( IndicatorConfig.supportedFieldTypes[fieldType]!(indicatorInput), - rainbowColors: rainbowColors, + rainbowLineStyles: rainbowLineStyles ?? + _getRainbowColors(bandsCount) + .map((Color color) => LineStyle(color: color)) + .toList(), rainbowOptions: RainbowOptions( period: period, movingAverageType: movingAverageType, bandsCount: bandsCount, + showLastIndicator: showLastIndicator, ), ); diff --git a/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.g.dart index 778045ea1..0a4d53b69 100644 --- a/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_config.g.dart @@ -7,52 +7,32 @@ part of 'rainbow_indicator_config.dart'; // ************************************************************************** RainbowIndicatorConfig _$RainbowIndicatorConfigFromJson( - Map json) { - return RainbowIndicatorConfig( - period: json['period'] as int, - movingAverageType: - _$enumDecode(_$MovingAverageTypeEnumMap, json['movingAverageType']), - fieldType: json['fieldType'] as String, - bandsCount: json['bandsCount'] as int, - ); -} + Map json) => + RainbowIndicatorConfig( + period: json['period'] as int? ?? 50, + movingAverageType: $enumDecodeNullable( + _$MovingAverageTypeEnumMap, json['movingAverageType']) ?? + MovingAverageType.simple, + fieldType: json['fieldType'] as String? ?? 'close', + bandsCount: json['bandsCount'] as int? ?? 10, + rainbowLineStyles: (json['rainbowLineStyles'] as List?) + ?.map((e) => LineStyle.fromJson(e as Map)) + .toList(), + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + ); Map _$RainbowIndicatorConfigToJson( RainbowIndicatorConfig instance) => { + 'showLastIndicator': instance.showLastIndicator, 'period': instance.period, 'movingAverageType': - _$MovingAverageTypeEnumMap[instance.movingAverageType], + _$MovingAverageTypeEnumMap[instance.movingAverageType]!, 'fieldType': instance.fieldType, 'bandsCount': instance.bandsCount, + 'rainbowLineStyles': instance.rainbowLineStyles, }; -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', - ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} - const _$MovingAverageTypeEnumMap = { MovingAverageType.simple: 'simple', MovingAverageType.exponential: 'exponential', diff --git a/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.dart b/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.dart index 347824783..1a144c3b2 100644 --- a/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.dart @@ -6,6 +6,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/roc_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -18,7 +19,16 @@ class ROCIndicatorConfig extends IndicatorConfig { const ROCIndicatorConfig({ this.period = 14, this.fieldType = 'close', - }) : super(isOverlay: false); + this.lineStyle, + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? ROCIndicatorConfig.name, + ); /// Initializes from JSON. factory ROCIndicatorConfig.fromJson(Map json) => @@ -37,10 +47,18 @@ class ROCIndicatorConfig extends IndicatorConfig { /// Field type final String fieldType; + /// Line style. + final LineStyle? lineStyle; + @override Series getSeries(IndicatorInput indicatorInput) => ROCSeries.fromIndicator( IndicatorConfig.supportedFieldTypes[fieldType]!(indicatorInput), - rocOptions: ROCOptions(period: period), + rocOptions: ROCOptions( + period: period, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + ), + lineStyle: lineStyle, ); @override diff --git a/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.g.dart index 66d9d4aed..924c5dab7 100644 --- a/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/roc/roc_indicator_config.g.dart @@ -6,15 +6,24 @@ part of 'roc_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -ROCIndicatorConfig _$ROCIndicatorConfigFromJson(Map json) { - return ROCIndicatorConfig( - period: json['period'] as int, - fieldType: json['fieldType'] as String, - ); -} +ROCIndicatorConfig _$ROCIndicatorConfigFromJson(Map json) => + ROCIndicatorConfig( + period: json['period'] as int? ?? 14, + fieldType: json['fieldType'] as String? ?? 'close', + lineStyle: json['lineStyle'] == null + ? null + : LineStyle.fromJson(json['lineStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$ROCIndicatorConfigToJson(ROCIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'fieldType': instance.fieldType, + 'lineStyle': instance.lineStyle, }; diff --git a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart index 5874e2a82..a4c95f535 100644 --- a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart @@ -1,8 +1,9 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rsi_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/rsi_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -27,7 +28,15 @@ class RSIIndicatorConfig extends IndicatorConfig { this.lineStyle = const LineStyle(color: Colors.white), this.pinLabels = false, this.showZones = true, - }) : super(isOverlay: false); + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? RSIIndicatorConfig.name, + ); /// Initializes from JSON. factory RSIIndicatorConfig.fromJson(Map json) => @@ -63,7 +72,11 @@ class RSIIndicatorConfig extends IndicatorConfig { Series getSeries(IndicatorInput indicatorInput) => RSISeries.fromIndicator( IndicatorConfig.supportedFieldTypes[fieldType]!(indicatorInput), this, - rsiOptions: RSIOptions(period: period), + rsiOptions: RSIOptions( + period: period, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + ), showZones: showZones, ); diff --git a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.g.dart index 7dd0f24fd..63b00bfe0 100644 --- a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.g.dart @@ -6,20 +6,29 @@ part of 'rsi_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -RSIIndicatorConfig _$RSIIndicatorConfigFromJson(Map json) { - return RSIIndicatorConfig( - period: json['period'] as int, - fieldType: json['fieldType'] as String, - oscillatorLinesConfig: OscillatorLinesConfig.fromJson( - json['oscillatorLinesConfig'] as Map), - lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), - pinLabels: json['pinLabels'] as bool, - showZones: json['showZones'] as bool, - ); -} +RSIIndicatorConfig _$RSIIndicatorConfigFromJson(Map json) => + RSIIndicatorConfig( + period: json['period'] as int? ?? 14, + fieldType: json['fieldType'] as String? ?? 'close', + oscillatorLinesConfig: json['oscillatorLinesConfig'] == null + ? const OscillatorLinesConfig(overboughtValue: 80, oversoldValue: 20) + : OscillatorLinesConfig.fromJson( + json['oscillatorLinesConfig'] as Map), + lineStyle: json['lineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + pinLabels: json['pinLabels'] as bool? ?? false, + showZones: json['showZones'] as bool? ?? true, + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$RSIIndicatorConfigToJson(RSIIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'lineStyle': instance.lineStyle, 'fieldType': instance.fieldType, diff --git a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart index d690ddf70..aae4ab987 100644 --- a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart @@ -1,6 +1,7 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/oscillator_limit.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.dart b/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.dart index 12615b525..577454765 100644 --- a/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.dart @@ -4,6 +4,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/smi_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -27,7 +28,17 @@ class SMIIndicatorConfig extends IndicatorConfig { this.signalPeriod = 10, this.maType = MovingAverageType.exponential, this.showZones = true, - }) : super(isOverlay: false); + this.lineStyle, + this.signalLineStyle, + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? SMIIndicatorConfig.name, + ); /// Initializes from JSON. factory SMIIndicatorConfig.fromJson(Map json) => @@ -64,6 +75,12 @@ class SMIIndicatorConfig extends IndicatorConfig { /// Whether to show zones (intersection between indicator and overbought/sold). final bool showZones; + /// Line style. + final LineStyle? lineStyle; + + /// Signal line style. + final LineStyle? signalLineStyle; + @override Series getSeries(IndicatorInput indicatorInput) => SMISeries( indicatorInput, @@ -72,6 +89,10 @@ class SMIIndicatorConfig extends IndicatorConfig { smoothingPeriod: smoothingPeriod, doubleSmoothingPeriod: doubleSmoothingPeriod, signalOptions: MAOptions(period: signalPeriod, type: maType), + lineStyle: lineStyle, + signalLineStyle: signalLineStyle, + showLastIndicator: showLastIndicator, + pipSize: pipSize, ), overboughtValue: overboughtValue, oversoldValue: oversoldValue, diff --git a/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.g.dart index d53c18909..30b34f195 100644 --- a/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/smi/smi_indicator_config.g.dart @@ -6,57 +6,45 @@ part of 'smi_indicator_config.dart'; // JsonSerializableGenerator // ************************************************************************** -SMIIndicatorConfig _$SMIIndicatorConfigFromJson(Map json) { - return SMIIndicatorConfig( - period: json['period'] as int, - smoothingPeriod: json['smoothingPeriod'] as int, - doubleSmoothingPeriod: json['doubleSmoothingPeriod'] as int, - overboughtValue: (json['overboughtValue'] as num).toDouble(), - oversoldValue: (json['oversoldValue'] as num).toDouble(), - signalPeriod: json['signalPeriod'] as int, - maType: _$enumDecode(_$MovingAverageTypeEnumMap, json['maType']), - showZones: json['showZones'] as bool, - ); -} +SMIIndicatorConfig _$SMIIndicatorConfigFromJson(Map json) => + SMIIndicatorConfig( + period: json['period'] as int? ?? 10, + smoothingPeriod: json['smoothingPeriod'] as int? ?? 3, + doubleSmoothingPeriod: json['doubleSmoothingPeriod'] as int? ?? 3, + overboughtValue: (json['overboughtValue'] as num?)?.toDouble() ?? 40, + oversoldValue: (json['oversoldValue'] as num?)?.toDouble() ?? -40, + signalPeriod: json['signalPeriod'] as int? ?? 10, + maType: $enumDecodeNullable(_$MovingAverageTypeEnumMap, json['maType']) ?? + MovingAverageType.exponential, + showZones: json['showZones'] as bool? ?? true, + lineStyle: json['lineStyle'] == null + ? null + : LineStyle.fromJson(json['lineStyle'] as Map), + signalLineStyle: json['signalLineStyle'] == null + ? null + : LineStyle.fromJson(json['signalLineStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$SMIIndicatorConfigToJson(SMIIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'smoothingPeriod': instance.smoothingPeriod, 'doubleSmoothingPeriod': instance.doubleSmoothingPeriod, 'signalPeriod': instance.signalPeriod, - 'maType': _$MovingAverageTypeEnumMap[instance.maType], + 'maType': _$MovingAverageTypeEnumMap[instance.maType]!, 'overboughtValue': instance.overboughtValue, 'oversoldValue': instance.oversoldValue, 'showZones': instance.showZones, + 'lineStyle': instance.lineStyle, + 'signalLineStyle': instance.signalLineStyle, }; -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', - ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} - const _$MovingAverageTypeEnumMap = { MovingAverageType.simple: 'simple', MovingAverageType.exponential: 'exponential', diff --git a/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart b/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart index 370023eec..6e86eb795 100644 --- a/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart @@ -1,9 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/dropdown_menu.dart' as deriv_dropdown; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/field_widget.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide DropdownMenu; import '../callbacks.dart'; import '../indicator_config.dart'; diff --git a/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.dart b/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.dart index 18bab078d..e13a60df8 100644 --- a/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.dart @@ -1,4 +1,3 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_item.dart'; @@ -6,8 +5,10 @@ import 'package:deriv_chart/src/add_ons/indicators_ui/oscillator_lines/oscillato import 'package:deriv_chart/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_item.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/stochastic_oscillator_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/stochastic_oscillator_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -29,7 +30,17 @@ class StochasticOscillatorIndicatorConfig extends IndicatorConfig { overboughtValue: 80, oversoldValue: 20, ), - }) : super(isOverlay: false); + this.fastLineStyle = const LineStyle(color: Colors.white), + this.slowLineStyle = const LineStyle(color: Colors.red), + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? StochasticOscillatorIndicatorConfig.name, + ); /// Initializes from JSON. factory StochasticOscillatorIndicatorConfig.fromJson( @@ -70,6 +81,12 @@ class StochasticOscillatorIndicatorConfig extends IndicatorConfig { /// default is true final bool showZones; + /// Fast line style. + final LineStyle fastLineStyle; + + /// Slow line style. + final LineStyle slowLineStyle; + @override Series getSeries(IndicatorInput indicatorInput) => StochasticOscillatorSeries( IndicatorConfig.supportedFieldTypes[fieldType]!(indicatorInput), this, diff --git a/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.g.dart index 66b3e96fa..7e28d60d4 100644 --- a/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.g.dart @@ -7,23 +7,41 @@ part of 'stochastic_oscillator_indicator_config.dart'; // ************************************************************************** StochasticOscillatorIndicatorConfig - _$StochasticOscillatorIndicatorConfigFromJson(Map json) { - return StochasticOscillatorIndicatorConfig( - period: json['period'] as int, - fieldType: json['fieldType'] as String, - overBoughtPrice: (json['overBoughtPrice'] as num).toDouble(), - overSoldPrice: (json['overSoldPrice'] as num).toDouble(), - showZones: json['showZones'] as bool, - isSmooth: json['isSmooth'] as bool, - lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), - oscillatorLinesConfig: OscillatorLinesConfig.fromJson( - json['oscillatorLinesConfig'] as Map), - ); -} + _$StochasticOscillatorIndicatorConfigFromJson(Map json) => + StochasticOscillatorIndicatorConfig( + period: json['period'] as int? ?? 14, + fieldType: json['fieldType'] as String? ?? 'close', + overBoughtPrice: (json['overBoughtPrice'] as num?)?.toDouble() ?? 80, + overSoldPrice: (json['overSoldPrice'] as num?)?.toDouble() ?? 20, + showZones: json['showZones'] as bool? ?? false, + isSmooth: json['isSmooth'] as bool? ?? true, + lineStyle: json['lineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + oscillatorLinesConfig: json['oscillatorLinesConfig'] == null + ? const OscillatorLinesConfig( + overboughtValue: 80, oversoldValue: 20) + : OscillatorLinesConfig.fromJson( + json['oscillatorLinesConfig'] as Map), + fastLineStyle: json['fastLineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson( + json['fastLineStyle'] as Map), + slowLineStyle: json['slowLineStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson( + json['slowLineStyle'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$StochasticOscillatorIndicatorConfigToJson( StochasticOscillatorIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'overBoughtPrice': instance.overBoughtPrice, 'overSoldPrice': instance.overSoldPrice, @@ -32,4 +50,6 @@ Map _$StochasticOscillatorIndicatorConfigToJson( 'fieldType': instance.fieldType, 'isSmooth': instance.isSmooth, 'showZones': instance.showZones, + 'fastLineStyle': instance.fastLineStyle, + 'slowLineStyle': instance.slowLineStyle, }; diff --git a/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.dart b/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.dart index 5dda2f3c0..14d67512e 100644 --- a/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.dart @@ -27,7 +27,15 @@ class WilliamsRIndicatorConfig extends IndicatorConfig { oversoldValue: -80, overboughtValue: -20, ), - }) : super(isOverlay: false); + int pipSize = 4, + bool showLastIndicator = false, + String? title, + }) : super( + isOverlay: false, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + title: title ?? WilliamsRIndicatorConfig.name, + ); /// Initializes from JSON. factory WilliamsRIndicatorConfig.fromJson(Map json) => @@ -58,10 +66,17 @@ class WilliamsRIndicatorConfig extends IndicatorConfig { @override Series getSeries(IndicatorInput indicatorInput) => WilliamsRSeries( indicatorInput, - WilliamsROptions(period), + WilliamsROptions( + period, + pipSize: pipSize, + showLastIndicator: showLastIndicator, + ), overboughtValue: oscillatorLimits.overboughtValue, oversoldValue: oscillatorLimits.oversoldValue, + overboughtLineStyle: oscillatorLimits.overboughtStyle, + oversoldLineStyle: oscillatorLimits.oversoldStyle, showZones: showZones, + lineStyle: lineStyle, ); @override diff --git a/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.g.dart index 8fbc73632..aa99d1bdd 100644 --- a/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.g.dart @@ -7,21 +7,33 @@ part of 'williams_r_indicator_config.dart'; // ************************************************************************** WilliamsRIndicatorConfig _$WilliamsRIndicatorConfigFromJson( - Map json) { - return WilliamsRIndicatorConfig( - period: json['period'] as int, - lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), - zeroHorizontalLinesStyle: LineStyle.fromJson( - json['zeroHorizontalLinesStyle'] as Map), - showZones: json['showZones'] as bool, - oscillatorLimits: OscillatorLinesConfig.fromJson( - json['oscillatorLimits'] as Map), - ); -} + Map json) => + WilliamsRIndicatorConfig( + period: json['period'] as int? ?? 14, + lineStyle: json['lineStyle'] == null + ? const LineStyle(color: Colors.white) + : LineStyle.fromJson(json['lineStyle'] as Map), + zeroHorizontalLinesStyle: json['zeroHorizontalLinesStyle'] == null + ? const LineStyle(color: Colors.red) + : LineStyle.fromJson( + json['zeroHorizontalLinesStyle'] as Map), + showZones: json['showZones'] as bool? ?? true, + oscillatorLimits: json['oscillatorLimits'] == null + ? const OscillatorLinesConfig( + oversoldValue: -80, overboughtValue: -20) + : OscillatorLinesConfig.fromJson( + json['oscillatorLimits'] as Map), + pipSize: json['pipSize'] as int? ?? 4, + showLastIndicator: json['showLastIndicator'] as bool? ?? false, + title: json['title'] as String?, + ); Map _$WilliamsRIndicatorConfigToJson( WilliamsRIndicatorConfig instance) => { + 'title': instance.title, + 'showLastIndicator': instance.showLastIndicator, + 'pipSize': instance.pipSize, 'period': instance.period, 'lineStyle': instance.lineStyle, 'zeroHorizontalLinesStyle': instance.zeroHorizontalLinesStyle, diff --git a/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart b/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart index 8b123fc99..1e77784fc 100644 --- a/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart @@ -1,6 +1,7 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/zigzag_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -18,7 +19,8 @@ class ZigZagIndicatorConfig extends IndicatorConfig { const ZigZagIndicatorConfig({ this.distance = 10, this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.blue), - }) : super(); + String? title, + }) : super(title: title ?? ZigZagIndicatorConfig.name); /// Initializes from JSON. factory ZigZagIndicatorConfig.fromJson(Map json) => diff --git a/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.g.dart b/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.g.dart index 21e81cd12..1ea550d6d 100644 --- a/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.g.dart +++ b/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.g.dart @@ -7,16 +7,19 @@ part of 'zigzag_indicator_config.dart'; // ************************************************************************** ZigZagIndicatorConfig _$ZigZagIndicatorConfigFromJson( - Map json) { - return ZigZagIndicatorConfig( - distance: (json['distance'] as num).toDouble(), - lineStyle: LineStyle.fromJson(json['lineStyle'] as Map), - ); -} + Map json) => + ZigZagIndicatorConfig( + distance: (json['distance'] as num?)?.toDouble() ?? 10, + lineStyle: json['lineStyle'] == null + ? const LineStyle(thickness: 0.9, color: Colors.blue) + : LineStyle.fromJson(json['lineStyle'] as Map), + title: json['title'] as String?, + ); Map _$ZigZagIndicatorConfigToJson( ZigZagIndicatorConfig instance) => { + 'title': instance.title, 'distance': instance.distance, 'lineStyle': instance.lineStyle, }; diff --git a/lib/src/add_ons/repository.dart b/lib/src/add_ons/repository.dart new file mode 100644 index 000000000..626031fc8 --- /dev/null +++ b/lib/src/add_ons/repository.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +/// Holds indicators/drawing tools that were added to the Chart during runtime. +abstract class Repository extends ChangeNotifier { + /// Retrieves the list of items in a repository. + List get items; + + /// To adds a new indicator or drawing tool. + void add(T config); + + /// To edit an indicator or drawing tool at [index]. + void editAt(int index); + + /// To update an indicator or drawing tool at [index]. + void updateAt(int index, T config); + + /// Removes indicator or drawing tool at [index]. + void removeAt(int index); + + /// Swaps two elements of a list. + void swap(int index1, int index2); +} diff --git a/lib/src/deriv_chart/chart/basic_chart.dart b/lib/src/deriv_chart/chart/basic_chart.dart index 85c21920f..60c81ad11 100644 --- a/lib/src/deriv_chart/chart/basic_chart.dart +++ b/lib/src/deriv_chart/chart/basic_chart.dart @@ -1,13 +1,16 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_data_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:deriv_chart/src/deriv_chart/chart/y_axis/y_grid_label_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/y_axis/y_grid_line_painter.dart'; +import 'package:deriv_chart/src/models/chart_axis_config.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../misc/callbacks.dart'; +import 'data_visualization/chart_series/series.dart'; import 'data_visualization/models/animation_info.dart'; import 'helpers/functions/conversion.dart'; import 'helpers/functions/helper_functions.dart'; @@ -23,6 +26,7 @@ class BasicChart extends StatefulWidget { this.opacity = 1, ChartAxisConfig? chartAxisConfig, Key? key, + this.onQuoteAreaChanged, }) : chartAxisConfig = chartAxisConfig ?? const ChartAxisConfig(), super(key: key); @@ -38,6 +42,9 @@ class BasicChart extends StatefulWidget { /// The axis configuration of the chart. final ChartAxisConfig chartAxisConfig; + /// Callback provided by library user. + final VisibleQuoteAreaChangedCallback? onQuoteAreaChanged; + @override BasicChartState createState() => BasicChartState(); } @@ -164,8 +171,22 @@ class BasicChartState extends State /// Call function to calculate the grid line quotes and put them inside /// [yAxisModel]. - List calculateGridLineQuotes(YAxisModel yAxisModel) => - gridLineQuotes = yAxisModel.gridQuotes(); + List calculateGridLineQuotes(YAxisModel yAxisModel) { + final List newGridLineQuotes = yAxisModel.gridQuotes(); + + if (newGridLineQuotes.isNotEmpty && + (gridLineQuotes == null || + gridLineQuotes!.isEmpty || + newGridLineQuotes.first != gridLineQuotes!.first || + newGridLineQuotes.last != gridLineQuotes!.last)) { + widget.onQuoteAreaChanged + ?.call(newGridLineQuotes.first, newGridLineQuotes.last); + } + + gridLineQuotes = newGridLineQuotes; + + return gridLineQuotes!; + } void _playNewTickAnimation() { _currentTickAnimationController @@ -211,6 +232,18 @@ class BasicChartState extends State vsync: this, duration: quoteBoundsAnimationDuration, ); + + /// Builds the widget once the animation is finished + /// so that the y-axis is correctly filled. + topBoundQuoteAnimationController.addListener(_quoteAnimationListener); + bottomBoundQuoteAnimationController.addListener(_quoteAnimationListener); + } + + void _quoteAnimationListener() { + if (topBoundQuoteAnimationController.isCompleted && + bottomBoundQuoteAnimationController.isCompleted) { + setState(() {}); + } } void _clearGestures() { @@ -267,8 +300,8 @@ class BasicChartState extends State ); /// Returns quote based on the y-coordinate. - double chartQuoteFromCanvasY(double quote) => quoteFromCanvasY( - y: quote, + double chartQuoteFromCanvasY(double y) => quoteFromCanvasY( + y: y, topBoundQuote: _topBoundQuote, bottomBoundQuote: _bottomBoundQuote, canvasHeight: canvasSize?.height ?? 200, @@ -287,10 +320,11 @@ class BasicChartState extends State constraints.maxHeight, ); - final YAxisModel yAxisModel = _setupYAxisModel(canvasSize!); - updateVisibleData(); _updateQuoteBoundTargets(); + + final YAxisModel yAxisModel = _setupYAxisModel(canvasSize!); + final List gridLineQuotes = calculateGridLineQuotes(yAxisModel); return Stack( @@ -415,6 +449,16 @@ class BasicChartState extends State verticalPaddingFraction = ((verticalPadding + dy) / canvasSize!.height).clamp(0.05, 0.49); }); + _onScaleYAxis(); + } + + void _onScaleYAxis() { + if (gridLineQuotes != null && gridLineQuotes!.isNotEmpty) { + widget.onQuoteAreaChanged?.call( + gridLineQuotes!.first, + gridLineQuotes!.last, + ); + } } void _setupInitialBounds() { diff --git a/lib/src/deriv_chart/chart/bottom_chart.dart b/lib/src/deriv_chart/chart/bottom_chart.dart index f56578339..a5c02c3a8 100644 --- a/lib/src/deriv_chart/chart/bottom_chart.dart +++ b/lib/src/deriv_chart/chart/bottom_chart.dart @@ -1,62 +1,216 @@ -import 'package:deriv_chart/deriv_chart.dart'; -import 'package:deriv_chart/src/theme/chart_default_theme.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/widgets/bottom_indicator_title.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/crosshair/crosshair_area_web.dart'; +import 'package:provider/provider.dart'; import 'basic_chart.dart'; -import 'y_axis/quote_grid.dart'; + +/// Called when the indicator is moved up/down +/// +/// [offset] is the displacement between the swap positions. +typedef SwapCallback = Function(int offset); /// The chart to add the bottom indicators too. class BottomChart extends BasicChart { /// Initializes a bottom chart. const BottomChart({ required Series series, + required this.granularity, + required this.title, int pipSize = 4, Key? key, + this.onRemove, + this.onEdit, + this.onExpandToggle, + this.onCrosshairDisappeared, + this.onCrosshairHover, + this.onSwap, + this.isExpanded = false, + this.showCrosshair = true, + this.showExpandedIcon = false, + this.showMoveUpIcon = false, + this.showMoveDownIcon = false, + this.bottomChartTitleMargin, }) : super(key: key, mainSeries: series, pipSize: pipSize); + /// For candles: Duration of one candle in ms. + /// For ticks: Average ms difference between two consecutive ticks. + final int granularity; + + /// Called when an indicator is to be removed. + final VoidCallback? onRemove; + + /// Called when an indicator is to be edited. + final VoidCallback? onEdit; + + /// Called when an indicator is to be expanded. + final VoidCallback? onExpandToggle; + + /// Called when an indicator is to moved up/down. + final SwapCallback? onSwap; + + /// Called when the crosshair is dismissed. + final VoidCallback? onCrosshairDisappeared; + + /// Called when the crosshair cursor is hovered/moved. + final OnCrosshairHover? onCrosshairHover; + + /// Whether the indicator is expanded or not. + final bool isExpanded; + + /// Whether the crosshair should be shown or not. + final bool showCrosshair; + + /// The title of the bottom chart. + final String title; + + /// Whether the expanded icon should be shown or not. + final bool showExpandedIcon; + + /// Whether the move up icon should be shown or not. + final bool showMoveUpIcon; + + /// Whether the move down icon should be shown or not. + final bool showMoveDownIcon; + + /// Specifies the margin to prevent overlap. + final EdgeInsets? bottomChartTitleMargin; + @override _BottomChartState createState() => _BottomChartState(); } class _BottomChartState extends BasicChartState { - @override - List calculateGridLineQuotes(YAxisModel yAxisModel) => - gridLineQuotes = const []; + ChartTheme get theme => context.read(); - @override - Widget build(BuildContext context) { - final ChartDefaultTheme theme = - Theme.of(context).brightness == Brightness.dark - ? ChartDefaultDarkTheme() - : ChartDefaultLightTheme(); - return ClipRect( - child: Stack( - children: [ - Column( - children: [ - Divider( - height: 0.5, - thickness: 1, - color: theme.brandGreenishColor, - ), - Expanded(child: super.build(context)), - ], + Widget _buildBottomChartOptions(BuildContext context) { + Widget _buildIcon({ + required IconData iconData, + void Function()? onPressed, + }) => + Material( + type: MaterialType.circle, + color: Colors.transparent, + clipBehavior: Clip.antiAlias, + child: IconButton( + icon: Icon( + iconData, + size: 16, + color: theme.base01Color, + ), + onPressed: onPressed, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), ), - Positioned( - top: 15, - left: 10, - child: Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: theme.base01Color.withOpacity(0.1), - borderRadius: BorderRadius.circular(2), + ); + + Widget _buildIcons() => Row( + children: [ + if (widget.showMoveUpIcon) + _buildIcon( + iconData: Icons.arrow_upward, + onPressed: () { + widget.onSwap?.call(-1); + }, ), - child: Text( - widget.mainSeries.runtimeType.toString(), + if (widget.showMoveDownIcon) + _buildIcon( + iconData: Icons.arrow_downward, + onPressed: () { + widget.onSwap?.call(1); + }, ), + if (widget.showExpandedIcon) + _buildIcon( + iconData: widget.isExpanded + ? Icons.fullscreen_exit + : Icons.fullscreen, + onPressed: () { + widget.onExpandToggle?.call(); + }, + ), + _buildIcon( + iconData: Icons.settings, + onPressed: () { + widget.onEdit?.call(); + }, ), - ), - ], + _buildIcon( + iconData: Icons.delete, + onPressed: () { + widget.onRemove?.call(); + }, + ), + ], + ); + + return Positioned( + top: 15, + left: widget.bottomChartTitleMargin?.left ?? 10, + child: Container( + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: theme.base07Color, + borderRadius: BorderRadius.circular(2), + ), + child: Row( + children: [ + BottomIndicatorTitle( + widget.title, + theme.textStyle( + color: theme.base01Color, + textStyle: + const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + ), + _buildIcons(), + ], + ), + ), + ); + } + + Widget _buildCrosshairAreaWeb() => CrosshairAreaWeb( + mainSeries: widget.mainSeries, + epochFromCanvasX: xAxis.epochFromX, + quoteFromCanvasY: chartQuoteFromCanvasY, + epochToCanvasX: xAxis.xFromEpoch, + quoteToCanvasY: chartQuoteToCanvasY, + quoteLabelsTouchAreaWidth: quoteLabelsTouchAreaWidth, + showCrosshairCursor: widget.showCrosshair, + onCrosshairDisappeared: widget.onCrosshairDisappeared, + onCrosshairHover: widget.onCrosshairHover, + ); + + @override + Widget build(BuildContext context) { + final ChartConfig chartConfig = ChartConfig( + pipSize: widget.pipSize, + granularity: widget.granularity, + ); + + return Provider.value( + value: chartConfig, + child: ClipRect( + child: Stack( + children: [ + Column( + children: [ + Divider( + height: 0.5, + thickness: 1, + color: theme.base01Color, + ), + Expanded(child: super.build(context)), + ], + ), + if (kIsWeb) _buildCrosshairAreaWeb(), + _buildBottomChartOptions(context) + ], + ), ), ); } diff --git a/lib/src/deriv_chart/chart/chart.dart b/lib/src/deriv_chart/chart/chart.dart index 760262024..fd5ce18dd 100644 --- a/lib/src/deriv_chart/chart/chart.dart +++ b/lib/src/deriv_chart/chart/chart.dart @@ -1,14 +1,29 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:collection/collection.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/misc/callbacks.dart'; +import 'package:deriv_chart/src/models/chart_axis_config.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/chart_default_light_theme.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; +import '../../add_ons/indicators_ui/indicator_config.dart'; +import '../../add_ons/repository.dart'; +import '../../misc/chart_controller.dart'; +import '../../models/tick.dart'; +import '../../theme/chart_default_dark_theme.dart'; +import '../../theme/chart_theme.dart'; import 'bottom_chart.dart'; +import 'data_visualization/annotations/chart_annotation.dart'; import 'data_visualization/chart_data.dart'; +import 'data_visualization/chart_series/data_series.dart'; +import 'data_visualization/chart_series/series.dart'; +import 'data_visualization/markers/marker_series.dart'; +import 'data_visualization/models/chart_object.dart'; import 'main_chart.dart'; /// Interactive chart widget. @@ -20,17 +35,31 @@ class Chart extends StatefulWidget { required this.drawingTools, this.pipSize = 4, this.controller, - this.overlaySeries, - this.bottomSeries, + this.overlayConfigs, + this.bottomConfigs, this.markerSeries, this.theme, this.onCrosshairAppeared, + this.onCrosshairDisappeared, + this.onCrosshairHover, this.onVisibleAreaChanged, + this.onQuoteAreaChanged, this.isLive = false, this.dataFitEnabled = false, this.opacity = 1.0, this.annotations, this.chartAxisConfig = const ChartAxisConfig(), + this.showCrosshair = false, + this.indicatorsRepo, + this.maxCurrentTickOffset, + this.msPerPx, + this.minIntervalWidth, + this.maxIntervalWidth, + this.verticalPaddingFraction, + this.bottomChartTitleMargin, + this.showDataFitButton, + this.showScrollToLastTickButton, + this.loadingAnimationColor, Key? key, }) : super(key: key); @@ -38,11 +67,11 @@ class Chart extends StatefulWidget { final DataSeries mainSeries; /// List of overlay indicator series to add on chart beside the [mainSeries]. - final List? overlaySeries; + final List? overlayConfigs; /// List of bottom indicator series to add on chart separate from the /// [mainSeries]. - final List? bottomSeries; + final List? bottomConfigs; /// Open position marker series. final MarkerSeries? markerSeries; @@ -64,9 +93,18 @@ class Chart extends StatefulWidget { /// Called when crosshair details appear after long press. final VoidCallback? onCrosshairAppeared; + /// Called when the crosshair is dismissed. + final VoidCallback? onCrosshairDisappeared; + + /// Called when the crosshair cursor is hovered/moved. + final OnCrosshairHoverCallback? onCrosshairHover; + /// Called when chart is scrolled or zoomed. final VisibleAreaChangedCallback? onVisibleAreaChanged; + /// Callback provided by library user. + final VisibleQuoteAreaChangedCallback? onQuoteAreaChanged; + /// Chart's theme. final ChartTheme? theme; @@ -88,6 +126,42 @@ class Chart extends StatefulWidget { /// Configurations for chart's axes. final ChartAxisConfig chartAxisConfig; + /// Whether the crosshair should be shown or not. + final bool showCrosshair; + + /// Max distance between rightBoundEpoch and nowEpoch in pixels. + final double? maxCurrentTickOffset; + + /// Specifies the zoom level of the chart. + final double? msPerPx; + + /// Specifies the minimum interval width + /// that is used for calculating the maximum msPerPx. + final double? minIntervalWidth; + + /// Specifies the maximum interval width + /// that is used for calculating the maximum msPerPx. + final double? maxIntervalWidth; + + /// Fraction of the chart's height taken by top or bottom padding. + /// Quote scaling (drag on quote area) is controlled by this variable. + final double? verticalPaddingFraction; + + /// Specifies the margin to prevent overlap. + final EdgeInsets? bottomChartTitleMargin; + + /// Whether the data fit button is shown or not. + final bool? showDataFitButton; + + /// Whether to show the scroll to last tick button or not. + final bool? showScrollToLastTickButton; + + /// The color of the loading animation. + final Color? loadingAnimationColor; + + /// Chart's indicators + final Repository? indicatorsRepo; + @override State createState() => _ChartState(); } @@ -97,6 +171,9 @@ class _ChartState extends State with WidgetsBindingObserver { bool? _followCurrentTick; late ChartController _controller; late ChartTheme _chartTheme; + late List? bottomSeries; + late List? overlaySeries; + int? expandedIndex; @override void initState() { @@ -115,6 +192,21 @@ class _ChartState extends State with WidgetsBindingObserver { _controller = widget.controller ?? ChartController(); } + List? _getIndicatorSeries(List? configs) { + if (configs == null) { + return null; + } + + return configs + .map((IndicatorConfig indicatorConfig) => indicatorConfig.getSeries( + IndicatorInput( + widget.mainSeries.input, + widget.granularity, + ), + )) + .toList(); + } + void _initChartTheme() { _chartTheme = widget.theme ?? (Theme.of(context).brightness == Brightness.dark @@ -130,13 +222,31 @@ class _ChartState extends State with WidgetsBindingObserver { chartAxisConfig: widget.chartAxisConfig, ); + final List? overlaySeries = + _getIndicatorSeries(widget.overlayConfigs); + + final List? bottomSeries = + _getIndicatorSeries(widget.bottomConfigs); + final List chartDataList = [ widget.mainSeries, - if (widget.overlaySeries != null) ...widget.overlaySeries!, - if (widget.bottomSeries != null) ...widget.bottomSeries!, + if (overlaySeries != null) ...overlaySeries, + if (bottomSeries != null) ...bottomSeries, if (widget.annotations != null) ...widget.annotations!, ]; + _controller + ..getSeriesList = (() => [ + if (overlaySeries != null) ...overlaySeries, + if (bottomSeries != null) ...bottomSeries, + ]) + ..getConfigsList = (() => [ + if (widget.overlayConfigs != null) ...?widget.overlayConfigs, + if (widget.bottomConfigs != null) ...?widget.bottomConfigs, + ]); + + final bool isExpanded = expandedIndex != null; + return MultiProvider( providers: [ Provider.value(value: _chartTheme), @@ -153,6 +263,10 @@ class _ChartState extends State with WidgetsBindingObserver { onVisibleAreaChanged: _onVisibleAreaChanged, isLive: widget.isLive, startWithDataFitMode: widget.dataFitEnabled, + maxCurrentTickOffset: widget.maxCurrentTickOffset, + msPerPx: widget.msPerPx, + minIntervalWidth: widget.minIntervalWidth, + maxIntervalWidth: widget.maxIntervalWidth, child: Column( children: [ Expanded( @@ -161,27 +275,96 @@ class _ChartState extends State with WidgetsBindingObserver { drawingTools: widget.drawingTools, controller: _controller, mainSeries: widget.mainSeries, - overlaySeries: widget.overlaySeries, + overlaySeries: overlaySeries, annotations: widget.annotations, markerSeries: widget.markerSeries, pipSize: widget.pipSize, onCrosshairAppeared: widget.onCrosshairAppeared, + onQuoteAreaChanged: widget.onQuoteAreaChanged, isLive: widget.isLive, showLoadingAnimationForHistoricalData: !widget.dataFitEnabled, - showDataFitButton: widget.dataFitEnabled, + showDataFitButton: + widget.showDataFitButton ?? widget.dataFitEnabled, + showScrollToLastTickButton: + widget.showScrollToLastTickButton ?? true, opacity: widget.opacity, chartAxisConfig: widget.chartAxisConfig, + verticalPaddingFraction: widget.verticalPaddingFraction, + showCrosshair: widget.showCrosshair, + onCrosshairDisappeared: widget.onCrosshairDisappeared, + onCrosshairHover: ( + PointerHoverEvent ev, + EpochToX epochToX, + QuoteToY quoteToY, + EpochFromX epochFromX, + QuoteFromY quoteFromY, + ) => + widget.onCrosshairHover?.call( + ev, + epochToX, + quoteToY, + epochFromX, + quoteFromY, + null, + ), + loadingAnimationColor: widget.loadingAnimationColor, ), ), - if (widget.bottomSeries?.isNotEmpty ?? false) - ...widget.bottomSeries! - .map((Series series) => Expanded( - child: BottomChart( - series: series, - pipSize: widget.pipSize, - ))) - .toList() + if (bottomSeries?.isNotEmpty ?? false) + ...bottomSeries!.mapIndexed((int index, Series series) { + if (isExpanded && expandedIndex != index) { + return const SizedBox.shrink(); + } + + return Expanded( + flex: isExpanded ? bottomSeries.length : 1, + child: BottomChart( + series: series, + granularity: widget.granularity, + pipSize: widget.bottomConfigs?[index].pipSize ?? + widget.pipSize, + title: widget.bottomConfigs![index].title, + bottomChartTitleMargin: widget.bottomChartTitleMargin, + onRemove: () => _onRemove(widget.bottomConfigs![index]), + onEdit: () => _onEdit(widget.bottomConfigs![index]), + onExpandToggle: () { + setState(() { + expandedIndex = + expandedIndex != index ? index : null; + }); + }, + onSwap: (int offset) => _onSwap( + widget.bottomConfigs![index], + widget.bottomConfigs![index + offset]), + onCrosshairDisappeared: widget.onCrosshairDisappeared, + onCrosshairHover: ( + PointerHoverEvent ev, + EpochToX epochToX, + QuoteToY quoteToY, + EpochFromX epochFromX, + QuoteFromY quoteFromY, + ) => + widget.onCrosshairHover?.call( + ev, + epochToX, + quoteToY, + epochFromX, + quoteFromY, + widget.bottomConfigs![index], + ), + isExpanded: isExpanded, + showCrosshair: widget.showCrosshair, + showExpandedIcon: bottomSeries.length > 1, + showMoveUpIcon: !isExpanded && + bottomSeries.length > 1 && + index != 0, + showMoveDownIcon: !isExpanded && + bottomSeries.length > 1 && + index != bottomSeries.length - 1, + ), + ); + }).toList() ], ), ), @@ -190,6 +373,30 @@ class _ChartState extends State with WidgetsBindingObserver { ); } + void _onEdit(IndicatorConfig config) { + if (widget.indicatorsRepo != null) { + final int index = widget.indicatorsRepo!.items.indexOf(config); + widget.indicatorsRepo!.editAt(index); + } + } + + void _onRemove(IndicatorConfig config) { + expandedIndex = null; + + if (widget.indicatorsRepo != null) { + final int index = widget.indicatorsRepo!.items.indexOf(config); + widget.indicatorsRepo!.removeAt(index); + } + } + + void _onSwap(IndicatorConfig config1, IndicatorConfig config2) { + if (widget.indicatorsRepo != null) { + final int index1 = widget.indicatorsRepo!.items.indexOf(config1); + final int index2 = widget.indicatorsRepo!.items.indexOf(config2); + widget.indicatorsRepo!.swap(index1, index2); + } + } + void _onVisibleAreaChanged(int leftBoundEpoch, int rightBoundEpoch) { widget.onVisibleAreaChanged?.call(leftBoundEpoch, rightBoundEpoch); diff --git a/lib/src/deriv_chart/chart/crosshair/crosshair_area.dart b/lib/src/deriv_chart/chart/crosshair/crosshair_area.dart index 3cde33e25..9bb36c87c 100644 --- a/lib/src/deriv_chart/chart/crosshair/crosshair_area.dart +++ b/lib/src/deriv_chart/chart/crosshair/crosshair_area.dart @@ -35,7 +35,7 @@ class CrosshairArea extends StatefulWidget { /// Called on longpress to show candle/point details. final VoidCallback? onCrosshairAppeared; - /// Called when candle or point is dismissed. + /// Called when the crosshair is dismissed. final VoidCallback? onCrosshairDisappeared; @override diff --git a/lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart b/lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart new file mode 100644 index 000000000..36a137dde --- /dev/null +++ b/lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart @@ -0,0 +1,92 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:deriv_chart/src/misc/callbacks.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; + +/// Place this area on top of the chart to display candle/point details on longpress. +class CrosshairAreaWeb extends StatefulWidget { + /// Initializes a widget to display candle/point details on longpress in a chart. + const CrosshairAreaWeb({ + required this.mainSeries, + required this.epochFromCanvasX, + required this.quoteFromCanvasY, + required this.epochToCanvasX, + required this.quoteToCanvasY, + this.quoteLabelsTouchAreaWidth = 70, + this.showCrosshairCursor = true, + this.pipSize = 4, + Key? key, + this.onCrosshairAppeared, + this.onCrosshairDisappeared, + this.onCrosshairHover, + }) : super(key: key); + + /// The main series of the chart. + final Series mainSeries; + + /// Number of decimal digits when showing prices. + final int pipSize; + + /// Width of the touch area for vertical zoom (on top of quote labels). + final double quoteLabelsTouchAreaWidth; + + /// Whether the crosshair cursor should be shown or not. + final bool showCrosshairCursor; + + /// Conversion function for converting chart's canvas' X position to epoch. + final EpochFromX epochFromCanvasX; + + /// Conversion function for converting chart's canvas' Y position to quote. + final QuoteFromY quoteFromCanvasY; + + /// Conversion function for converting epoch to chart's canvas' X position. + final EpochToX epochToCanvasX; + + /// Conversion function for converting quote to chart's canvas' Y position. + final QuoteToY quoteToCanvasY; + + /// Called on longpress to show candle/point details. + final VoidCallback? onCrosshairAppeared; + + /// Called when the crosshair is dismissed. + final VoidCallback? onCrosshairDisappeared; + + /// Called when the crosshair cursor is hovered/moved. + final OnCrosshairHover? onCrosshairHover; + + @override + _CrosshairAreaWebState createState() => _CrosshairAreaWebState(); +} + +class _CrosshairAreaWebState extends State { + XAxisModel get xAxis => context.read(); + + @override + Widget build(BuildContext context) => Positioned.fill( + right: widget.quoteLabelsTouchAreaWidth, + child: MouseRegion( + cursor: widget.showCrosshairCursor + ? SystemMouseCursors.precise + : SystemMouseCursors.basic, + child: const SizedBox.expand(), + onExit: (PointerExitEvent ev) => + widget.onCrosshairDisappeared?.call(), + onHover: (PointerHoverEvent ev) { + if (widget.onCrosshairHover == null) { + return; + } + + widget.onCrosshairHover?.call( + ev, + widget.epochToCanvasX, + widget.quoteToCanvasY, + widget.epochFromCanvasX, + widget.quoteFromCanvasY, + ); + }, + ), + ); +} diff --git a/lib/src/deriv_chart/chart/custom_painters/chart_data_painter.dart b/lib/src/deriv_chart/chart/custom_painters/chart_data_painter.dart index 261239528..0f275d38a 100644 --- a/lib/src/deriv_chart/chart/custom_painters/chart_data_painter.dart +++ b/lib/src/deriv_chart/chart/custom_painters/chart_data_painter.dart @@ -1,12 +1,15 @@ // ignore_for_file: unnecessary_null_comparison -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; -import 'package:deriv_chart/src/theme/painting_styles/chart_painting_style.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; +import '../data_visualization/chart_series/line_series/line_series.dart'; +import '../data_visualization/chart_series/ohlc_series/candle/candle_series.dart'; +import '../data_visualization/chart_series/series.dart'; + /// A `CustomPainter` which paints the chart data inside the chart. class ChartDataPainter extends BaseChartDataPainter { /// Initializes a `CustomPainter` which paints the chart data inside @@ -57,8 +60,7 @@ class ChartDataPainter extends BaseChartDataPainter { @override bool shouldRepaint(covariant ChartDataPainter oldDelegate) { bool styleChanged() => - (mainSeries is LineSeries && oldDelegate.mainSeries is CandleSeries) || - (mainSeries is CandleSeries && oldDelegate.mainSeries is LineSeries) || + (mainSeries.runtimeType != oldDelegate.mainSeries.runtimeType) || (mainSeries is LineSeries && theme.lineStyle != oldDelegate.theme.lineStyle) || (mainSeries is CandleSeries && @@ -68,8 +70,8 @@ class ChartDataPainter extends BaseChartDataPainter { mainSeries.shouldRepaint(oldDelegate.mainSeries); return super.shouldRepaint(oldDelegate) || - visibleAnimationChanged() || - styleChanged(); + styleChanged() || + visibleAnimationChanged(); } } @@ -149,15 +151,14 @@ class BaseChartDataPainter extends CustomPainter { return true; } - final Map oldStyles = - Map.fromIterable( + final Map oldSeries = Map.fromIterable( oldDelegate.series, key: (dynamic series) => series.id, - value: (dynamic series) => series.style, - ); - return series.any( - (Series series) => series.style != oldStyles[series.id], + value: (dynamic series) => series, ); + + return series + .any((Series series) => series.shouldRepaint(oldSeries[series.id])); } return rightBoundEpoch != oldDelegate.rightBoundEpoch || diff --git a/lib/src/deriv_chart/chart/custom_painters/loading_painter.dart b/lib/src/deriv_chart/chart/custom_painters/loading_painter.dart index 21ddb0ef9..bd25a7683 100644 --- a/lib/src/deriv_chart/chart/custom_painters/loading_painter.dart +++ b/lib/src/deriv_chart/chart/custom_painters/loading_painter.dart @@ -7,6 +7,7 @@ class LoadingPainter extends CustomPainter { LoadingPainter({ required this.loadingAnimationProgress, required this.loadingRightBoundX, + this.loadingAnimationColor, }); /// The progress shown in `double` for the loading. @@ -15,6 +16,9 @@ class LoadingPainter extends CustomPainter { /// The right bound of the loading area in X axis. final double loadingRightBoundX; + /// The color of the loading animation. + final Color? loadingAnimationColor; + @override void paint(Canvas canvas, Size size) { paintLoadingAnimation( @@ -22,6 +26,7 @@ class LoadingPainter extends CustomPainter { size: size, loadingAnimationProgress: loadingAnimationProgress, loadingRightBoundX: loadingRightBoundX, + color: loadingAnimationColor, ); } diff --git a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier.dart b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier.dart index 6d56dbbb9..3e0ed3879 100644 --- a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier.dart +++ b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier.dart @@ -1,7 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/barrier_objects.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; +import '../barrier.dart'; +import '../horizontal_barrier/horizontal_barrier.dart'; import 'accumulators_entry_spot_barrier_painter.dart'; /// [AccumulatorsEntrySpotBarrier] creates a dot and a dashed horizontal barrier diff --git a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier_painter.dart b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier_painter.dart index 2e0a209e1..7aaca29a1 100644 --- a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier_painter.dart @@ -1,15 +1,18 @@ import 'dart:ui' as ui; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/barrier_objects.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_accumulators_entry_spot.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_line.dart'; import 'package:deriv_chart/src/theme/colors.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; import 'package:deriv_chart/src/theme/painting_styles/entry_spot_style.dart'; import 'package:flutter/material.dart'; +import 'accumulators_entry_spot_barrier.dart'; + /// A class for painting horizontal barrier with entry spot. class AccumulatorsEntrySpotBarrierPainter< T extends AccumulatorsEntrySpotBarrier> extends SeriesPainter { diff --git a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/candle_indicator_painter.dart b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/candle_indicator_painter.dart index f4b5ce88c..27f2274ca 100644 --- a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/candle_indicator_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/candle_indicator_painter.dart @@ -1,15 +1,17 @@ import 'dart:math'; import 'dart:ui'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/barrier_objects.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_text.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; import 'package:flutter/material.dart'; +import 'horizontal_barrier.dart'; import 'horizontal_barrier_painter.dart'; +import 'tick_indicator.dart'; /// A class for painting candle indicators. class CandleIndicatorPainter extends HorizontalBarrierPainter { diff --git a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier.dart b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier.dart index c76415992..832484037 100644 --- a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier.dart +++ b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier.dart @@ -1,7 +1,9 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/barrier_objects.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; +import '../barrier.dart'; import 'horizontal_barrier_painter.dart'; /// Horizontal barrier class. diff --git a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier_painter.dart b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier_painter.dart index a401cf800..901526286 100644 --- a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/horizontal_barrier_painter.dart @@ -1,15 +1,19 @@ import 'dart:ui' as ui; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/barrier_objects.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/create_shape_path.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_dot.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_line.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_text.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; import 'package:flutter/material.dart'; +import 'horizontal_barrier.dart'; +import 'tick_indicator.dart'; + /// A class for painting horizontal barriers. class HorizontalBarrierPainter extends SeriesPainter { @@ -54,6 +58,7 @@ class HorizontalBarrierPainter series.style as HorizontalBarrierStyle? ?? theme.horizontalBarrierStyle; _paint = Paint() + ..style = PaintingStyle.fill ..strokeWidth = 1 ..color = style.color; diff --git a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/tick_indicator.dart b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/tick_indicator.dart index 520ed83bf..f0fabd09f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/tick_indicator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/annotations/barriers/horizontal_barrier/tick_indicator.dart @@ -1,11 +1,15 @@ import 'dart:async'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; +import 'package:deriv_chart/src/models/candle.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; import 'package:flutter/material.dart'; import 'candle_indicator_painter.dart'; +import 'horizontal_barrier.dart'; import 'horizontal_barrier_painter.dart'; /// Tick indicator. diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_data.dart b/lib/src/deriv_chart/chart/data_visualization/chart_data.dart index 68ec6a319..e5459be7a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_data.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_data.dart @@ -13,6 +13,12 @@ typedef EpochToX = double Function(int); /// Conversion function to convert value(quote) value to canvas Y. typedef QuoteToY = double Function(double); +/// Conversion function to convert canvas X to epoch value. +typedef EpochFromX = int Function(double); + +/// Conversion function to convert canvas Y to value(quote). +typedef QuoteFromY = double Function(double); + /// Any data that the chart takes and makes it paint its self on the chart's /// canvas including Line, CandleStick data, Markers, barriers etc.. abstract class ChartData { diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart index 5dc2a3bc2..cb7ff57d6 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart @@ -1,12 +1,16 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/functions/min_max_calculator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; import 'package:deriv_chart/src/theme/painting_styles/data_series_style.dart'; import 'package:flutter/material.dart'; +import '../annotations/barriers/horizontal_barrier/horizontal_barrier.dart'; import '../chart_data.dart'; import 'indexed_entry.dart'; +import 'series.dart'; import 'visible_entries.dart'; /// Series with only a single list of data to paint. diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart index fdbc7439c..d661f15af 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart @@ -1,15 +1,19 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/adx/adx_indicator_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_painters/bar_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; -import 'package:deriv_chart/src/theme/painting_styles/bar_style.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; +import '../data_series.dart'; +import '../series.dart'; import '../series_painter.dart'; import 'models/adx_options.dart'; import 'single_indicator_series.dart'; @@ -24,10 +28,17 @@ class ADXSeries extends Series { String? id, }) : super(id ?? 'ADX$adxOptions'); - late SingleIndicatorSeries _adxSeries; - late SingleIndicatorSeries _positiveDISeries; - late SingleIndicatorSeries _negativeDISeries; - late SingleIndicatorSeries _adxHistogramSeries; + /// ADX series + late SingleIndicatorSeries adxSeries; + + /// Positive DI series + late SingleIndicatorSeries positiveDISeries; + + /// Negative DI series + late SingleIndicatorSeries negativeDISeries; + + /// ADX histogram series + late SingleIndicatorSeries adxHistogramSeries; late List _adxSeriesList; @@ -56,27 +67,35 @@ class ADXSeries extends Series { positiveDIIndicator, negativeDIIndicator, adxPeriod: adxOptions.smoothingPeriod, - ); + )..calculateValues(); - _positiveDISeries = SingleIndicatorSeries( + positiveDISeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => positiveDIIndicator, inputIndicator: positiveDIIndicator, options: adxOptions, - style: const LineStyle(color: Colors.green), + style: config.positiveLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.positiveLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); - _negativeDISeries = SingleIndicatorSeries( + negativeDISeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => negativeDIIndicator, inputIndicator: negativeDIIndicator, options: adxOptions, - style: const LineStyle(color: Colors.red), + style: config.negativeLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.negativeLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); - _adxHistogramSeries = SingleIndicatorSeries( + adxHistogramSeries = SingleIndicatorSeries( painterCreator: (Series series) => BarPainter( series as DataSeries, checkColorCallback: ({ @@ -88,10 +107,10 @@ class ADXSeries extends Series { indicatorCreator: () => adxHistogramIndicator, inputIndicator: adxHistogramIndicator, options: adxOptions, - style: const BarStyle(), + style: config.barStyle, ); - _adxSeries = SingleIndicatorSeries( + adxSeries = SingleIndicatorSeries( painterCreator: (Series series) => OscillatorLinePainter( series as DataSeries, secondaryHorizontalLines: [0], @@ -99,16 +118,34 @@ class ADXSeries extends Series { indicatorCreator: () => adxIndicator, inputIndicator: adxIndicator, options: adxOptions, - style: const LineStyle(color: Colors.white), + style: config.lineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.lineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); _adxSeriesList = [ - _adxHistogramSeries, - _adxSeries, - _positiveDISeries, - _negativeDISeries, + adxSeries, + positiveDISeries, + negativeDISeries, ]; + if (config.showHistogram) { + _adxSeriesList.add(adxHistogramSeries); + } + + if (config.showShading) { + return ChannelFillPainter( + positiveDISeries, + negativeDISeries, + firstUpperChannelFillColor: + config.positiveLineStyle.color.withOpacity(0.2), + secondUpperChannelFillColor: + config.negativeLineStyle.color.withOpacity(0.2), + ); + } + return null; } @@ -117,12 +154,12 @@ class ADXSeries extends Series { final ADXSeries? series = oldData as ADXSeries?; final bool positiveDIUpdated = - _positiveDISeries.didUpdate(series?._positiveDISeries); + positiveDISeries.didUpdate(series?.positiveDISeries); final bool negativeDIUpdated = - _negativeDISeries.didUpdate(series?._negativeDISeries); - final bool adxUpdated = _adxSeries.didUpdate(series?._adxSeries); + negativeDISeries.didUpdate(series?.negativeDISeries); + final bool adxUpdated = adxSeries.didUpdate(series?.adxSeries); final bool histogramUpdated = - _adxHistogramSeries.didUpdate(series?._adxHistogramSeries); + adxHistogramSeries.didUpdate(series?.adxHistogramSeries); return positiveDIUpdated || negativeDIUpdated || @@ -141,6 +178,16 @@ class ADXSeries extends Series { List recalculateMinMax() => [_adxSeriesList.getMinValue(), _adxSeriesList.getMaxValue()]; + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + final ADXSeries oldSeries = previous as ADXSeries; + return config.toJson().toString() != oldSeries.config.toJson().toString(); + } + @override void paint( Canvas canvas, @@ -152,22 +199,27 @@ class ADXSeries extends Series { ChartTheme theme, ) { if (config.showSeries) { - _positiveDISeries.paint( + positiveDISeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _negativeDISeries.paint( + negativeDISeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _adxSeries.paint( + adxSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } if (config.showHistogram) { - _adxHistogramSeries.paint( + adxHistogramSeries.paint( + canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + } + + if (config.showShading) { + super.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } } @override - int? getMaxEpoch() => _adxSeries.getMaxEpoch(); + int? getMaxEpoch() => adxSeries.getMaxEpoch(); @override - int? getMinEpoch() => _adxSeries.getMinEpoch(); + int? getMinEpoch() => adxSeries.getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart index bd51b0f39..3b3b64c14 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; @@ -28,13 +29,9 @@ class AlligatorSeries extends Series { IndicatorInput indicatorInput, { required this.alligatorOptions, String? id, - this.jawOffset = 8, - this.teethOffset = 5, - this.lipsOffset = 3, }) : _fieldIndicator = HL2Indicator(indicatorInput), _indicatorInput = indicatorInput, - super(id ?? - 'Alligator$alligatorOptions$jawOffset$teethOffset$lipsOffset'); + super(id ?? 'Alligator$alligatorOptions'); final Indicator _fieldIndicator; final IndicatorInput _indicatorInput; @@ -42,37 +39,37 @@ class AlligatorSeries extends Series { /// Alligator options AlligatorOptions alligatorOptions; - SingleIndicatorSeries? _jawSeries; - SingleIndicatorSeries? _teethSeries; - SingleIndicatorSeries? _lipsSeries; + /// Jaw series + SingleIndicatorSeries? jawSeries; - SingleIndicatorSeries? _bullishSeries; - SingleIndicatorSeries? _bearishSeries; + /// Teeth series + SingleIndicatorSeries? teethSeries; - /// Shift to future in jaw series - final int jawOffset; + /// Lips series + SingleIndicatorSeries? lipsSeries; - /// Shift to future in teeth series - final int teethOffset; - - /// Shift to future in lips series - final int lipsOffset; + SingleIndicatorSeries? _bullishSeries; + SingleIndicatorSeries? _bearishSeries; @override SeriesPainter? createPainter() { if (alligatorOptions.showLines) { - _jawSeries = SingleIndicatorSeries( + jawSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => MMAIndicator(_fieldIndicator, alligatorOptions.jawPeriod), inputIndicator: _fieldIndicator, options: alligatorOptions, - style: const LineStyle(color: Colors.blue), - offset: jawOffset, + style: alligatorOptions.jawLineStyle, + offset: alligatorOptions.jawOffset, + lastTickIndicatorStyle: getLastIndicatorStyle( + alligatorOptions.jawLineStyle.color, + showLastIndicator: alligatorOptions.showLastIndicator, + ), ); - _teethSeries = SingleIndicatorSeries( + teethSeries = SingleIndicatorSeries( painterCreator: ( Series series, ) => @@ -83,11 +80,15 @@ class AlligatorSeries extends Series { ), inputIndicator: _fieldIndicator, options: alligatorOptions, - style: const LineStyle(color: Colors.red), - offset: teethOffset, + style: alligatorOptions.teethLineStyle, + offset: alligatorOptions.teethOffset, + lastTickIndicatorStyle: getLastIndicatorStyle( + alligatorOptions.teethLineStyle.color, + showLastIndicator: alligatorOptions.showLastIndicator, + ), ); - _lipsSeries = SingleIndicatorSeries( + lipsSeries = SingleIndicatorSeries( painterCreator: ( Series series, ) => @@ -96,8 +97,12 @@ class AlligatorSeries extends Series { MMAIndicator(_fieldIndicator, alligatorOptions.lipsPeriod), inputIndicator: _fieldIndicator, options: alligatorOptions, - style: const LineStyle(color: Colors.green), - offset: lipsOffset, + style: alligatorOptions.lipsLineStyle, + offset: alligatorOptions.lipsOffset, + lastTickIndicatorStyle: getLastIndicatorStyle( + alligatorOptions.lipsLineStyle.color, + showLastIndicator: alligatorOptions.showLastIndicator, + ), ); } @@ -131,11 +136,11 @@ class AlligatorSeries extends Series { bool didUpdate(ChartData? oldData) { final AlligatorSeries? series = oldData as AlligatorSeries?; - final bool _jawUpdated = _jawSeries?.didUpdate(series?._jawSeries) ?? false; + final bool _jawUpdated = jawSeries?.didUpdate(series?.jawSeries) ?? false; final bool _teethUpdated = - _teethSeries?.didUpdate(series?._teethSeries) ?? false; + teethSeries?.didUpdate(series?.teethSeries) ?? false; final bool _lipsUpdated = - _lipsSeries?.didUpdate(series?._lipsSeries) ?? false; + lipsSeries?.didUpdate(series?.lipsSeries) ?? false; final bool _bearishUpdated = _bearishSeries?.didUpdate(series?._bearishSeries) ?? false; @@ -151,9 +156,9 @@ class AlligatorSeries extends Series { @override void onUpdate(int leftEpoch, int rightEpoch) { - _jawSeries?.update(leftEpoch, rightEpoch); - _teethSeries?.update(leftEpoch, rightEpoch); - _lipsSeries?.update(leftEpoch, rightEpoch); + jawSeries?.update(leftEpoch, rightEpoch); + teethSeries?.update(leftEpoch, rightEpoch); + lipsSeries?.update(leftEpoch, rightEpoch); _bullishSeries?.update(leftEpoch, rightEpoch); _bearishSeries?.update(leftEpoch, rightEpoch); } @@ -161,17 +166,26 @@ class AlligatorSeries extends Series { @override List recalculateMinMax() => [ [ - _jawSeries, - _teethSeries, - _lipsSeries, + jawSeries, + teethSeries, + lipsSeries, ].getMinValue(), [ - _jawSeries, - _teethSeries, - _lipsSeries, + jawSeries, + teethSeries, + lipsSeries, ].getMaxValue() ]; + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + return alligatorOptions != (previous as AlligatorSeries).alligatorOptions; + } + @override void paint( Canvas canvas, @@ -182,11 +196,11 @@ class AlligatorSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _jawSeries?.paint( + jawSeries?.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _teethSeries?.paint( + teethSeries?.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _lipsSeries?.paint( + lipsSeries?.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); _bearishSeries?.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); @@ -196,9 +210,9 @@ class AlligatorSeries extends Series { @override int? getMaxEpoch() => - [_jawSeries, _teethSeries, _lipsSeries].getMaxEpoch(); + [jawSeries, teethSeries, lipsSeries].getMaxEpoch(); @override int? getMinEpoch() => - [_jawSeries, _teethSeries, _lipsSeries].getMinEpoch(); + [jawSeries, teethSeries, lipsSeries].getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/aroon_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/aroon_series.dart index 60c3187c2..8d4d2005f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/aroon_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/aroon_series.dart @@ -4,11 +4,11 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/single_indicator_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; -import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; @@ -32,25 +32,32 @@ class AroonSeries extends Series { /// Configs for `ArronIndicator` final AroonIndicatorConfig indicatorConfig; - late SingleIndicatorSeries _aroonUpSeries; - late SingleIndicatorSeries _aroonDownSeries; + /// Arron up series + late SingleIndicatorSeries aroonUpSeries; + + /// Arron down series + late SingleIndicatorSeries aroonDownSeries; /// options AroonOptions aroonOption; @override SeriesPainter? createPainter() { - _aroonUpSeries = SingleIndicatorSeries( + aroonUpSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => AroonUpIndicator.fromIndicator( HighValueIndicator(indicatorInput), period: indicatorConfig.period), inputIndicator: CloseValueIndicator(indicatorInput), - style: const LineStyle(color: Colors.green), + style: indicatorConfig.upLineStyle, options: aroonOption, + lastTickIndicatorStyle: getLastIndicatorStyle( + indicatorConfig.upLineStyle.color, + showLastIndicator: indicatorConfig.showLastIndicator, + ), ); - _aroonDownSeries = SingleIndicatorSeries( + aroonDownSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => AroonDownIndicator.fromIndicator( @@ -58,7 +65,11 @@ class AroonSeries extends Series { period: indicatorConfig.period), inputIndicator: CloseValueIndicator(indicatorInput), options: aroonOption, - style: const LineStyle(color: Colors.red), + style: indicatorConfig.downLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + indicatorConfig.downLineStyle.color, + showLastIndicator: indicatorConfig.showLastIndicator, + ), ); return null; @@ -67,28 +78,27 @@ class AroonSeries extends Series { @override bool didUpdate(ChartData? oldData) { final AroonSeries? series = oldData as AroonSeries?; - final bool _aroonUpUpdated = - _aroonUpSeries.didUpdate(series?._aroonUpSeries); + final bool _aroonUpUpdated = aroonUpSeries.didUpdate(series?.aroonUpSeries); final bool _aroonDownUpdated = - _aroonDownSeries.didUpdate(series?._aroonDownSeries); + aroonDownSeries.didUpdate(series?.aroonDownSeries); return _aroonUpUpdated || _aroonDownUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _aroonUpSeries.update(leftEpoch, rightEpoch); - _aroonDownSeries.update(leftEpoch, rightEpoch); + aroonUpSeries.update(leftEpoch, rightEpoch); + aroonDownSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => [ [ - _aroonUpSeries, - _aroonDownSeries, + aroonUpSeries, + aroonDownSeries, ].getMinValue(), [ - _aroonUpSeries, - _aroonDownSeries, + aroonUpSeries, + aroonDownSeries, ].getMaxValue() ]; @@ -102,21 +112,21 @@ class AroonSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _aroonDownSeries.paint( + aroonDownSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _aroonUpSeries.paint( + aroonUpSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } @override int? getMaxEpoch() => [ - _aroonDownSeries, - _aroonUpSeries, + aroonDownSeries, + aroonUpSeries, ].getMaxEpoch(); @override int? getMinEpoch() => [ - _aroonDownSeries, - _aroonUpSeries, + aroonDownSeries, + aroonUpSeries, ].getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart index eb8ecd488..db1200a73 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart @@ -1,6 +1,8 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; @@ -39,9 +41,14 @@ class BollingerBandSeries extends Series { }) : _fieldIndicator = indicator, super(id ?? 'Bollinger$bbOptions'); - late SingleIndicatorSeries _lowerSeries; - late SingleIndicatorSeries _middleSeries; - late SingleIndicatorSeries _upperSeries; + /// Lower series + late SingleIndicatorSeries lowerSeries; + + /// Middle series + late SingleIndicatorSeries middleSeries; + + /// Upper series + late SingleIndicatorSeries upperSeries; /// Bollinger bands options final BollingerBandsOptions bbOptions; @@ -58,66 +65,87 @@ class BollingerBandSeries extends Series { final CachedIndicator bbmSMA = MASeries.getMAIndicator(_fieldIndicator, bbOptions); - _middleSeries = SingleIndicatorSeries( + middleSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => bbmSMA, inputIndicator: _fieldIndicator, options: bbOptions, + style: bbOptions.middleLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + bbOptions.middleLineStyle.color, + showLastIndicator: bbOptions.showLastIndicator, + ), ); - _lowerSeries = SingleIndicatorSeries( - painterCreator: (Series series) => - LinePainter(series as DataSeries), - indicatorCreator: () => BollingerBandsLowerIndicator( - bbmSMA, - standardDeviation, - k: bbOptions.standardDeviationFactor, - ), - inputIndicator: _fieldIndicator, - options: bbOptions); - - _upperSeries = SingleIndicatorSeries( - painterCreator: (Series series) => - LinePainter(series as DataSeries), - indicatorCreator: () => BollingerBandsUpperIndicator( - bbmSMA, - standardDeviation, - k: bbOptions.standardDeviationFactor, - ), - inputIndicator: _fieldIndicator, - options: bbOptions); + lowerSeries = SingleIndicatorSeries( + painterCreator: (Series series) => + LinePainter(series as DataSeries), + indicatorCreator: () => BollingerBandsLowerIndicator( + bbmSMA, + standardDeviation, + k: bbOptions.standardDeviationFactor, + ), + inputIndicator: _fieldIndicator, + options: bbOptions, + style: bbOptions.lowerLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + bbOptions.lowerLineStyle.color, + showLastIndicator: bbOptions.showLastIndicator, + ), + ); - _innerSeries - ..add(_lowerSeries) - ..add(_middleSeries) - ..add(_upperSeries); + upperSeries = SingleIndicatorSeries( + painterCreator: (Series series) => + LinePainter(series as DataSeries), + indicatorCreator: () => BollingerBandsUpperIndicator( + bbmSMA, + standardDeviation, + k: bbOptions.standardDeviationFactor, + ), + inputIndicator: _fieldIndicator, + options: bbOptions, + style: bbOptions.upperLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + bbOptions.upperLineStyle.color, + showLastIndicator: bbOptions.showLastIndicator, + ), + ); - // TODO(ramin): return the painter that paints Channel Fill between bands - return null; + _innerSeries + ..add(lowerSeries) + ..add(middleSeries) + ..add(upperSeries); + + return ChannelFillPainter( + upperSeries, + lowerSeries, + firstUpperChannelFillColor: bbOptions.fillColor.withOpacity(0.2), + secondUpperChannelFillColor: bbOptions.fillColor.withOpacity(0.2), + ); } @override bool didUpdate(ChartData? oldData) { final BollingerBandSeries? series = oldData as BollingerBandSeries?; - final bool lowerUpdated = _lowerSeries.didUpdate(series?._lowerSeries); - final bool middleUpdated = _middleSeries.didUpdate(series?._middleSeries); - final bool upperUpdated = _upperSeries.didUpdate(series?._upperSeries); + final bool lowerUpdated = lowerSeries.didUpdate(series?.lowerSeries); + final bool middleUpdated = middleSeries.didUpdate(series?.middleSeries); + final bool upperUpdated = upperSeries.didUpdate(series?.upperSeries); return lowerUpdated || middleUpdated || upperUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _lowerSeries.update(leftEpoch, rightEpoch); - _middleSeries.update(leftEpoch, rightEpoch); - _upperSeries.update(leftEpoch, rightEpoch); + lowerSeries.update(leftEpoch, rightEpoch); + middleSeries.update(leftEpoch, rightEpoch); + upperSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => - // Can just use _lowerSeries minValue for min and _upperSeries maxValue + // Can just use lowerSeries minValue for min and upperSeries maxValue // for max. But to be safe we calculate min and max. from all three series [ _innerSeries @@ -128,6 +156,15 @@ class BollingerBandSeries extends Series { .reduce((double a, double b) => safeMax(a, b)), ]; + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + return bbOptions != (previous as BollingerBandSeries).bbOptions; + } + @override void paint( Canvas canvas, @@ -138,14 +175,19 @@ class BollingerBandSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _lowerSeries.paint( + lowerSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _middleSeries.paint( + middleSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _upperSeries.paint( + upperSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - // TODO(ramin): call super.paint to paint the Channels fill. + if (bbOptions.showChannelFill && + upperSeries.visibleEntries.isNotEmpty && + lowerSeries.visibleEntries.isNotEmpty) { + super.paint( + canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + } } @override diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/cci_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/cci_series.dart index ffabdca86..2308a90c5 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/cci_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/cci_series.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; @@ -33,6 +34,7 @@ class CCISeries extends AbstractSingleIndicatorSeries { const LineStyle(color: Colors.white, thickness: 0.5), LineStyle cciLineStyle = const LineStyle(), this.showZones = true, + this.showLastIndicator = false, String? id, }) : _options = options, super( @@ -40,6 +42,10 @@ class CCISeries extends AbstractSingleIndicatorSeries { id ?? 'CCISeries', options: options, style: cciLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + cciLineStyle.color, + showLastIndicator: showLastIndicator, + ), ); final IndicatorInput _indicatorInput; @@ -64,6 +70,9 @@ class CCISeries extends AbstractSingleIndicatorSeries { /// Whether to fill overbought/sold zones. final bool showZones; + /// Whether to show last indicator or not. + final bool showLastIndicator; + @override SeriesPainter createPainter() => showZones ? OscillatorLinePainter( diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart index b32747bf1..dae29e3b6 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart @@ -1,16 +1,19 @@ -import 'dart:ui' as ui; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_fill.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; +import '../line_series/line_series.dart'; +import '../series.dart'; import '../series_painter.dart'; /// Donchian Channels series @@ -37,9 +40,14 @@ class DonchianChannelsSeries extends Series { // TODO(Ramin): define DonchianChannelOptions class super(id ?? 'Donchian$config'); - late LineSeries _upperChannelSeries; - late LineSeries _middleChannelSeries; - late LineSeries _lowerChannelSeries; + /// Upper channel series. + late LineSeries upperChannelSeries; + + /// Middle channel series. + late LineSeries middleChannelSeries; + + /// Lower channel series. + late LineSeries lowerChannelSeries; final HighValueIndicator _highIndicator; final LowValueIndicator _lowIndicator; @@ -67,50 +75,81 @@ class DonchianChannelsSeries extends Series { lowerChannelIndicator, )..calculateValues(); - _upperChannelSeries = LineSeries( + upperChannelSeries = LineSeries( upperChannelIndicator.results, style: config.upperLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.upperLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); - _lowerChannelSeries = LineSeries( + lowerChannelSeries = LineSeries( lowerChannelIndicator.results, style: config.lowerLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.lowerLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); - _middleChannelSeries = LineSeries( + middleChannelSeries = LineSeries( middleChannelIndicator.results, style: config.middleLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.middleLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); - // TODO(ramin): return the painter that paints Channel Fill between bands + + if (config.showChannelFill) { + return ChannelFillPainter( + upperChannelSeries, + lowerChannelSeries, + firstUpperChannelFillColor: config.fillColor.withOpacity(0.2), + secondUpperChannelFillColor: config.fillColor.withOpacity(0.2), + ); + } + return null; } + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + final DonchianChannelsSeries oldSeries = previous as DonchianChannelsSeries; + return config.toJson().toString() != oldSeries.config.toJson().toString(); + } + @override bool didUpdate(ChartData? oldData) { final DonchianChannelsSeries? oldSeries = oldData as DonchianChannelsSeries?; final bool upperUpdated = - _upperChannelSeries.didUpdate(oldSeries?._upperChannelSeries); + upperChannelSeries.didUpdate(oldSeries?.upperChannelSeries); final bool middleUpdated = - _middleChannelSeries.didUpdate(oldSeries?._middleChannelSeries); + middleChannelSeries.didUpdate(oldSeries?.middleChannelSeries); final bool lowerUpdated = - _lowerChannelSeries.didUpdate(oldSeries?._lowerChannelSeries); + lowerChannelSeries.didUpdate(oldSeries?.lowerChannelSeries); return upperUpdated || middleUpdated || lowerUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _upperChannelSeries.update(leftEpoch, rightEpoch); - _middleChannelSeries.update(leftEpoch, rightEpoch); - _lowerChannelSeries.update(leftEpoch, rightEpoch); + upperChannelSeries.update(leftEpoch, rightEpoch); + middleChannelSeries.update(leftEpoch, rightEpoch); + lowerChannelSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => [ - _lowerChannelSeries.minValue, - _upperChannelSeries.maxValue, + lowerChannelSeries.minValue, + upperChannelSeries.maxValue, ]; @override @@ -123,119 +162,25 @@ class DonchianChannelsSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _upperChannelSeries.paint( + upperChannelSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _middleChannelSeries.paint( + middleChannelSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _lowerChannelSeries.paint( + lowerChannelSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - if (_lowerChannelSeries.entries == null || - _upperChannelSeries.entries == null) { - return; - } - if (config.showChannelFill && - _upperChannelSeries.visibleEntries.isNotEmpty && - _lowerChannelSeries.visibleEntries.isNotEmpty) { - final Path fillPath = Path() - ..moveTo( - epochToX(_upperChannelSeries.getEpochOf( - _upperChannelSeries.visibleEntries.first, - _upperChannelSeries.visibleEntries.startIndex, - )), - quoteToY(_upperChannelSeries.visibleEntries.first.quote), - ); - - for (int i = _upperChannelSeries.visibleEntries.startIndex + 1; - i < _upperChannelSeries.visibleEntries.endIndex - 1; - i++) { - final Tick tick = _upperChannelSeries.entries![i]; - fillPath.lineTo( - epochToX(_upperChannelSeries.getEpochOf(tick, i)), - quoteToY(tick.quote), - ); - } - - // Check for animated upper tick. - final Tick lastUpperTick = _upperChannelSeries.entries!.last; - final Tick lastUpperVisibleTick = _upperChannelSeries.visibleEntries.last; - double? lastVisibleTickX; - - if (lastUpperTick == lastUpperVisibleTick && - _upperChannelSeries.prevLastEntry != null) { - lastVisibleTickX = ui.lerpDouble( - epochToX(_upperChannelSeries.getEpochOf( - _upperChannelSeries.prevLastEntry!.entry, - _upperChannelSeries.prevLastEntry!.index, - )), - epochToX(lastUpperTick.epoch), - animationInfo.currentTickPercent, - ); - - final double tickY = quoteToY(ui.lerpDouble( - _upperChannelSeries.prevLastEntry!.entry.quote, - lastUpperTick.quote, - animationInfo.currentTickPercent, - )!); - - fillPath.lineTo(lastVisibleTickX!, tickY); - } else { - lastVisibleTickX = epochToX(lastUpperVisibleTick.epoch); - fillPath.lineTo(lastVisibleTickX, quoteToY(lastUpperVisibleTick.quote)); - } - - // Check for animated lower tick. - final Tick lastLowerTick = _lowerChannelSeries.entries!.last; - final Tick lastLowerVisibleTick = _lowerChannelSeries.visibleEntries.last; - - if (lastLowerTick == lastLowerVisibleTick && - _lowerChannelSeries.prevLastEntry != null) { - lastVisibleTickX = ui.lerpDouble( - epochToX(_lowerChannelSeries.getEpochOf( - _lowerChannelSeries.prevLastEntry!.entry, - _lowerChannelSeries.prevLastEntry!.index, - )), - epochToX(lastLowerTick.epoch), - animationInfo.currentTickPercent, - ); - - final double tickY = quoteToY(ui.lerpDouble( - _lowerChannelSeries.prevLastEntry!.entry.quote, - lastLowerTick.quote, - animationInfo.currentTickPercent, - )!); - - fillPath.lineTo(lastVisibleTickX!, tickY); - } else { - lastVisibleTickX = epochToX(lastLowerVisibleTick.epoch); - fillPath.lineTo(lastVisibleTickX, quoteToY(lastLowerVisibleTick.quote)); - } - - for (int i = _lowerChannelSeries.visibleEntries.endIndex - 1; - i >= _lowerChannelSeries.visibleEntries.startIndex; - i--) { - final Tick tick = _lowerChannelSeries.entries![i]; - fillPath.lineTo( - epochToX(_lowerChannelSeries.getEpochOf(tick, i)), - quoteToY(tick.quote), - ); - } - - fillPath.close(); - - paintFill( - canvas, - fillPath, - config.fillColor, - ); + upperChannelSeries.visibleEntries.isNotEmpty && + lowerChannelSeries.visibleEntries.isNotEmpty) { + super.paint( + canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } } // min/max epoch for all 3 channels are equal, using only `_loweChannelSeries` min/max. @override - int? getMaxEpoch() => _lowerChannelSeries.getMaxEpoch(); + int? getMaxEpoch() => lowerChannelSeries.getMaxEpoch(); @override - int? getMinEpoch() => _lowerChannelSeries.getMinEpoch(); + int? getMinEpoch() => lowerChannelSeries.getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/dpo_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/dpo_series.dart index 20cce38c5..002a5758c 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/dpo_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/dpo_series.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; @@ -38,7 +39,8 @@ class DPOSeries extends Series { }) : _fieldIndicator = indicator, super(id ?? 'Ichimoku$dpoOptions'); - late SingleIndicatorSeries _dpoSeries; + /// DPO Series + late SingleIndicatorSeries dpoSeries; /// Detrended Price Oscillator options final DPOOptions dpoOptions; @@ -55,7 +57,7 @@ class DPOSeries extends Series { isCentered: dpoOptions.isCentered, ); - _dpoSeries = SingleIndicatorSeries( + dpoSeries = SingleIndicatorSeries( painterCreator: (Series series) => OscillatorLinePainter( series as DataSeries, secondaryHorizontalLines: [0], @@ -64,6 +66,13 @@ class DPOSeries extends Series { inputIndicator: _fieldIndicator, options: dpoOptions, offset: dpoOptions.isCentered ? -dpoIndicator.timeShift : 0, + style: dpoOptions.lineStyle, + lastTickIndicatorStyle: dpoOptions.lineStyle != null + ? getLastIndicatorStyle( + dpoOptions.lineStyle!.color, + showLastIndicator: dpoOptions.showLastIndicator, + ) + : null, ); return null; @@ -73,20 +82,20 @@ class DPOSeries extends Series { bool didUpdate(ChartData? oldData) { final DPOSeries? series = oldData as DPOSeries; - final bool dpoUpdated = _dpoSeries.didUpdate(series?._dpoSeries); + final bool dpoUpdated = dpoSeries.didUpdate(series?.dpoSeries); return dpoUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _dpoSeries.update(leftEpoch, rightEpoch); + dpoSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => [ - _dpoSeries.maxValue, - _dpoSeries.minValue, + dpoSeries.minValue, + dpoSeries.maxValue, ]; @override @@ -99,13 +108,13 @@ class DPOSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _dpoSeries.paint( + dpoSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } @override - int? getMinEpoch() => _dpoSeries.getMinEpoch(); + int? getMinEpoch() => dpoSeries.getMinEpoch(); @override - int? getMaxEpoch() => _dpoSeries.getMaxEpoch(); + int? getMaxEpoch() => dpoSeries.getMaxEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fcb_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fcb_series.dart index ed802b1b1..b5ecbceed 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fcb_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fcb_series.dart @@ -1,10 +1,11 @@ +import 'package:deriv_chart/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; -import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; @@ -20,64 +21,84 @@ class FractalChaosBandSeries extends Series { /// Initializes FractalChaosBandSeries( this.indicatorInput, { + required this.config, String? id, - // ignore: avoid_unused_constructor_parameters - bool channelFill = false, }) : super(id ?? 'FCB'); ///input data final IndicatorInput indicatorInput; - late SingleIndicatorSeries _fcbHighSeries; - late SingleIndicatorSeries _fcbLowSeries; + /// FCB high series + late SingleIndicatorSeries fcbHighSeries; + + /// FCB low series + late SingleIndicatorSeries fcbLowSeries; + + /// Configuration of FCB Indicator. + final FractalChaosBandIndicatorConfig config; @override SeriesPainter? createPainter() { - _fcbHighSeries = SingleIndicatorSeries( + fcbHighSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), - // Using SMA temporarily until TA's migration branch gets updated. - indicatorCreator: () => - SMAIndicator(CloseValueIndicator(indicatorInput), 10), + indicatorCreator: () => FCBHighIndicator(indicatorInput), inputIndicator: CloseValueIndicator(indicatorInput), - style: const LineStyle(color: Colors.blue), + style: config.highLineStyle, ); - _fcbLowSeries = SingleIndicatorSeries( + fcbLowSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), - indicatorCreator: () => - SMAIndicator(CloseValueIndicator(indicatorInput), 10), + indicatorCreator: () => FCBLowIndicator(indicatorInput), inputIndicator: CloseValueIndicator(indicatorInput), - style: const LineStyle(color: Colors.blue), + style: config.lowLineStyle, ); + if (config.showChannelFill) { + return ChannelFillPainter( + fcbHighSeries, + fcbLowSeries, + firstUpperChannelFillColor: config.fillColor.withOpacity(0.2), + secondUpperChannelFillColor: config.fillColor.withOpacity(0.2), + ); + } + return null; } + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + final FractalChaosBandSeries oldSeries = previous as FractalChaosBandSeries; + return config.toJson().toString() != oldSeries.config.toJson().toString(); + } + @override bool didUpdate(ChartData? oldData) { final FractalChaosBandSeries? series = oldData as FractalChaosBandSeries?; - final bool _fcbHighUpdated = - _fcbHighSeries.didUpdate(series?._fcbHighSeries); - final bool _fcbLowUpdated = _fcbLowSeries.didUpdate(series?._fcbLowSeries); + final bool _fcbHighUpdated = fcbHighSeries.didUpdate(series?.fcbHighSeries); + final bool _fcbLowUpdated = fcbLowSeries.didUpdate(series?.fcbLowSeries); return _fcbHighUpdated || _fcbLowUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _fcbHighSeries.update(leftEpoch, rightEpoch); - _fcbLowSeries.update(leftEpoch, rightEpoch); + fcbHighSeries.update(leftEpoch, rightEpoch); + fcbLowSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => [ [ - _fcbHighSeries, - _fcbLowSeries, + fcbHighSeries, + fcbLowSeries, ].getMinValue(), [ - _fcbHighSeries, - _fcbLowSeries, + fcbHighSeries, + fcbLowSeries, ].getMaxValue() ]; @@ -91,21 +112,28 @@ class FractalChaosBandSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _fcbLowSeries.paint( + fcbLowSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _fcbHighSeries.paint( + fcbHighSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + + if (config.showChannelFill && + fcbHighSeries.visibleEntries.isNotEmpty && + fcbLowSeries.visibleEntries.isNotEmpty) { + super.paint( + canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + } } @override int? getMaxEpoch() => [ - _fcbLowSeries, - _fcbHighSeries, + fcbLowSeries, + fcbHighSeries, ].getMaxEpoch(); @override int? getMinEpoch() => [ - _fcbLowSeries, - _fcbHighSeries, + fcbLowSeries, + fcbHighSeries, ].getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart index d80cf5467..dd5769b76 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart @@ -1,10 +1,12 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/create_shape_path.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../../../chart_data.dart'; import '../../data_painter.dart'; +import '../../data_series.dart'; /// A [DataPainter] for painting arrow data. class ArrowPainter extends DataPainter> { diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/gator_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/gator_series.dart index 2c33c3a42..b7b0e5e88 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/gator_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/gator_series.dart @@ -28,6 +28,7 @@ class GatorSeries extends Series { IndicatorInput indicatorInput, { required this.gatorOptions, required this.gatorConfig, + this.barStyle = const BarStyle(), String? id, }) : _fieldIndicator = HL2Indicator(indicatorInput), super(id ?? 'Gator$AlligatorOptions'); @@ -37,15 +38,21 @@ class GatorSeries extends Series { /// Gator options AlligatorOptions gatorOptions; - late SingleIndicatorSeries _gatorTopSeries; - late SingleIndicatorSeries _gatorBottomSeries; + /// Gator top series + late SingleIndicatorSeries gatorTopSeries; + + /// Gator bottom series + late SingleIndicatorSeries gatorBottomSeries; /// Gator config final GatorIndicatorConfig gatorConfig; + /// Histogram bar style + final BarStyle barStyle; + @override SeriesPainter? createPainter() { - _gatorTopSeries = SingleIndicatorSeries( + gatorTopSeries = SingleIndicatorSeries( painterCreator: (Series series) => BarPainter( series as DataSeries, checkColorCallback: ({ @@ -59,14 +66,15 @@ class GatorSeries extends Series { jawPeriod: gatorOptions.jawPeriod, jawOffset: gatorConfig.jawOffset, teethPeriod: gatorOptions.teethPeriod, - teethOffset: gatorConfig.teethOffset), + teethOffset: gatorConfig.teethOffset) + ..calculateValues(), inputIndicator: _fieldIndicator, options: gatorOptions, - style: const BarStyle(), + style: barStyle, offset: min(gatorConfig.jawOffset, gatorConfig.teethOffset), ); - _gatorBottomSeries = SingleIndicatorSeries( + gatorBottomSeries = SingleIndicatorSeries( painterCreator: ( Series series, ) => @@ -84,10 +92,10 @@ class GatorSeries extends Series { teethOffset: gatorConfig.teethOffset, lipsOffset: gatorConfig.lipsOffset, lipsPeriod: gatorOptions.lipsPeriod, - ), + )..calculateValues(), inputIndicator: _fieldIndicator, options: gatorOptions, - style: const BarStyle(), + style: barStyle, offset: min(gatorConfig.teethOffset, gatorConfig.lipsOffset), ); @@ -98,23 +106,23 @@ class GatorSeries extends Series { bool didUpdate(ChartData? oldData) { final GatorSeries? series = oldData as GatorSeries?; final bool _gatorBottomUpdated = - _gatorBottomSeries.didUpdate(series?._gatorBottomSeries); + gatorBottomSeries.didUpdate(series?.gatorBottomSeries); final bool _gatorTopUpdated = - _gatorTopSeries.didUpdate(series?._gatorTopSeries); + gatorTopSeries.didUpdate(series?.gatorTopSeries); return _gatorTopUpdated || _gatorBottomUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _gatorTopSeries.update(leftEpoch, rightEpoch); - _gatorBottomSeries.update(leftEpoch, rightEpoch); + gatorTopSeries.update(leftEpoch, rightEpoch); + gatorBottomSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => [ - [_gatorBottomSeries, _gatorTopSeries].getMinValue(), - [_gatorBottomSeries, _gatorTopSeries].getMaxValue() + [gatorBottomSeries, gatorTopSeries].getMinValue(), + [gatorBottomSeries, gatorTopSeries].getMaxValue() ]; @override @@ -127,17 +135,17 @@ class GatorSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _gatorBottomSeries.paint( + gatorBottomSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _gatorTopSeries.paint( + gatorTopSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } @override int? getMaxEpoch() => - [_gatorBottomSeries, _gatorTopSeries].getMaxEpoch(); + [gatorBottomSeries, gatorTopSeries].getMaxEpoch(); @override int? getMinEpoch() => - [_gatorBottomSeries, _gatorTopSeries].getMinEpoch(); + [gatorBottomSeries, gatorTopSeries].getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart index 3bfee5033..ce5f9cb43 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart @@ -1,14 +1,18 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; +import '../data_series.dart'; +import '../series.dart'; import '../series_painter.dart'; import 'models/ichimoku_clouds_options.dart'; import 'single_indicator_series.dart'; @@ -23,11 +27,21 @@ class IchimokuCloudSeries extends Series { String? id, }) : super(id ?? 'Ichimoku$ichimokuCloudOptions'); - late SingleIndicatorSeries _conversionLineSeries; - late SingleIndicatorSeries _baseLineSeries; - late SingleIndicatorSeries _laggingSpanSeries; - late SingleIndicatorSeries _spanASeries; - late SingleIndicatorSeries _spanBSeries; + /// Conversion line series. + late SingleIndicatorSeries conversionLineSeries; + + /// Base line series. + late SingleIndicatorSeries baseLineSeries; + + /// Lagging line series. + late SingleIndicatorSeries laggingSpanSeries; + + /// SpanA line series. + late SingleIndicatorSeries spanASeries; + + /// SpanB line series. + late SingleIndicatorSeries spanBSeries; + final List _ichimokuSeries = []; /// List of [Tick]s to calculate IchimokuCloud on. @@ -72,74 +86,84 @@ class IchimokuCloudSeries extends Series { period: ichimokuCloudOptions.leadingSpanBPeriod, ); - _conversionLineSeries = SingleIndicatorSeries( + conversionLineSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => conversionLineIndicator, inputIndicator: closeValueIndicator, options: ichimokuCloudOptions, - style: const LineStyle( - color: Colors.indigo, + style: config.conversionLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.conversionLineStyle.color, + showLastIndicator: config.showLastIndicator, ), ); - _baseLineSeries = SingleIndicatorSeries( + baseLineSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => baseLineIndicator, inputIndicator: closeValueIndicator, options: ichimokuCloudOptions, - style: const LineStyle( - color: Colors.redAccent, + style: config.baseLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.baseLineStyle.color, + showLastIndicator: config.showLastIndicator, ), ); - _laggingSpanSeries = SingleIndicatorSeries( + laggingSpanSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => laggingSpanIndicator, inputIndicator: closeValueIndicator, options: ichimokuCloudOptions, offset: config.laggingSpanOffset, - style: const LineStyle( - color: Colors.lime, + style: config.laggingLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.laggingLineStyle.color, + showLastIndicator: config.showLastIndicator, ), ); - _spanASeries = SingleIndicatorSeries( + spanASeries = SingleIndicatorSeries( painterCreator: (Series series) => null, indicatorCreator: () => spanAIndicator, inputIndicator: closeValueIndicator, options: ichimokuCloudOptions, offset: ichimokuCloudOptions.baseLinePeriod, - style: const LineStyle( - color: Colors.green, + style: config.spanALineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.spanALineStyle.color, + showLastIndicator: config.showLastIndicator, ), ); - _spanBSeries = SingleIndicatorSeries( + spanBSeries = SingleIndicatorSeries( painterCreator: (Series series) => null, indicatorCreator: () => spanBIndicator, inputIndicator: closeValueIndicator, options: ichimokuCloudOptions, offset: ichimokuCloudOptions.baseLinePeriod, - style: const LineStyle( - color: Colors.red, + style: config.spanBLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.spanBLineStyle.color, + showLastIndicator: config.showLastIndicator, ), ); _ichimokuSeries - ..add(_conversionLineSeries) - ..add(_baseLineSeries) - ..add(_laggingSpanSeries) - ..add(_spanASeries) - ..add(_spanBSeries); + ..add(conversionLineSeries) + ..add(baseLineSeries) + ..add(laggingSpanSeries) + ..add(spanASeries) + ..add(spanBSeries); return ChannelFillPainter( - _spanASeries, - _spanBSeries, - firstUpperChannelFillColor: Colors.green.withOpacity(0.2), - secondUpperChannelFillColor: Colors.red.withOpacity(0.2), + spanASeries, + spanBSeries, + firstUpperChannelFillColor: config.spanALineStyle.color.withOpacity(0.2), + secondUpperChannelFillColor: config.spanBLineStyle.color.withOpacity(0.2), ); } @@ -148,13 +172,13 @@ class IchimokuCloudSeries extends Series { final IchimokuCloudSeries? series = oldData as IchimokuCloudSeries?; final bool conversionLineUpdated = - _conversionLineSeries.didUpdate(series?._conversionLineSeries); + conversionLineSeries.didUpdate(series?.conversionLineSeries); final bool baseLineUpdated = - _baseLineSeries.didUpdate(series?._baseLineSeries); + baseLineSeries.didUpdate(series?.baseLineSeries); final bool laggingSpanUpdated = - _laggingSpanSeries.didUpdate(series?._laggingSpanSeries); - final bool spanAUpdated = _spanASeries.didUpdate(series?._spanASeries); - final bool spanBUpdated = _spanBSeries.didUpdate(series?._spanBSeries); + laggingSpanSeries.didUpdate(series?.laggingSpanSeries); + final bool spanAUpdated = spanASeries.didUpdate(series?.spanASeries); + final bool spanBUpdated = spanBSeries.didUpdate(series?.spanBSeries); return conversionLineUpdated || baseLineUpdated || @@ -165,11 +189,11 @@ class IchimokuCloudSeries extends Series { @override void onUpdate(int leftEpoch, int rightEpoch) { - _conversionLineSeries.update(leftEpoch, rightEpoch); - _baseLineSeries.update(leftEpoch, rightEpoch); - _laggingSpanSeries.update(leftEpoch, rightEpoch); - _spanASeries.update(leftEpoch, rightEpoch); - _spanBSeries.update(leftEpoch, rightEpoch); + conversionLineSeries.update(leftEpoch, rightEpoch); + baseLineSeries.update(leftEpoch, rightEpoch); + laggingSpanSeries.update(leftEpoch, rightEpoch); + spanASeries.update(leftEpoch, rightEpoch); + spanBSeries.update(leftEpoch, rightEpoch); } @override @@ -185,6 +209,16 @@ class IchimokuCloudSeries extends Series { return [minValue, maxValue]; } + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + final IchimokuCloudSeries oldSeries = previous as IchimokuCloudSeries; + return config.toJson().toString() != oldSeries.config.toJson().toString(); + } + @override void paint( Canvas canvas, @@ -195,18 +229,22 @@ class IchimokuCloudSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _conversionLineSeries.paint( + conversionLineSeries.paint( + canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + baseLineSeries.paint( + canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + laggingSpanSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _baseLineSeries.paint( + spanASeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _laggingSpanSeries.paint( + spanBSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); super.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); if (animationInfo.currentTickPercent == 1) { - _spanASeries.resetLastEntryAnimation(); - _spanBSeries.resetLastEntryAnimation(); + spanASeries.resetLastEntryAnimation(); + spanBSeries.resetLastEntryAnimation(); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart index a1c2b48bb..6465c41b5 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart @@ -1,11 +1,12 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; -import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; @@ -45,9 +46,14 @@ class MAEnvSeries extends Series { /// Moving Average Envelope options MAEnvOptions? maEnvOptions; - late SingleIndicatorSeries _lowerSeries; - late SingleIndicatorSeries _middleSeries; - late SingleIndicatorSeries _upperSeries; + /// Lower series + late SingleIndicatorSeries lowerSeries; + + /// Middle series + late SingleIndicatorSeries middleSeries; + + /// Upper series + late SingleIndicatorSeries upperSeries; final List _innerSeries = []; @@ -56,7 +62,7 @@ class MAEnvSeries extends Series { final CachedIndicator smaIndicator = MASeries.getMAIndicator(_fieldIndicator, maEnvOptions!); - _lowerSeries = SingleIndicatorSeries( + lowerSeries = SingleIndicatorSeries( painterCreator: ( Series series, ) => @@ -68,19 +74,27 @@ class MAEnvSeries extends Series { ), inputIndicator: _fieldIndicator, options: maEnvOptions, - style: const LineStyle(color: Colors.red), + style: maEnvOptions!.lowerLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + maEnvOptions!.lowerLineStyle.color, + showLastIndicator: maEnvOptions!.showLastIndicator, + ), ); - _middleSeries = SingleIndicatorSeries( + middleSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => smaIndicator, inputIndicator: _fieldIndicator, options: maEnvOptions, - style: const LineStyle(color: Colors.blue), + style: maEnvOptions!.middleLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + maEnvOptions!.middleLineStyle.color, + showLastIndicator: maEnvOptions!.showLastIndicator, + ), ); - _upperSeries = SingleIndicatorSeries( + upperSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => MAEnvUpperIndicator( @@ -90,38 +104,47 @@ class MAEnvSeries extends Series { ), inputIndicator: _fieldIndicator, options: maEnvOptions, - style: const LineStyle(color: Colors.green), + style: maEnvOptions!.upperLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + maEnvOptions!.upperLineStyle.color, + showLastIndicator: maEnvOptions!.showLastIndicator, + ), ); _innerSeries - ..add(_lowerSeries) - ..add(_middleSeries) - ..add(_upperSeries); - - return null; + ..add(lowerSeries) + ..add(middleSeries) + ..add(upperSeries); + + return ChannelFillPainter( + upperSeries, + lowerSeries, + firstUpperChannelFillColor: maEnvOptions?.fillColor.withOpacity(0.2), + secondUpperChannelFillColor: maEnvOptions?.fillColor.withOpacity(0.2), + ); } @override bool didUpdate(ChartData? oldData) { final MAEnvSeries? series = oldData as MAEnvSeries?; - final bool _lowerUpdated = _lowerSeries.didUpdate(series?._lowerSeries); - final bool _middleUpdated = _middleSeries.didUpdate(series?._middleSeries); - final bool _upperUpdated = _upperSeries.didUpdate(series?._upperSeries); + final bool _lowerUpdated = lowerSeries.didUpdate(series?.lowerSeries); + final bool _middleUpdated = middleSeries.didUpdate(series?.middleSeries); + final bool _upperUpdated = upperSeries.didUpdate(series?.upperSeries); return _lowerUpdated || _middleUpdated || _upperUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _lowerSeries.update(leftEpoch, rightEpoch); - _middleSeries.update(leftEpoch, rightEpoch); - _upperSeries.update(leftEpoch, rightEpoch); + lowerSeries.update(leftEpoch, rightEpoch); + middleSeries.update(leftEpoch, rightEpoch); + upperSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => - // Can just use _lowerSeries minValue for min and _upperSeries maxValue + // Can just use lowerSeries minValue for min and upperSeries maxValue // for max. But to be safe we calculate min and max. from all three series // TODO(Ramin): Maybe later we can have these code and getMin/MaxEpochs in a parent class for Indicators like MAEnv, Ichimoku, Bollinger, etc [ @@ -133,6 +156,15 @@ class MAEnvSeries extends Series { .reduce((double a, double b) => safeMax(a, b)), ]; + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + return maEnvOptions != (previous as MAEnvSeries).maEnvOptions; + } + @override void paint( Canvas canvas, @@ -143,12 +175,20 @@ class MAEnvSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _lowerSeries.paint( + lowerSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _middleSeries.paint( + middleSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _upperSeries.paint( + upperSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + + if (maEnvOptions != null && + maEnvOptions!.showChannelFill && + upperSeries.visibleEntries.isNotEmpty && + lowerSeries.visibleEntries.isNotEmpty) { + super.paint( + canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); + } } @override diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_rainbow_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_rainbow_series.dart index c286ea1db..693833fdf 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_rainbow_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_rainbow_series.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; @@ -27,11 +28,11 @@ class RainbowSeries extends Series { RainbowSeries( IndicatorInput indicatorInput, { required RainbowOptions rainbowOptions, - List? rainbowColors, + List? rainbowLineStyles, String? id, }) : this.fromIndicator( CloseValueIndicator(indicatorInput), - rainbowColors: rainbowColors ?? const [], + rainbowLineStyles: rainbowLineStyles ?? const [], id: id, rainbowOptions: rainbowOptions, ); @@ -40,7 +41,7 @@ class RainbowSeries extends Series { RainbowSeries.fromIndicator( Indicator indicator, { required this.rainbowOptions, - this.rainbowColors = const [], + this.rainbowLineStyles = const [], String? id, }) : _fieldIndicator = indicator, super(id ?? 'MARainbow$rainbowOptions'); @@ -50,38 +51,50 @@ class RainbowSeries extends Series { /// Rainbow options RainbowOptions rainbowOptions; - final List _rainbowSeries = []; + /// Rainbow series + final List rainbowSeries = []; - /// colors of rainbow bands - final List rainbowColors; + /// Line styles of rainbow bands + final List rainbowLineStyles; @override SeriesPainter? createPainter() { /// check if we have color for every band - final bool useColors = rainbowColors.length == rainbowOptions.bandsCount; + final bool useColors = + rainbowLineStyles.length == rainbowOptions.bandsCount; final List> indicators = >[]; for (int i = 0; i < rainbowOptions.bandsCount; i++) { + final LineStyle style = + useColors ? rainbowLineStyles[i] : const LineStyle(color: Colors.red); if (i == 0) { indicators .add(MASeries.getMAIndicator(_fieldIndicator, rainbowOptions)); - _rainbowSeries.add(SingleIndicatorSeries( + rainbowSeries.add(SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => indicators[0] as CachedIndicator, inputIndicator: _fieldIndicator, options: rainbowOptions, - style: LineStyle(color: useColors ? rainbowColors[i] : Colors.red), + style: style, + lastTickIndicatorStyle: getLastIndicatorStyle( + style.color, + showLastIndicator: rainbowOptions.showLastIndicator, + ), )); } else { indicators .add(MASeries.getMAIndicator(indicators[i - 1], rainbowOptions)); - _rainbowSeries.add(SingleIndicatorSeries( + rainbowSeries.add(SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => indicators[i] as CachedIndicator, inputIndicator: _fieldIndicator, options: rainbowOptions, - style: LineStyle(color: useColors ? rainbowColors[i] : Colors.red), + style: style, + lastTickIndicatorStyle: getLastIndicatorStyle( + style.color, + showLastIndicator: rainbowOptions.showLastIndicator, + ), )); } } @@ -93,14 +106,13 @@ class RainbowSeries extends Series { final RainbowSeries? oldRainbowSeries = oldData as RainbowSeries?; if (oldRainbowSeries == null) { return false; - } else if (oldRainbowSeries._rainbowSeries.length != - _rainbowSeries.length) { + } else if (oldRainbowSeries.rainbowSeries.length != rainbowSeries.length) { return true; } bool needUpdate = false; - for (int i = 0; i < _rainbowSeries.length; i++) { - if (_rainbowSeries[i].didUpdate(oldRainbowSeries._rainbowSeries[i])) { + for (int i = 0; i < rainbowSeries.length; i++) { + if (rainbowSeries[i].didUpdate(oldRainbowSeries.rainbowSeries[i])) { needUpdate = true; } } @@ -109,7 +121,7 @@ class RainbowSeries extends Series { @override void onUpdate(int leftEpoch, int rightEpoch) { - for (final SingleIndicatorSeries series in _rainbowSeries) { + for (final SingleIndicatorSeries series in rainbowSeries) { series.update(leftEpoch, rightEpoch); } } @@ -125,7 +137,7 @@ class RainbowSeries extends Series { /// Returns minimum value of all series double _getMinValue() { final List minValues = []; - for (final SingleIndicatorSeries series in _rainbowSeries) { + for (final SingleIndicatorSeries series in rainbowSeries) { minValues.add(series.minValue); } return minValues.reduce(min); @@ -134,12 +146,23 @@ class RainbowSeries extends Series { /// Returns maximum value of all series double _getMaxValue() { final List maxValues = []; - for (final SingleIndicatorSeries series in _rainbowSeries) { + for (final SingleIndicatorSeries series in rainbowSeries) { maxValues.add(series.maxValue); } return maxValues.reduce(max); } + @override + bool shouldRepaint(ChartData? previous) { + if (previous == null) { + return true; + } + + final RainbowSeries oldSeries = previous as RainbowSeries; + return rainbowOptions != oldSeries.rainbowOptions || + rainbowLineStyles != oldSeries.rainbowLineStyles; + } + @override void paint( Canvas canvas, @@ -150,7 +173,7 @@ class RainbowSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - for (final SingleIndicatorSeries series in _rainbowSeries) { + for (final SingleIndicatorSeries series in rainbowSeries) { series.paint( canvas, size, @@ -165,9 +188,9 @@ class RainbowSeries extends Series { @override int? getMaxEpoch() => - _rainbowSeries.isNotEmpty ? _rainbowSeries[0].getMaxEpoch() : null; + rainbowSeries.isNotEmpty ? rainbowSeries[0].getMaxEpoch() : null; @override int? getMinEpoch() => - _rainbowSeries.isNotEmpty ? _rainbowSeries[0].getMinEpoch() : null; + rainbowSeries.isNotEmpty ? rainbowSeries[0].getMinEpoch() : null; } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart index 573f8fd26..83d633475 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart @@ -1,4 +1,6 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; @@ -42,11 +44,27 @@ class MASeries extends AbstractSingleIndicatorSeries { options: options, style: style ?? const LineStyle(thickness: 0.5), offset: offset, + lastTickIndicatorStyle: style != null + ? getLastIndicatorStyle( + style.color, + showLastIndicator: options.showLastIndicator, + ) + : null, ); @override SeriesPainter createPainter() => LinePainter(this); + @override + bool shouldRepaint(ChartData? oldDelegate) { + if (oldDelegate == null) { + return true; + } + + final MASeries oldSeries = oldDelegate as MASeries; + return options != oldSeries.options || style != oldSeries.style; + } + @override CachedIndicator initializeIndicator() => MASeries.getMAIndicator(inputIndicator, options as MAOptions); diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/macd_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/macd_series.dart index 6d3ae1807..c856855a7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/macd_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/macd_series.dart @@ -1,4 +1,3 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/macd_indicator/macd_indicator_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_painters/bar_painter.dart'; @@ -6,12 +5,16 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; -import 'package:deriv_chart/src/theme/painting_styles/bar_style.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; +import '../data_series.dart'; +import '../series.dart'; import '../series_painter.dart'; import 'models/macd_options.dart'; @@ -22,15 +25,20 @@ class MACDSeries extends Series { this.indicatorInput, { required this.options, required this.config, - String id = '', - }) : super(id); + String? id = '', + }) : super(id ?? 'MACD$options'); ///input data final IndicatorInput indicatorInput; - late SingleIndicatorSeries _macdSeries; - late SingleIndicatorSeries _signalMACDSeries; - late SingleIndicatorSeries _macdHistogramSeries; + /// MACD Series + late SingleIndicatorSeries macdSeries; + + /// Signal MACD Series + late SingleIndicatorSeries signalMACDSeries; + + /// MACD Histogram Series + late SingleIndicatorSeries macdHistogramSeries; /// MACD Configuration. MACDIndicatorConfig config; @@ -55,26 +63,34 @@ class MACDSeries extends Series { macdIndicator, signalMACDIndicator) ..calculateValues(); - _macdSeries = SingleIndicatorSeries( + macdSeries = SingleIndicatorSeries( painterCreator: (Series series) => OscillatorLinePainter( series as DataSeries, secondaryHorizontalLines: [0]), indicatorCreator: () => macdIndicator, inputIndicator: CloseValueIndicator(indicatorInput), - style: const LineStyle(color: Colors.white), + style: options.lineStyle, options: options, + lastTickIndicatorStyle: getLastIndicatorStyle( + options.lineStyle.color, + showLastIndicator: options.showLastIndicator, + ), ); - _signalMACDSeries = SingleIndicatorSeries( + signalMACDSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => signalMACDIndicator, inputIndicator: CloseValueIndicator(indicatorInput), - style: const LineStyle(color: Colors.redAccent), + style: options.signalLineStyle, options: options, + lastTickIndicatorStyle: getLastIndicatorStyle( + options.signalLineStyle.color, + showLastIndicator: options.showLastIndicator, + ), ); - _macdHistogramSeries = SingleIndicatorSeries( + macdHistogramSeries = SingleIndicatorSeries( painterCreator: (Series series) => BarPainter( series as DataSeries, checkColorCallback: ({ @@ -85,7 +101,7 @@ class MACDSeries extends Series { ), indicatorCreator: () => macdHistogramIndicator, inputIndicator: CloseValueIndicator(indicatorInput), - style: const BarStyle(), + style: options.barStyle, options: options, ); @@ -96,29 +112,29 @@ class MACDSeries extends Series { bool didUpdate(ChartData? oldData) { final MACDSeries? series = oldData as MACDSeries; - final bool macdUpdated = _macdSeries.didUpdate(series?._macdSeries); + final bool macdUpdated = macdSeries.didUpdate(series?.macdSeries); final bool signalMACDUpdated = - _signalMACDSeries.didUpdate(series?._signalMACDSeries); + signalMACDSeries.didUpdate(series?.signalMACDSeries); final bool macdHistogramUpdated = - _macdHistogramSeries.didUpdate(series?._macdHistogramSeries); + macdHistogramSeries.didUpdate(series?.macdHistogramSeries); return macdUpdated || signalMACDUpdated || macdHistogramUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _macdSeries.update(leftEpoch, rightEpoch); - _signalMACDSeries.update(leftEpoch, rightEpoch); - _macdHistogramSeries.update(leftEpoch, rightEpoch); + macdSeries.update(leftEpoch, rightEpoch); + signalMACDSeries.update(leftEpoch, rightEpoch); + macdHistogramSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => [ - [_macdSeries, _signalMACDSeries, _macdHistogramSeries] + [macdSeries, signalMACDSeries, macdHistogramSeries] .getMinValue(), - [_macdSeries, _signalMACDSeries, _macdHistogramSeries] + [macdSeries, signalMACDSeries, macdHistogramSeries] .getMaxValue() ]; @@ -132,25 +148,25 @@ class MACDSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _macdHistogramSeries.paint( + macdHistogramSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _macdSeries.paint( + macdSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _signalMACDSeries.paint( + signalMACDSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } @override int? getMaxEpoch() => [ - _macdSeries, - _signalMACDSeries, - _macdHistogramSeries, + macdSeries, + signalMACDSeries, + macdHistogramSeries, ].getMaxEpoch(); @override int? getMinEpoch() => [ - _macdSeries, - _signalMACDSeries, - _macdHistogramSeries, + macdSeries, + signalMACDSeries, + macdHistogramSeries, ].getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/alligator_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/alligator_options.dart index 7354af9c5..533491a82 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/alligator_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/alligator_options.dart @@ -1,3 +1,6 @@ +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +import 'package:flutter/material.dart'; + import 'indicator_options.dart'; /// Alligator indicator options. @@ -9,7 +12,14 @@ class AlligatorOptions extends IndicatorOptions { this.lipsPeriod = 5, this.showLines = true, this.showFractal = false, - }) : super(); + this.jawOffset = 8, + this.teethOffset = 5, + this.lipsOffset = 3, + this.jawLineStyle = const LineStyle(color: Colors.blue), + this.teethLineStyle = const LineStyle(color: Colors.red), + this.lipsLineStyle = const LineStyle(color: Colors.green), + bool showLastIndicator = false, + }) : super(showLastIndicator: showLastIndicator); /// Smoothing period for jaw series final int jawPeriod; @@ -26,6 +36,24 @@ class AlligatorOptions extends IndicatorOptions { /// show fractal indicator or not final bool showFractal; + /// Shift to future in jaw series + final int jawOffset; + + /// Shift to future in teeth series + final int teethOffset; + + /// Shift to future in lips series + final int lipsOffset; + + /// Jaw line style. + final LineStyle jawLineStyle; + + /// Teeth line style. + final LineStyle teethLineStyle; + + /// Lips line style. + final LineStyle lipsLineStyle; + @override List get props => [ jawPeriod, @@ -33,5 +61,11 @@ class AlligatorOptions extends IndicatorOptions { lipsPeriod, showLines, showFractal, + jawOffset, + teethOffset, + lipsOffset, + jawLineStyle, + teethLineStyle, + lipsLineStyle, ]; } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/bollinger_bands_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/bollinger_bands_options.dart index 0fca379e1..926c006ee 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/bollinger_bands_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/bollinger_bands_options.dart @@ -1,3 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; + import '../ma_series.dart'; import 'indicator_options.dart'; @@ -8,11 +11,44 @@ class BollingerBandsOptions extends MAOptions { this.standardDeviationFactor = 2, int period = 20, MovingAverageType movingAverageType = MovingAverageType.simple, - }) : super(period: period, type: movingAverageType); + this.upperLineStyle = const LineStyle(color: Colors.white), + this.middleLineStyle = const LineStyle(color: Colors.white), + this.lowerLineStyle = const LineStyle(color: Colors.white), + this.fillColor = Colors.white12, + this.showChannelFill = true, + bool showLastIndicator = false, + }) : super( + period: period, + type: movingAverageType, + showLastIndicator: showLastIndicator, + ); /// Standard Deviation value final double standardDeviationFactor; + /// Upper line style. + final LineStyle upperLineStyle; + + /// Middle line style. + final LineStyle middleLineStyle; + + /// Lower line style. + final LineStyle lowerLineStyle; + + /// Fill color. + final Color fillColor; + + /// Whether the area between upper and lower channel is filled. + final bool showChannelFill; + @override - List get props => super.props..add(standardDeviationFactor); + List get props => super.props + ..addAll([ + standardDeviationFactor, + upperLineStyle, + middleLineStyle, + lowerLineStyle, + fillColor, + showChannelFill + ]); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/dpo_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/dpo_options.dart index 7707dc832..27de865e0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/dpo_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/dpo_options.dart @@ -1,3 +1,5 @@ +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; + import '../ma_series.dart'; import 'indicator_options.dart'; @@ -8,7 +10,18 @@ class DPOOptions extends MAOptions { int period = 14, MovingAverageType movingAverageType = MovingAverageType.simple, this.isCentered = true, - }) : super(period: period, type: movingAverageType); + this.lineStyle, + bool showLastIndicator = false, + int pipSize = 4, + }) : super( + period: period, + type: movingAverageType, + showLastIndicator: showLastIndicator, + pipSize: pipSize, + ); + + /// Line style. + final LineStyle? lineStyle; /// Wether the indicator should be calculated `Centered` or not. final bool isCentered; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ichimoku_clouds_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ichimoku_clouds_options.dart index 8901a04c2..e13e5f20a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ichimoku_clouds_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ichimoku_clouds_options.dart @@ -7,7 +7,8 @@ class IchimokuCloudOptions extends IndicatorOptions { this.conversionLinePeriod = 9, this.baseLinePeriod = 26, this.leadingSpanBPeriod = 52, - }); + bool showLastIndicator = false, + }) : super(showLastIndicator: showLastIndicator); /// The `period` for the `IchimokuConversionLine`. Default is set to `9`. final int conversionLinePeriod; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/indicator_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/indicator_options.dart index 13115ac6c..ded6e7fc8 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/indicator_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/indicator_options.dart @@ -12,13 +12,27 @@ abstract class IndicatorOptions extends Equatable { /// Initializes /// /// Provides const constructor for sub-classes - const IndicatorOptions(); + const IndicatorOptions({ + this.showLastIndicator = false, + this.pipSize = 4, + }); + + /// Whether to show last indicator or not. + final bool showLastIndicator; + + /// Number of digits after decimal point in price. + final int pipSize; } /// Moving Average indicator options class MAOptions extends IndicatorOptions { /// Initializes - const MAOptions({this.period = 20, this.type = MovingAverageType.simple}); + const MAOptions({ + this.period = 20, + this.type = MovingAverageType.simple, + bool showLastIndicator = false, + int pipSize = 4, + }) : super(showLastIndicator: showLastIndicator, pipSize: pipSize); /// The average of this number of past data which will be calculated as /// MA value. diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ma_env_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ma_env_options.dart index 182c8d3f2..6c6a89c03 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ma_env_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/ma_env_options.dart @@ -1,3 +1,5 @@ +import 'package:flutter/material.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import '../ma_series.dart'; @@ -11,7 +13,17 @@ class MAEnvOptions extends MAOptions { this.shiftType = ShiftType.percent, int period = 50, MovingAverageType movingAverageType = MovingAverageType.simple, - }) : super(period: period, type: movingAverageType); + this.upperLineStyle = const LineStyle(color: Colors.green), + this.middleLineStyle = const LineStyle(color: Colors.blue), + this.lowerLineStyle = const LineStyle(color: Colors.red), + this.fillColor = Colors.white12, + this.showChannelFill = true, + bool showLastIndicator = false, + }) : super( + period: period, + type: movingAverageType, + showLastIndicator: showLastIndicator, + ); /// Shift value final double shift; @@ -19,8 +31,30 @@ class MAEnvOptions extends MAOptions { /// Shift type could be Percent or Point final ShiftType shiftType; + /// Upper line style. + final LineStyle upperLineStyle; + + /// Middle line style. + final LineStyle middleLineStyle; + + /// Lower line style. + final LineStyle lowerLineStyle; + + /// Fill color. + final Color fillColor; + + /// Whether the area between upper and lower channel is filled. + final bool showChannelFill; + @override List get props => super.props - ..add(shift) - ..add(shiftType); + ..addAll([ + shift, + shiftType, + upperLineStyle, + middleLineStyle, + lowerLineStyle, + fillColor, + showChannelFill + ]); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/macd_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/macd_options.dart index 5f86edf49..17fcd4fc8 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/macd_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/macd_options.dart @@ -1,3 +1,7 @@ +import 'package:deriv_chart/src/theme/painting_styles/bar_style.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +import 'package:flutter/material.dart'; + import 'indicator_options.dart'; /// MACD Options. @@ -7,7 +11,15 @@ class MACDOptions extends IndicatorOptions { this.fastMAPeriod = 12, this.slowMAPeriod = 26, this.signalPeriod = 9, - }); + this.barStyle = const BarStyle(), + this.lineStyle = const LineStyle(color: Colors.white), + this.signalLineStyle = const LineStyle(color: Colors.redAccent), + bool showLastIndicator = false, + int pipSize = 4, + }) : super( + showLastIndicator: showLastIndicator, + pipSize: pipSize, + ); /// The `period` for the `MACDFastMA`. Default is set to `12`. final int fastMAPeriod; @@ -18,6 +30,15 @@ class MACDOptions extends IndicatorOptions { /// The `period` for the `MACDSignal`. Default is set to `9`. final int signalPeriod; + /// Histogram bar style + final BarStyle barStyle; + + /// Line style. + final LineStyle lineStyle; + + /// Signal line style. + final LineStyle signalLineStyle; + @override List get props => [fastMAPeriod, slowMAPeriod, signalPeriod]; } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rainbow_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rainbow_options.dart index d5e65d627..ab7103063 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rainbow_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rainbow_options.dart @@ -8,7 +8,12 @@ class RainbowOptions extends MAOptions { this.bandsCount = 10, int period = 50, MovingAverageType movingAverageType = MovingAverageType.simple, - }) : super(period: period, type: movingAverageType); + bool showLastIndicator = false, + }) : super( + period: period, + type: movingAverageType, + showLastIndicator: showLastIndicator, + ); /// number of rainbow bands final int bandsCount; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/roc_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/roc_options.dart index 0eb22c8a3..2b68b410f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/roc_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/roc_options.dart @@ -3,7 +3,14 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie /// ROC indicator options. class ROCOptions extends IndicatorOptions { /// Initializes - const ROCOptions({this.period = 14}); + const ROCOptions({ + this.period = 14, + bool showLastIndicator = false, + int pipSize = 4, + }) : super( + pipSize: pipSize, + showLastIndicator: showLastIndicator, + ); /// The Period final int period; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rsi_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rsi_options.dart index b5cef0357..2a0cbb9e7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rsi_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rsi_options.dart @@ -3,7 +3,14 @@ import 'indicator_options.dart'; /// RSI indicator options. class RSIOptions extends IndicatorOptions { /// Initializes an RSI indicator options. - const RSIOptions({this.period = 14}); + const RSIOptions({ + this.period = 14, + bool showLastIndicator = false, + int pipSize = 4, + }) : super( + pipSize: pipSize, + showLastIndicator: showLastIndicator, + ); /// The period to calculate rsi Indicator on. final int period; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/smi_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/smi_options.dart index 8b434ecc5..72214fdfc 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/smi_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/smi_options.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/indicator_options.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; /// SMI Options class SMIOptions extends IndicatorOptions { @@ -12,7 +13,14 @@ class SMIOptions extends IndicatorOptions { period: 10, type: MovingAverageType.exponential, ), - }); + this.lineStyle, + this.signalLineStyle, + bool showLastIndicator = false, + int pipSize = 4, + }) : super( + showLastIndicator: showLastIndicator, + pipSize: pipSize, + ); /// Period final int period; @@ -26,6 +34,12 @@ class SMIOptions extends IndicatorOptions { /// SMI signal options. final MAOptions signalOptions; + /// Line style. + final LineStyle? lineStyle; + + /// Signal line style. + final LineStyle? signalLineStyle; + @override List get props => [ period, diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/stochastic_oscillator_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/stochastic_oscillator_options.dart index 66343708a..d92fe0b82 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/stochastic_oscillator_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/stochastic_oscillator_options.dart @@ -3,7 +3,15 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie /// StochasticOscillator indicator options. class StochasticOscillatorOptions extends IndicatorOptions { /// Initializes an StochasticOscillator indicator options. - const StochasticOscillatorOptions({this.period = 14, this.isSmooth = true}); + const StochasticOscillatorOptions({ + this.period = 14, + this.isSmooth = true, + bool showLastIndicator = false, + int pipSize = 4, + }) : super( + showLastIndicator: showLastIndicator, + pipSize: pipSize, + ); /// The period to calculate Stochastic Oscillator Indicator on. final int period; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/williams_r_options.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/williams_r_options.dart index a0b0ce247..c437e0454 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/williams_r_options.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/williams_r_options.dart @@ -3,7 +3,14 @@ import 'indicator_options.dart'; /// PSAR options class WilliamsROptions extends IndicatorOptions { /// Initializes - const WilliamsROptions(this.period); + const WilliamsROptions( + this.period, { + bool showLastIndicator = false, + int pipSize = 4, + }) : super( + pipSize: pipSize, + showLastIndicator: showLastIndicator, + ); /// Period final int period; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/parabolic_sar/parabolic_sar_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/parabolic_sar/parabolic_sar_series.dart index 611b08f72..a7a0a1044 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/parabolic_sar/parabolic_sar_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/parabolic_sar/parabolic_sar_series.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_painters/scatter_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/parabolic_sar_options.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; @@ -33,6 +34,16 @@ class ParabolicSARSeries extends AbstractSingleIndicatorSeries { @override SeriesPainter createPainter() => ScatterPainter(this); + @override + bool shouldRepaint(ChartData? oldDelegate) { + if (oldDelegate == null) { + return true; + } + + final ParabolicSARSeries oldSeries = oldDelegate as ParabolicSARSeries; + return options != oldSeries.options || style != oldSeries.style; + } + @override CachedIndicator initializeIndicator() => CustomParabolicSarIndicator( _indicatorInput, diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/roc_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/roc_series.dart index 0f0e3c54d..4b0a5acba 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/roc_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/roc_series.dart @@ -1,22 +1,28 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/abstract_single_indicator_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/roc_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; +import '../series.dart'; +import '../series_painter.dart'; + /// ROC series. class ROCSeries extends AbstractSingleIndicatorSeries { /// Initializes an ROC Indicator. ROCSeries( IndicatorInput indicatorInput, { required ROCOptions rocOptions, + LineStyle? lineStyle, String? id, }) : this.fromIndicator( CloseValueIndicator(indicatorInput), rocOptions: rocOptions, + lineStyle: lineStyle, id: id ?? 'ROCIndicator', ); @@ -24,12 +30,20 @@ class ROCSeries extends AbstractSingleIndicatorSeries { ROCSeries.fromIndicator( Indicator inputIndicator, { required this.rocOptions, + LineStyle? lineStyle, String? id, }) : _inputIndicator = inputIndicator, super( inputIndicator, id ?? 'ROCIndicator', options: rocOptions, + style: lineStyle, + lastTickIndicatorStyle: lineStyle != null + ? getLastIndicatorStyle( + lineStyle.color, + showLastIndicator: rocOptions.showLastIndicator, + ) + : null, ); final Indicator _inputIndicator; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/rsi_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/rsi_series.dart index cb8b13ba0..38cd0ede3 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/rsi_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/rsi_series.dart @@ -1,11 +1,13 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; +import '../series.dart'; import '../series_painter.dart'; import 'abstract_single_indicator_series.dart'; import 'models/rsi_options.dart'; @@ -39,6 +41,10 @@ class RSISeries extends AbstractSingleIndicatorSeries { id ?? 'RSIIndicator', options: rsiOptions, style: config.lineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.lineStyle.color, + showLastIndicator: rsiOptions.showLastIndicator, + ), ); final Indicator _inputIndicator; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/sample_multi_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/sample_multi_series.dart index 418494ada..59a789a5a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/sample_multi_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/sample_multi_series.dart @@ -1,13 +1,15 @@ import 'dart:math'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; +import '../series.dart'; import '../series_painter.dart'; +import 'ma_series.dart'; import 'models/indicator_options.dart'; import 'sample_multi_painter.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/smi_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/smi_series.dart index 7d7a6a23f..874e7ab12 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/smi_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/smi_series.dart @@ -1,4 +1,3 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/single_indicator_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; @@ -6,10 +5,17 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; +import '../data_series.dart'; +import '../series.dart'; +import 'ma_series.dart'; import 'models/smi_options.dart'; /// Stochastic Momentum Index series class. @@ -23,8 +29,11 @@ class SMISeries extends Series { String? id, }) : super(id ?? 'SMI'); - late SingleIndicatorSeries _smiSeries; - late SingleIndicatorSeries _smiSignalSeries; + /// SMI series. + late SingleIndicatorSeries smiSeries; + + /// SMI signal series. + late SingleIndicatorSeries smiSignalSeries; late List _innerSeries; @@ -47,9 +56,9 @@ class SMISeries extends Series { period: smiOptions.period, smoothingPeriod: smiOptions.smoothingPeriod, doubleSmoothingPeriod: smiOptions.doubleSmoothingPeriod, - ); + )..calculateValues(); - _smiSeries = SingleIndicatorSeries( + smiSeries = SingleIndicatorSeries( painterCreator: (Series series) => OscillatorLinePainter( series as DataSeries, topHorizontalLine: overboughtValue, @@ -57,21 +66,34 @@ class SMISeries extends Series { secondaryHorizontalLinesStyle: const LineStyle(), ), indicatorCreator: () => smiIndicator, + style: smiOptions.lineStyle, options: smiOptions, inputIndicator: CloseValueIndicator(input), + lastTickIndicatorStyle: smiOptions.lineStyle != null + ? getLastIndicatorStyle( + smiOptions.lineStyle!.color, + showLastIndicator: smiOptions.showLastIndicator, + ) + : null, ); - _smiSignalSeries = SingleIndicatorSeries( + smiSignalSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => MASeries.getMAIndicator(smiIndicator, smiOptions.signalOptions), inputIndicator: smiIndicator, - style: const LineStyle(color: Colors.red), + style: smiOptions.signalLineStyle ?? const LineStyle(color: Colors.red), options: smiOptions, + lastTickIndicatorStyle: smiOptions.signalLineStyle != null + ? getLastIndicatorStyle( + smiOptions.signalLineStyle!.color, + showLastIndicator: smiOptions.showLastIndicator, + ) + : null, ); - _innerSeries = [_smiSeries, _smiSignalSeries]; + _innerSeries = [smiSeries, smiSignalSeries]; return null; } @@ -80,23 +102,23 @@ class SMISeries extends Series { bool didUpdate(ChartData? oldData) { final SMISeries? oldSeries = oldData as SMISeries?; - final bool smiUpdated = _smiSeries.didUpdate(oldSeries?._smiSeries); + final bool smiUpdated = smiSeries.didUpdate(oldSeries?.smiSeries); final bool smiSignalUpdated = - _smiSignalSeries.didUpdate(oldSeries?._smiSignalSeries); + smiSignalSeries.didUpdate(oldSeries?.smiSignalSeries); return smiUpdated || smiSignalUpdated; } @override - int? getMaxEpoch() => _smiSeries.getMaxEpoch(); + int? getMaxEpoch() => smiSeries.getMaxEpoch(); @override - int? getMinEpoch() => _smiSeries.getMinEpoch(); + int? getMinEpoch() => smiSeries.getMinEpoch(); @override void onUpdate(int leftEpoch, int rightEpoch) { - _smiSeries.update(leftEpoch, rightEpoch); - _smiSignalSeries.update(leftEpoch, rightEpoch); + smiSeries.update(leftEpoch, rightEpoch); + smiSignalSeries.update(leftEpoch, rightEpoch); } @override @@ -115,9 +137,9 @@ class SMISeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _smiSeries.paint( + smiSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _smiSignalSeries.paint( + smiSignalSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/stochastic_oscillator_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/stochastic_oscillator_series.dart index cbeb92be4..0e4c6eb63 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/stochastic_oscillator_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/stochastic_oscillator_series.dart @@ -8,10 +8,10 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; -import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; @@ -27,8 +27,11 @@ class StochasticOscillatorSeries extends Series { String? id, }) : super(id ?? 'StochasticOscillator'); - late SingleIndicatorSeries _fastPercentStochasticIndicatorSeries; - late SingleIndicatorSeries _slowStochasticIndicatorSeries; + /// Fast percent StochasticOscillator indicator series. + late SingleIndicatorSeries fastPercentStochasticIndicatorSeries; + + /// Slow percent StochasticOscillator indicator series. + late SingleIndicatorSeries slowStochasticIndicatorSeries; ///input data final Indicator inputIndicator; @@ -54,7 +57,7 @@ class StochasticOscillatorSeries extends Series { SmoothedFastStochasticIndicator(fastStochasticIndicator, period: stochasticOscillatorOptions.period); - _fastPercentStochasticIndicatorSeries = SingleIndicatorSeries( + fastPercentStochasticIndicatorSeries = SingleIndicatorSeries( painterCreator: (Series series) => config.showZones ? OscillatorLinePainter( series as DataSeries, @@ -69,20 +72,28 @@ class StochasticOscillatorSeries extends Series { indicatorCreator: () => smoothedFastStochasticIndicator, inputIndicator: inputIndicator, options: stochasticOscillatorOptions, - style: const LineStyle(color: Colors.white), + style: config.fastLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.fastLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); - _slowStochasticIndicatorSeries = SingleIndicatorSeries( + slowStochasticIndicatorSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => SmoothedSlowStochasticIndicator(slowStochasticIndicator), inputIndicator: inputIndicator, options: stochasticOscillatorOptions, - style: const LineStyle(color: Colors.red), + style: config.slowLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.slowLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); } else { - _fastPercentStochasticIndicatorSeries = SingleIndicatorSeries( + fastPercentStochasticIndicatorSeries = SingleIndicatorSeries( painterCreator: (Series series) => config.showZones ? OscillatorLinePainter( series as DataSeries, @@ -97,16 +108,24 @@ class StochasticOscillatorSeries extends Series { indicatorCreator: () => fastStochasticIndicator, options: stochasticOscillatorOptions, inputIndicator: inputIndicator, - style: const LineStyle(color: Colors.white), + style: config.fastLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.fastLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); - _slowStochasticIndicatorSeries = SingleIndicatorSeries( + slowStochasticIndicatorSeries = SingleIndicatorSeries( painterCreator: (Series series) => LinePainter(series as DataSeries), indicatorCreator: () => slowStochasticIndicator, inputIndicator: inputIndicator, options: stochasticOscillatorOptions, - style: const LineStyle(color: Colors.red), + style: config.slowLineStyle, + lastTickIndicatorStyle: getLastIndicatorStyle( + config.slowLineStyle.color, + showLastIndicator: config.showLastIndicator, + ), ); } return null; @@ -116,29 +135,29 @@ class StochasticOscillatorSeries extends Series { bool didUpdate(ChartData? oldData) { final StochasticOscillatorSeries? series = oldData as StochasticOscillatorSeries?; - final bool _fastUpdated = _fastPercentStochasticIndicatorSeries - .didUpdate(series?._fastPercentStochasticIndicatorSeries); - final bool _slowUpdated = _slowStochasticIndicatorSeries - .didUpdate(series?._slowStochasticIndicatorSeries); + final bool _fastUpdated = fastPercentStochasticIndicatorSeries + .didUpdate(series?.fastPercentStochasticIndicatorSeries); + final bool _slowUpdated = slowStochasticIndicatorSeries + .didUpdate(series?.slowStochasticIndicatorSeries); return _fastUpdated || _slowUpdated; } @override void onUpdate(int leftEpoch, int rightEpoch) { - _fastPercentStochasticIndicatorSeries.update(leftEpoch, rightEpoch); - _slowStochasticIndicatorSeries.update(leftEpoch, rightEpoch); + fastPercentStochasticIndicatorSeries.update(leftEpoch, rightEpoch); + slowStochasticIndicatorSeries.update(leftEpoch, rightEpoch); } @override List recalculateMinMax() => [ [ - _fastPercentStochasticIndicatorSeries, - _slowStochasticIndicatorSeries, + fastPercentStochasticIndicatorSeries, + slowStochasticIndicatorSeries, ].getMinValue(), [ - _fastPercentStochasticIndicatorSeries, - _slowStochasticIndicatorSeries, + fastPercentStochasticIndicatorSeries, + slowStochasticIndicatorSeries, ].getMaxValue() ]; @@ -152,21 +171,21 @@ class StochasticOscillatorSeries extends Series { ChartConfig chartConfig, ChartTheme theme, ) { - _fastPercentStochasticIndicatorSeries.paint( + fastPercentStochasticIndicatorSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _slowStochasticIndicatorSeries.paint( + slowStochasticIndicatorSeries.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } @override int? getMaxEpoch() => [ - _fastPercentStochasticIndicatorSeries, - _slowStochasticIndicatorSeries, + fastPercentStochasticIndicatorSeries, + slowStochasticIndicatorSeries, ].getMaxEpoch(); @override int? getMinEpoch() => [ - _fastPercentStochasticIndicatorSeries, - _slowStochasticIndicatorSeries, + fastPercentStochasticIndicatorSeries, + slowStochasticIndicatorSeries, ].getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/williams_r_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/williams_r_series.dart index 9af82f064..d7aa3ab93 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/williams_r_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/williams_r_series.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; @@ -19,13 +20,23 @@ class WilliamsRSeries extends AbstractSingleIndicatorSeries { this.overboughtValue = -20, this.oversoldValue = -80, this.showZones = true, - this.overboughtSoldLineStyles = + this.overboughtLineStyle = const LineStyle(color: Colors.white, thickness: 0.5), + this.oversoldLineStyle = + const LineStyle(color: Colors.white, thickness: 0.5), + LineStyle? lineStyle, String? id, }) : super( CloseValueIndicator(_indicatorDataInput), id ?? 'WilliamsR', options: _options, + style: lineStyle, + lastTickIndicatorStyle: lineStyle != null + ? getLastIndicatorStyle( + lineStyle.color, + showLastIndicator: _options.showLastIndicator, + ) + : null, ); final IndicatorDataInput _indicatorDataInput; @@ -38,8 +49,11 @@ class WilliamsRSeries extends AbstractSingleIndicatorSeries { /// Oversold value final double oversoldValue; - /// The line style for overbought/sold horizontal lines. - final LineStyle overboughtSoldLineStyles; + /// LineStyle of overbought line + final LineStyle overboughtLineStyle; + + /// LineStyle of oversold line + final LineStyle oversoldLineStyle; /// Whether to show overbought/sold lines and zones channel fill. final bool showZones; @@ -50,10 +64,11 @@ class WilliamsRSeries extends AbstractSingleIndicatorSeries { this, topHorizontalLine: overboughtValue, bottomHorizontalLine: oversoldValue, - secondaryHorizontalLinesStyle: overboughtSoldLineStyles, + secondaryHorizontalLinesStyle: overboughtLineStyle, // TODO(NA): Zero line style will be removed from // OscillatorLinePainter - topHorizontalLinesStyle: overboughtSoldLineStyles, + topHorizontalLinesStyle: overboughtLineStyle, + bottomHorizontalLinesStyle: oversoldLineStyle, ) : LinePainter(this); diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/zigzag_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/zigzag_series.dart index 89513c5a9..bbc400ac6 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/zigzag_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/zigzag_series.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; @@ -29,11 +30,20 @@ class ZigZagSeries extends LineSeries { @override SeriesPainter> createPainter() => LinePainter(this); + @override + bool shouldRepaint(ChartData? oldDelegate) { + if (oldDelegate == null) { + return true; + } + + return style != (oldDelegate as ZigZagSeries).style; + } + @override VisibleEntries getVisibleEntries(int startIndex, int endIndex) { int firstIndex = startIndex; int lastIndex = endIndex; - if (entries == null || startIndex < 0 || endIndex >= entries!.length) { + if (entries == null || startIndex < 0 || endIndex - 1 > entries!.length) { return VisibleEntries.empty(); } if (entries![startIndex].quote.isNaN) { diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart index ae0da50dc..dae22b0cb 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart @@ -1,12 +1,14 @@ import 'dart:ui' as ui; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; import '../data_painter.dart'; +import '../data_series.dart'; /// A [DataPainter] for painting two line data and the channel fill inside /// of them. diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart index 35fa4ad54..7e41671ef 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart @@ -1,11 +1,13 @@ import 'dart:ui' as ui; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; import '../data_painter.dart'; +import '../data_series.dart'; /// A [DataPainter] for painting line data. class LinePainter extends DataPainter> { diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_series.dart index 8318e55a7..ea55f85d0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_series.dart @@ -1,6 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; +import '../data_series.dart'; import '../series_painter.dart'; import 'line_painter.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart index 9b2c057e3..23067059a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart @@ -1,10 +1,13 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_text.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; +import '../data_series.dart'; import 'line_painter.dart'; /// A [LinePainter] for painting line with two main top and bottom diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_series.dart index 1c6e87f3b..972701099 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_series.dart @@ -1,6 +1,9 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +import '../data_series.dart'; import '../series_painter.dart'; +import 'line_series.dart'; import 'oscillator_line_painter.dart'; /// Oscillator Line Series. @@ -30,6 +33,7 @@ class OscillatorLineSeries extends LineSeries { final double _topHorizontalLine; final double _bottomHorizontalLine; final LineStyle? _secondaryHorizontalLinesStyle; + // ignore: unused_field final LineStyle? _mainHorizontalLinesStyle; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart index 2ae58ee6e..e82d31f98 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart @@ -1,6 +1,6 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_chart/src/theme/painting_styles/chart_painting_style.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart index ea50a0d99..78003954e 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart @@ -1,9 +1,11 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; +import 'series.dart'; + /// A class responsible to paint its [series] data. abstract class SeriesPainter { /// Initializes series for sub-class. diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart index f06d99615..78b12fb9b 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -9,8 +9,9 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; /// Line drawing tool. A line is a vector defined by two points that is /// infinite in both directions. diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index bbe3c4960..448f09827 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -9,7 +9,6 @@ import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:deriv_chart/deriv_chart.dart'; /// Paints every existing drawing. class DrawingPainter extends StatefulWidget { diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index 96a2e1acf..73e08e553 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -12,8 +12,9 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; /// Line drawing tool. A line is a vector defined by two points that is /// infinite in both directions. diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index 339c18101..8909b7224 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -11,8 +11,9 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; /// Vertical drawing tool. A vertical is a vertical line defined by one point /// that is infinite in both directions. diff --git a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart index 503bcd854..8c9227dc3 100644 --- a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart +++ b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart @@ -1,12 +1,14 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../chart_data.dart'; import 'animated_active_marker.dart'; +import 'marker.dart'; +import 'marker_series.dart'; /// Layer with markers. class MarkerArea extends StatefulWidget { @@ -77,6 +79,7 @@ class _MarkerAreaState extends State { duration: animationDuration, opacity: widget.markerSeries.activeMarker != null ? 0.5 : 1, child: CustomPaint( + child: Container(), painter: _MarkerPainter( series: widget.markerSeries, epochToX: xAxis.xFromEpoch, diff --git a/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart b/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart index d4e7ebde4..2382e8e36 100644 --- a/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/markers/marker_series.dart @@ -1,10 +1,15 @@ import 'dart:collection'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/crosshair/find.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/painting_styles/marker_style.dart'; import '../chart_data.dart'; +import '../chart_series/series.dart'; +import 'active_marker.dart'; +import 'marker.dart'; +import 'marker_icon_painters/marker_icon_painter.dart'; import 'marker_painter.dart'; /// Marker series diff --git a/lib/src/deriv_chart/chart/helpers/indicator.dart b/lib/src/deriv_chart/chart/helpers/indicator.dart new file mode 100644 index 000000000..8f0b150df --- /dev/null +++ b/lib/src/deriv_chart/chart/helpers/indicator.dart @@ -0,0 +1,27 @@ +import 'dart:ui'; + +import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; +import 'package:flutter/material.dart'; + +import 'functions/helper_functions.dart'; + +/// Returns the last indicator style. +HorizontalBarrierStyle? getLastIndicatorStyle( + Color color, { + bool showLastIndicator = false, +}) { + if (showLastIndicator) { + return HorizontalBarrierStyle( + color: color, + hasLine: false, + textStyle: TextStyle( + fontSize: 10, + height: 1.3, + fontWeight: FontWeight.normal, + color: calculateTextColor(color), + fontFeatures: const [FontFeature.tabularFigures()], + ), + ); + } + return null; +} diff --git a/lib/src/deriv_chart/chart/helpers/paint_functions/paint_loading.dart b/lib/src/deriv_chart/chart/helpers/paint_functions/paint_loading.dart index 4533df568..4ad88c638 100644 --- a/lib/src/deriv_chart/chart/helpers/paint_functions/paint_loading.dart +++ b/lib/src/deriv_chart/chart/helpers/paint_functions/paint_loading.dart @@ -8,9 +8,10 @@ void paintLoadingAnimation({ required Size size, required double loadingAnimationProgress, required double loadingRightBoundX, + Color? color, }) { final Paint loadingPaint = Paint() - ..color = Colors.white12 + ..color = color ?? Colors.white12 ..strokeWidth = 1 ..style = PaintingStyle.fill; diff --git a/lib/src/deriv_chart/chart/loading_animation.dart b/lib/src/deriv_chart/chart/loading_animation.dart index bc86e6e8f..192d74528 100644 --- a/lib/src/deriv_chart/chart/loading_animation.dart +++ b/lib/src/deriv_chart/chart/loading_animation.dart @@ -6,12 +6,16 @@ class LoadingAnimationArea extends StatefulWidget { /// Creates loading animation area. const LoadingAnimationArea({ required this.loadingRightBoundX, + this.loadingAnimationColor, Key? key, }) : super(key: key); /// The right bound in the chart area when loading area is showing. final double loadingRightBoundX; + /// The color of the loading animation. + final Color? loadingAnimationColor; + @override _LoadingAnimationAreaState createState() => _LoadingAnimationAreaState(); } @@ -50,6 +54,7 @@ class _LoadingAnimationAreaState extends State painter: LoadingPainter( loadingAnimationProgress: _loadingAnimationController.value, loadingRightBoundX: widget.loadingRightBoundX, + loadingAnimationColor: widget.loadingAnimationColor, ), ), ), diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index 394b7a9e4..5f9c3c69f 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -1,5 +1,9 @@ import 'package:collection/collection.dart' show IterableExtension; -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/misc/chart_controller.dart'; +import 'package:deriv_chart/src/models/chart_axis_config.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:deriv_chart/src/deriv_chart/chart/crosshair/crosshair_area_web.dart'; import 'package:deriv_chart/src/deriv_chart/chart/crosshair/crosshair_area.dart'; import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_data_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_painter.dart'; @@ -8,13 +12,19 @@ import 'package:deriv_chart/src/deriv_chart/chart/loading_animation.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; -import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../misc/callbacks.dart'; +import '../../theme/chart_theme.dart'; import 'basic_chart.dart'; +import 'data_visualization/annotations/chart_annotation.dart'; import 'data_visualization/chart_data.dart'; +import 'data_visualization/chart_series/data_series.dart'; +import 'data_visualization/chart_series/series.dart'; +import 'data_visualization/markers/marker_series.dart'; import 'data_visualization/models/animation_info.dart'; +import 'data_visualization/models/chart_object.dart'; import 'helpers/functions/helper_functions.dart'; import 'multiple_animated_builder.dart'; @@ -29,13 +39,20 @@ class MainChart extends BasicChart { Key? key, this.showLoadingAnimationForHistoricalData = true, this.showDataFitButton = false, + this.showScrollToLastTickButton = true, this.markerSeries, this.controller, this.onCrosshairAppeared, + this.onCrosshairDisappeared, + this.onCrosshairHover, this.overlaySeries, this.annotations, + this.verticalPaddingFraction, + this.loadingAnimationColor, double opacity = 1, ChartAxisConfig? chartAxisConfig, + VisibleQuoteAreaChangedCallback? onQuoteAreaChanged, + this.showCrosshair = false, }) : _mainSeries = mainSeries, chartDataList = [ mainSeries, @@ -48,6 +65,7 @@ class MainChart extends BasicChart { pipSize: pipSize, opacity: opacity, chartAxisConfig: chartAxisConfig, + onQuoteAreaChanged: onQuoteAreaChanged, ); /// The indicator series that are displayed on the main chart. @@ -67,6 +85,12 @@ class MainChart extends BasicChart { /// The function that gets called on crosshair appearance. final VoidCallback? onCrosshairAppeared; + /// Called when the crosshair is dismissed. + final VoidCallback? onCrosshairDisappeared; + + /// Called when the crosshair cursor is hovered/moved. + final OnCrosshairHover? onCrosshairHover; + /// Chart's widget controller. final ChartController? controller; @@ -79,9 +103,22 @@ class MainChart extends BasicChart { /// Whether to show the data fit button or not. final bool showDataFitButton; + /// Whether to show the scroll to last tick button or not. + final bool showScrollToLastTickButton; + /// Convenience list to access all chart data. final List chartDataList; + /// Whether the crosshair should be shown or not. + final bool showCrosshair; + + /// Fraction of the chart's height taken by top or bottom padding. + /// Quote scaling (drag on quote area) is controlled by this variable. + final double? verticalPaddingFraction; + + /// The color of the loading animation. + final Color? loadingAnimationColor; + @override _ChartImplementationState createState() => _ChartImplementationState(); } @@ -130,12 +167,11 @@ class _ChartImplementationState extends BasicChartState { void initState() { super.initState(); - widget.controller?.onScrollToLastTick = ({required bool animate}) { - if (mounted) { - // TODO(Ramin): add the ability to close the controller. - xAxis.scrollToLastTick(animate: animate); - } - }; + if (widget.verticalPaddingFraction != null) { + verticalPaddingFraction = widget.verticalPaddingFraction!; + } + + _setupController(); } @override @@ -193,6 +229,43 @@ class _ChartImplementationState extends BasicChartState { _setupCrosshairZoomOutAnimation(); } + void _setupController() { + widget.controller?.onScrollToLastTick = ({required bool animate}) { + if (mounted) { + // TODO(Ramin): add the ability to close the controller. + xAxis.scrollToLastTick(animate: animate); + } + }; + + widget.controller?.onScale = (double scale) { + xAxis + ..onScaleAndPanStart(ScaleStartDetails()) + ..onScaleUpdate(ScaleUpdateDetails(scale: scale)); + return xAxis.msPerPx; + }; + + widget.controller?.onScroll = (double pxShift) { + xAxis.scrollBy(pxShift); + }; + + widget.controller?.toggleDataFitMode = ({required bool enableDataFit}) { + if (enableDataFit) { + xAxis.enableDataFit(); + } else { + xAxis.disableDataFit(); + } + }; + + widget.controller?.getXFromEpoch = (int epoch) => xAxis.xFromEpoch(epoch); + widget.controller?.getYFromQuote = + (double quote) => chartQuoteToCanvasY(quote); + + widget.controller?.getEpochFromX = (double x) => xAxis.epochFromX(x); + widget.controller?.getQuoteFromY = (double y) => chartQuoteFromCanvasY(y); + + widget.controller?.getMsPerPx = () => xAxis.msPerPx; + } + void _setupCrosshairZoomOutAnimation() { crosshairZoomOutAnimationController = AnimationController( vsync: this, @@ -275,8 +348,10 @@ class _ChartImplementationState extends BasicChartState { ), ), _buildDrawingToolChart(), - if (!widget.drawingTools.isDrawingMoving) _buildCrosshairArea(), - if (_isScrollToLastTickAvailable) + if (!widget.drawingTools.isDrawingMoving) + kIsWeb ? _buildCrosshairAreaWeb() : _buildCrosshairArea(), + if (widget.showScrollToLastTickButton && + _isScrollToLastTickAvailable) Positioned( bottom: 0, right: quoteLabelsTouchAreaWidth, @@ -312,6 +387,7 @@ class _ChartImplementationState extends BasicChartState { loadingRightBoundX: widget._mainSeries.input.isEmpty ? xAxis.width! : xAxis.xFromEpoch(widget._mainSeries.input.first.epoch), + loadingAnimationColor: widget.loadingAnimationColor, ); Widget _buildAnnotations() => MultipleAnimatedBuilder( @@ -359,6 +435,18 @@ class _ChartImplementationState extends BasicChartState { ), ); + Widget _buildCrosshairAreaWeb() => CrosshairAreaWeb( + mainSeries: widget.mainSeries as DataSeries, + epochFromCanvasX: xAxis.epochFromX, + quoteFromCanvasY: chartQuoteFromCanvasY, + epochToCanvasX: xAxis.xFromEpoch, + quoteToCanvasY: chartQuoteToCanvasY, + quoteLabelsTouchAreaWidth: quoteLabelsTouchAreaWidth, + showCrosshairCursor: widget.showCrosshair, + onCrosshairDisappeared: widget.onCrosshairDisappeared, + onCrosshairHover: widget.onCrosshairHover, + ); + Widget _buildScrollToLastTickButton() => Material( type: MaterialType.circle, color: Colors.transparent, @@ -366,6 +454,7 @@ class _ChartImplementationState extends BasicChartState { child: IconButton( icon: const Icon(Icons.arrow_forward), onPressed: xAxis.scrollToLastTick, + color: context.read().base01Color, ), ); diff --git a/lib/src/deriv_chart/chart/x_axis/grid/calc_time_grid.dart b/lib/src/deriv_chart/chart/x_axis/grid/calc_time_grid.dart index 96e1c0ed7..59e0c3c00 100644 --- a/lib/src/deriv_chart/chart/x_axis/grid/calc_time_grid.dart +++ b/lib/src/deriv_chart/chart/x_axis/grid/calc_time_grid.dart @@ -62,6 +62,9 @@ Duration timeGridInterval( PxFromMs pxFromMs, { double minDistanceBetweenLines = 100, List intervals = const [ + Duration(seconds: 1), + Duration(seconds: 2), + Duration(seconds: 3), Duration(seconds: 5), Duration(seconds: 10), Duration(seconds: 30), diff --git a/lib/src/deriv_chart/chart/x_axis/grid/check_new_day.dart b/lib/src/deriv_chart/chart/x_axis/grid/check_new_day.dart new file mode 100644 index 000000000..f9dd79868 --- /dev/null +++ b/lib/src/deriv_chart/chart/x_axis/grid/check_new_day.dart @@ -0,0 +1,8 @@ +/// Returns true if a new day is started 0:0:0 +bool checkNewDate(DateTime time) { + final bool is0h0m0s = time.hour == 0 && time.minute == 0 && time.second == 0; + + return (time.month == 1 && time.day == 1 && is0h0m0s) || + (time.day == 1 && is0h0m0s) || + is0h0m0s; +} diff --git a/lib/src/deriv_chart/chart/x_axis/grid/paint_x_grid.dart b/lib/src/deriv_chart/chart/x_axis/grid/paint_x_grid.dart index 1ef027c94..344520968 100644 --- a/lib/src/deriv_chart/chart/x_axis/grid/paint_x_grid.dart +++ b/lib/src/deriv_chart/chart/x_axis/grid/paint_x_grid.dart @@ -1,4 +1,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_text.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/grid/check_new_day.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/x_axis/grid/time_label.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_chart/src/theme/painting_styles/grid_style.dart'; import 'package:flutter/material.dart'; @@ -6,19 +9,28 @@ import 'package:flutter/material.dart'; void paintXGrid( Canvas canvas, Size size, { - required List timeLabels, required List xCoords, - required GridStyle style, + required ChartTheme style, + required List timestamps, }) { - assert(timeLabels.length == xCoords.length); + assert(timestamps.length == xCoords.length); + final GridStyle gridStyle = style.gridStyle; + + _paintTimeGridLines( + canvas, + size, + xCoords, + style, + gridStyle, + timestamps, + ); - _paintTimeGridLines(canvas, size, xCoords, style); _paintTimeLabels( canvas, size, xCoords: xCoords, - timeLabels: timeLabels, - style: style, + gridStyle: gridStyle, + timestamps: timestamps, ); } @@ -26,16 +38,20 @@ void _paintTimeGridLines( Canvas canvas, Size size, List xCoords, - GridStyle style, + ChartTheme style, + GridStyle gridStyle, + List time, ) { - for (final double x in xCoords) { + for (int i = 0; i < xCoords.length; i++) { canvas.drawLine( - Offset(x, 0), - Offset(x, size.height - style.xLabelsAreaHeight), + Offset(xCoords[i], 0), + Offset(xCoords[i], size.height - gridStyle.xLabelsAreaHeight), Paint() - ..color = style.gridLineColor + ..color = checkNewDate(time[i]) + ? style.verticalBarrierStyle.color + : gridStyle.gridLineColor ..style = PaintingStyle.stroke - ..strokeWidth = style.lineThickness, + ..strokeWidth = gridStyle.lineThickness, ); } } @@ -43,19 +59,19 @@ void _paintTimeGridLines( void _paintTimeLabels( Canvas canvas, Size size, { - required List timeLabels, required List xCoords, - required GridStyle style, + required GridStyle gridStyle, + required List timestamps, }) { - timeLabels.asMap().forEach((int index, String timeLabel) { + for (int index = 0; index < timestamps.length; index++) { paintText( canvas, - text: timeLabel, + text: timeLabel(timestamps[index]), anchor: Offset( xCoords[index], - size.height - style.xLabelsAreaHeight / 2, + size.height - gridStyle.xLabelsAreaHeight / 2, ), - style: style.xLabelStyle, + style: gridStyle.xLabelStyle, ); - }); + } } diff --git a/lib/src/deriv_chart/chart/x_axis/grid/x_grid_painter.dart b/lib/src/deriv_chart/chart/x_axis/grid/x_grid_painter.dart index 872aa0e5b..8df7e577d 100644 --- a/lib/src/deriv_chart/chart/x_axis/grid/x_grid_painter.dart +++ b/lib/src/deriv_chart/chart/x_axis/grid/x_grid_painter.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/src/theme/painting_styles/grid_style.dart'; +import 'package:deriv_chart/deriv_chart.dart'; import 'package:flutter/material.dart'; import 'paint_x_grid.dart'; @@ -7,30 +7,30 @@ import 'paint_x_grid.dart'; class XGridPainter extends CustomPainter { /// Creates x-axis painter. XGridPainter({ - required this.timeLabels, required this.xCoords, required this.style, + required this.timestamps, }); - /// Time labels. - final List timeLabels; - /// X-coordinates of time labels. final List xCoords; /// Style of the grid. - final GridStyle style; + final ChartTheme style; + + /// List of DateTime on screen + final List timestamps; @override void paint(Canvas canvas, Size size) { - if (timeLabels.isEmpty || xCoords.isEmpty) { + if (timestamps.isEmpty || xCoords.isEmpty) { return; } paintXGrid( canvas, size, - timeLabels: timeLabels, + timestamps: timestamps, xCoords: xCoords, style: style, ); @@ -38,7 +38,7 @@ class XGridPainter extends CustomPainter { @override bool shouldRepaint(XGridPainter oldDelegate) => - timeLabels != oldDelegate.timeLabels || + timestamps != oldDelegate.timestamps || xCoords != oldDelegate.xCoords || style != oldDelegate.style; diff --git a/lib/src/deriv_chart/chart/x_axis/x_axis.dart b/lib/src/deriv_chart/chart/x_axis/x_axis.dart index d1281ec52..18294d403 100644 --- a/lib/src/deriv_chart/chart/x_axis/x_axis.dart +++ b/lib/src/deriv_chart/chart/x_axis/x_axis.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; -import 'grid/time_label.dart'; import 'grid/x_grid_painter.dart'; import 'x_axis_model.dart'; @@ -27,6 +26,10 @@ class XAxis extends StatefulWidget { this.onVisibleAreaChanged, this.minEpoch, this.maxEpoch, + this.maxCurrentTickOffset, + this.msPerPx, + this.minIntervalWidth, + this.maxIntervalWidth, Key? key, }) : super(key: key); @@ -54,6 +57,20 @@ class XAxis extends StatefulWidget { /// Number of digits after decimal point in price final int pipSize; + /// Max distance between rightBoundEpoch and nowEpoch in pixels. + final double? maxCurrentTickOffset; + + /// Specifies the zoom level of the chart. + final double? msPerPx; + + /// Specifies the minimum interval width + /// that is used for calculating the maximum msPerPx. + final double? minIntervalWidth; + + /// Specifies the maximum interval width + /// that is used for calculating the maximum msPerPx. + final double? maxIntervalWidth; + @override _XAxisState createState() => _XAxisState(); } @@ -84,6 +101,9 @@ class _XAxisState extends State with TickerProviderStateMixin { maxEpoch: widget.maxEpoch, maxCurrentTickOffset: chartConfig.chartAxisConfig.maxCurrentTickOffset, defaultIntervalWidth: chartConfig.chartAxisConfig.defaultIntervalWidth, + msPerPx: widget.msPerPx, + minIntervalWidth: widget.minIntervalWidth, + maxIntervalWidth: widget.maxIntervalWidth, ); _ticker = createTicker(_model.onNewFrame)..start(); @@ -143,21 +163,21 @@ class _XAxisState extends State with TickerProviderStateMixin { return Stack( fit: StackFit.expand, children: [ - if (context.read().chartAxisConfig.showEpochGrid) - RepaintBoundary( - child: CustomPaint( - painter: XGridPainter( - timeLabels: _noOverlapGridTimestamps - .map((DateTime time) => timeLabel(time)) - .toList(), - xCoords: _noOverlapGridTimestamps - .map((DateTime time) => - _model.xFromEpoch(time.millisecondsSinceEpoch)) - .toList(), - style: _chartTheme.gridStyle, - ), + if (context.read().chartAxisConfig.showEpochGrid) + RepaintBoundary( + child: CustomPaint( + painter: XGridPainter( + timestamps: _noOverlapGridTimestamps + .map((DateTime time) => time) + .toList(), + xCoords: _noOverlapGridTimestamps + .map((DateTime time) => + _model.xFromEpoch(time.millisecondsSinceEpoch)) + .toList(), + style: _chartTheme, ), ), + ), Padding( padding: EdgeInsets.only( bottom: _chartTheme.gridStyle.xLabelsAreaHeight, diff --git a/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart b/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart index fc5f8732f..4291ae91f 100644 --- a/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart +++ b/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart @@ -58,6 +58,9 @@ class XAxisModel extends ChangeNotifier { bool startWithDataFitMode = false, int? minEpoch, int? maxEpoch, + double? msPerPx, + double? minIntervalWidth, + double? maxIntervalWidth, this.onScale, this.onScroll, }) { @@ -72,10 +75,13 @@ class XAxisModel extends ChangeNotifier { _lastEpoch = DateTime.now().millisecondsSinceEpoch; _granularity = granularity; - _msPerPx = _defaultMsPerPx; + _msPerPx = msPerPx ?? _defaultMsPerPx; _isLive = isLive; + _maxCurrentTickOffset = maxCurrentTickOffset; _rightBoundEpoch = _maxRightBoundEpoch; _dataFitMode = startWithDataFitMode; + _minIntervalWidth = minIntervalWidth ?? 1; + _maxIntervalWidth = maxIntervalWidth ?? 80; _updateEntries(entries); @@ -83,7 +89,7 @@ class XAxisModel extends ChangeNotifier { ..addListener(() { final double diff = _scrollAnimationController.value - (_prevScrollAnimationValue ?? 0); - _scrollBy(diff); + scrollBy(diff); if (hasHitLimit) { _scrollAnimationController.stop(); @@ -96,17 +102,17 @@ class XAxisModel extends ChangeNotifier { /// Limits panning to the right. final double maxCurrentTickOffset; - // TODO(NA): Allow customization of this setting. - /// Scaling will not resize intervals to be smaller than this. - static const int minIntervalWidth = 1; + late double _minIntervalWidth; - // TODO(NA): Allow customization of this setting. - /// Scaling will not resize intervals to be bigger than this. - static const int maxIntervalWidth = 80; + late double _maxIntervalWidth; /// Default to this interval width on granularity change. final double defaultIntervalWidth; + /// Max distance between [rightBoundEpoch] and [_nowEpoch] in pixels. + /// Limits panning to the right. + late double _maxCurrentTickOffset; + late bool _isLive; /// for calculating time between two frames @@ -152,10 +158,10 @@ class XAxisModel extends ChangeNotifier { set rightBoundEpoch(int value) => _rightBoundEpoch = value; /// Current scrolling lower bound. - int get _minRightBoundEpoch => _shiftEpoch(_minEpoch, maxCurrentTickOffset); + int get _minRightBoundEpoch => _shiftEpoch(_minEpoch, _maxCurrentTickOffset); /// Current scrolling upper bound. - int get _maxRightBoundEpoch => _shiftEpoch(_maxEpoch, maxCurrentTickOffset); + int get _maxRightBoundEpoch => _shiftEpoch(_maxEpoch, _maxCurrentTickOffset); /// Has hit left or right panning limit. bool get hasHitLimit => @@ -176,10 +182,10 @@ class XAxisModel extends ChangeNotifier { double get msPerPx => _msPerPx; /// Min value for [_msPerPx]. Limits zooming in. - double get _minMsPerPx => _granularity / maxIntervalWidth; + double get _minMsPerPx => _granularity / _maxIntervalWidth; /// Max value for [_msPerPx]. Limits zooming out. - double get _maxMsPerPx => _granularity / minIntervalWidth; + double get _maxMsPerPx => _granularity / _minIntervalWidth; /// Starting value for [_msPerPx]. double get _defaultMsPerPx => _granularity / defaultIntervalWidth; @@ -227,7 +233,7 @@ class XAxisModel extends ChangeNotifier { } break; case ViewingMode.constantScrollSpeed: - _scrollBy(_panSpeed * elapsedMs); + scrollBy(_panSpeed * elapsedMs); break; case ViewingMode.stationary: break; @@ -304,7 +310,12 @@ class XAxisModel extends ChangeNotifier { /// Updates chart's isLive property. /// /// Should be called before [_updateGranularity] and [_updateEntries] - void _updateIsLive(bool? isLive) => _isLive = isLive ?? true; + void _updateIsLive(bool? isLive) { + if (isLive == null || _isLive == isLive) { + return; + } + _isLive = isLive; + } /// Fits available data to screen. void _fitData() { @@ -407,7 +418,7 @@ class XAxisModel extends ChangeNotifier { /// Called when user is panning the chart. void onPanUpdate(DragUpdateDetails details) { - _scrollBy(-details.delta.dx); + scrollBy(-details.delta.dx); } /// Called at the end of scale and pan gestures. @@ -415,27 +426,36 @@ class XAxisModel extends ChangeNotifier { _triggerScrollMomentum(details.velocity); } + /// Called to scale the chart + void scale(double scale) { + _msPerPx = (_prevMsPerPx! / scale).clamp(_minMsPerPx, _maxMsPerPx); + onScale?.call(); + notifyListeners(); + } + + /// Called to scroll the chart + void scrollBy(double pxShift) { + _rightBoundEpoch = _shiftEpoch(_rightBoundEpoch, pxShift); + _clampRightBoundEpoch(); + onScroll?.call(); + notifyListeners(); + } + void _scaleWithNowFixed(ScaleUpdateDetails details) { final double nowToRightBound = pxBetween(_nowEpoch, rightBoundEpoch); - _scale(details.scale); + scale(details.scale); _rightBoundEpoch = _shiftEpoch(_nowEpoch, nowToRightBound); } void _scaleWithFocalPointFixed(ScaleUpdateDetails details) { final double focalToRightBound = width! - details.focalPoint.dx; final int focalEpoch = _shiftEpoch(rightBoundEpoch, -focalToRightBound); - _scale(details.scale); + scale(details.scale); _rightBoundEpoch = _shiftEpoch(focalEpoch, focalToRightBound); } - void _scale(double scale) { - _msPerPx = (_prevMsPerPx! / scale).clamp(_minMsPerPx, _maxMsPerPx); - onScale?.call(); - notifyListeners(); - } - void _scrollTo(int rightBoundEpoch) { - if (_rightBoundEpoch != rightBoundEpoch) { + if (width != null && _rightBoundEpoch != rightBoundEpoch) { _rightBoundEpoch = rightBoundEpoch; _clampRightBoundEpoch(); onScroll?.call(); @@ -443,13 +463,6 @@ class XAxisModel extends ChangeNotifier { } } - void _scrollBy(double pxShift) { - _rightBoundEpoch = _shiftEpoch(_rightBoundEpoch, pxShift); - _clampRightBoundEpoch(); - onScroll?.call(); - notifyListeners(); - } - /// Animate scrolling to current tick. void scrollToLastTick({bool animate = true}) { final Duration duration = @@ -457,7 +470,7 @@ class XAxisModel extends ChangeNotifier { final int target = _shiftEpoch( // _lastEntryEpoch will be removed later. (_entries?.isNotEmpty ?? false) ? _entries!.last.epoch : _nowEpoch, - maxCurrentTickOffset) + + _maxCurrentTickOffset) + duration.inMilliseconds; final double distance = target > _rightBoundEpoch diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index ba36a239a..c8e7534d7 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -1,15 +1,16 @@ import 'package:deriv_chart/generated/l10n.dart'; import 'package:collection/collection.dart'; +import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicators_dialog.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:deriv_chart/src/deriv_chart/chart/chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/annotations/chart_annotation.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/chart_object.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; @@ -17,6 +18,7 @@ import 'package:deriv_chart/src/misc/callbacks.dart'; import 'package:deriv_chart/src/misc/chart_controller.dart'; import 'package:deriv_chart/src/models/chart_axis_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; +import 'package:deriv_chart/src/misc/extensions.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_chart/src/widgets/animated_popup.dart'; @@ -33,14 +35,29 @@ class DerivChart extends StatefulWidget { this.markerSeries, this.controller, this.onCrosshairAppeared, + this.onCrosshairDisappeared, + this.onCrosshairHover, this.onVisibleAreaChanged, + this.onQuoteAreaChanged, this.theme, this.isLive = false, this.dataFitEnabled = false, + this.showCrosshair = true, this.annotations, this.opacity = 1.0, this.pipSize = 4, this.chartAxisConfig = const ChartAxisConfig(), + this.indicatorsRepo, + this.drawingToolsRepo, + this.maxCurrentTickOffset, + this.msPerPx, + this.minIntervalWidth, + this.maxIntervalWidth, + this.verticalPaddingFraction, + this.bottomChartTitleMargin, + this.showDataFitButton, + this.showScrollToLastTickButton, + this.loadingAnimationColor, Key? key, }) : super(key: key); @@ -63,9 +80,18 @@ class DerivChart extends StatefulWidget { /// Called when crosshair details appear after long press. final VoidCallback? onCrosshairAppeared; + /// Called when the crosshair is dismissed. + final VoidCallback? onCrosshairDisappeared; + + /// Called when the crosshair cursor is hovered/moved. + final OnCrosshairHoverCallback? onCrosshairHover; + /// Called when chart is scrolled or zoomed. final VisibleAreaChangedCallback? onVisibleAreaChanged; + /// Callback provided by library user. + final VisibleQuoteAreaChangedCallback? onQuoteAreaChanged; + /// Chart's theme. final ChartTheme? theme; @@ -87,15 +113,53 @@ class DerivChart extends StatefulWidget { /// Chart's opacity, Will be applied on the [mainSeries]. final double opacity; + /// Whether the crosshair should be shown or not. + final bool showCrosshair; + + /// Max distance between rightBoundEpoch and nowEpoch in pixels. + final double? maxCurrentTickOffset; + + /// Specifies the zoom level of the chart. + final double? msPerPx; + + /// Specifies the minimum interval width + /// that is used for calculating the maximum msPerPx. + final double? minIntervalWidth; + + /// Specifies the maximum interval width + /// that is used for calculating the maximum msPerPx. + final double? maxIntervalWidth; + + /// Fraction of the chart's height taken by top or bottom padding. + /// Quote scaling (drag on quote area) is controlled by this variable. + final double? verticalPaddingFraction; + + /// Specifies the margin to prevent overlap. + final EdgeInsets? bottomChartTitleMargin; + + /// Whether the data fit button is shown or not. + final bool? showDataFitButton; + + /// Whether to show the scroll to last tick button or not. + final bool? showScrollToLastTickButton; + + /// The color of the loading animation. + final Color? loadingAnimationColor; + + /// Chart's indicators + final Repository? indicatorsRepo; + + /// Chart's drawings + final Repository? drawingToolsRepo; + @override _DerivChartState createState() => _DerivChartState(); } class _DerivChartState extends State { - final AddOnsRepository _indicatorsRepo = - AddOnsRepository(IndicatorConfig); - final AddOnsRepository _drawingToolsRepo = - AddOnsRepository(DrawingToolConfig); + late AddOnsRepository _indicatorsRepo; + + late AddOnsRepository _drawingToolsRepo; final DrawingTools _drawingTools = DrawingTools(); @@ -104,41 +168,102 @@ class _DerivChartState extends State { super.initState(); loadSavedIndicatorsAndDrawingTools(); + _initRepos(); + } + + void _initRepos() { + _indicatorsRepo = AddOnsRepository( + createAddOn: (Map map) => IndicatorConfig.fromJson(map), + onEditCallback: showIndicatorsDialog, + ); + + _drawingToolsRepo = AddOnsRepository( + createAddOn: (Map map) => + DrawingToolConfig.fromJson(map), + onEditCallback: showDrawingToolsDialog, + ); } Future loadSavedIndicatorsAndDrawingTools() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - final List _stateRepos = [ - _indicatorsRepo, - _drawingToolsRepo - ]; + final List> _stateRepos = + >[_indicatorsRepo, _drawingToolsRepo]; + _stateRepos.asMap().forEach((int index, dynamic element) { try { element.loadFromPrefs(prefs); } on Exception { // ignore: unawaited_futures showDialog( - context: context, - builder: (BuildContext context) => AnimatedPopupDialog( - child: Center( - child: element is AddOnsRepository - ? Text(ChartLocalization.of(context) - .warnFailedLoadingIndicators) - : Text(ChartLocalization.of(context) - .warnFailedLoadingDrawingTools), - ), - )); + context: context, + builder: (BuildContext context) => AnimatedPopupDialog( + child: Center( + child: element is Repository + ? Text(context.localization.warnFailedLoadingIndicators) + : Text(context.localization.warnFailedLoadingDrawingTools), + ), + ), + ); } }); } + void showIndicatorsDialog() { + showDialog( + context: context, + builder: ( + BuildContext context, + ) => + ChangeNotifierProvider>.value( + value: _indicatorsRepo, + child: IndicatorsDialog(), + ), + ); + } + + void showDrawingToolsDialog() { + setState(() { + _drawingTools + ..init() + ..drawingToolsRepo = _drawingToolsRepo; + }); + showDialog( + context: context, + builder: ( + BuildContext context, + ) => + ChangeNotifierProvider>.value( + value: _drawingToolsRepo, + child: DrawingToolsDialog( + drawingTools: _drawingTools, + ), + ), + ); + } + + Widget _buildIndicatorsIcon() => Align( + alignment: Alignment.topLeft, + child: IconButton( + icon: const Icon(Icons.architecture), + onPressed: showIndicatorsDialog, + ), + ); + + Widget _buildDrawingToolsIcon() => Align( + alignment: const FractionalOffset(0.1, 0), + child: IconButton( + icon: const Icon(Icons.drive_file_rename_outline_outlined), + onPressed: showDrawingToolsDialog, + ), + ); + @override Widget build(BuildContext context) => MultiProvider( - providers: >[ - ChangeNotifierProvider>.value( - value: _indicatorsRepo), - ChangeNotifierProvider>.value( - value: _drawingToolsRepo), + providers: >>[ + ChangeNotifierProvider>.value( + value: widget.indicatorsRepo ?? _indicatorsRepo), + ChangeNotifierProvider>.value( + value: widget.drawingToolsRepo ?? _drawingToolsRepo), ], child: Builder( builder: (BuildContext context) => Stack( @@ -148,89 +273,44 @@ class _DerivChartState extends State { pipSize: widget.pipSize, granularity: widget.granularity, controller: widget.controller, - overlaySeries: [ + overlayConfigs: [ ...context - .watch>() - .addOns - .where((IndicatorConfig indicatorConfig) => - indicatorConfig.isOverlay) - .map((IndicatorConfig indicatorConfig) => - indicatorConfig.getSeries( - IndicatorInput( - widget.mainSeries.input, - widget.granularity, - ), - )) + .watch>() + .items + .where((IndicatorConfig config) => config.isOverlay) ], - bottomSeries: [ + bottomConfigs: [ ...context - .watch>() - .addOns - .where((IndicatorConfig indicatorConfig) => - !indicatorConfig.isOverlay) - .map((IndicatorConfig indicatorConfig) => - indicatorConfig.getSeries( - IndicatorInput( - widget.mainSeries.input, - widget.granularity, - ), - )) + .watch>() + .items + .where((IndicatorConfig config) => !config.isOverlay) ], drawingTools: _drawingTools, markerSeries: widget.markerSeries, theme: widget.theme, onCrosshairAppeared: widget.onCrosshairAppeared, + onCrosshairDisappeared: widget.onCrosshairDisappeared, + onCrosshairHover: widget.onCrosshairHover, onVisibleAreaChanged: widget.onVisibleAreaChanged, + onQuoteAreaChanged: widget.onQuoteAreaChanged, isLive: widget.isLive, dataFitEnabled: widget.dataFitEnabled, opacity: widget.opacity, annotations: widget.annotations, + showCrosshair: widget.showCrosshair, + indicatorsRepo: widget.indicatorsRepo ?? _indicatorsRepo, + maxCurrentTickOffset: widget.maxCurrentTickOffset, + msPerPx: widget.msPerPx, + minIntervalWidth: widget.minIntervalWidth, + maxIntervalWidth: widget.maxIntervalWidth, + verticalPaddingFraction: widget.verticalPaddingFraction, + bottomChartTitleMargin: widget.bottomChartTitleMargin, + showDataFitButton: widget.showDataFitButton, + showScrollToLastTickButton: widget.showScrollToLastTickButton, + loadingAnimationColor: widget.loadingAnimationColor, ), - Align( - alignment: Alignment.topLeft, - child: IconButton( - icon: const Icon(Icons.architecture), - onPressed: () { - showDialog( - context: context, - builder: ( - BuildContext context, - ) => - ChangeNotifierProvider< - AddOnsRepository>.value( - value: _indicatorsRepo, - child: IndicatorsDialog(), - ), - ); - }, - ), - ), - Align( - alignment: const FractionalOffset(0.1, 0), - child: IconButton( - icon: const Icon(Icons.drive_file_rename_outline_outlined), - onPressed: () { - setState(() { - _drawingTools - ..init() - ..drawingToolsRepo = _drawingToolsRepo; - }); - showDialog( - context: context, - builder: ( - BuildContext context, - ) => - ChangeNotifierProvider< - AddOnsRepository>.value( - value: _drawingToolsRepo, - child: DrawingToolsDialog( - drawingTools: _drawingTools, - ), - ), - ); - }, - ), - ), + if (widget.indicatorsRepo == null) _buildIndicatorsIcon(), + if (widget.drawingToolsRepo == null) _buildDrawingToolsIcon(), ], ), ), diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart index 589e32d84..9721642f0 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart @@ -1,6 +1,6 @@ import 'package:collection/collection.dart'; -import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; @@ -18,7 +18,7 @@ class DrawingTools { DrawingToolConfig? selectedDrawingTool; /// Keep the reference to the drawing tools repository. - AddOnsRepository? drawingToolsRepo; + Repository? drawingToolsRepo; /// For tracking the drawing movement bool isDrawingMoving = false; diff --git a/lib/src/misc/callbacks.dart b/lib/src/misc/callbacks.dart index 4cbbaedb3..b0ea2dcdb 100644 --- a/lib/src/misc/callbacks.dart +++ b/lib/src/misc/callbacks.dart @@ -1,5 +1,48 @@ +import 'package:deriv_chart/src/add_ons/add_on_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:flutter/gestures.dart'; + /// Called when chart is scrolled or zoomed. /// /// [leftEpoch] is an epoch value of the chart's left edge. /// [rightEpoch] is an epoch value of the chart's right edge. typedef VisibleAreaChangedCallback = Function(int leftEpoch, int rightEpoch); + +/// Called when the quotes in y-axis is changed +/// +/// [topQuote] is an quote value of the chart's top edge. +/// [bottomQuote] is an quote value of the chart's bottom edge. +typedef VisibleQuoteAreaChangedCallback = Function( + double topQuote, double bottomQuote); + +/// Called when the crosshair is moved +/// +/// [ev] is an instance of PointerHoverEvent +/// [epochToX] is a function to convert epoch value to canvas X. +/// [quoteToY] is a function to convert value(quote) value to canvas Y. +/// [epochFromX] is a function to convert canvas X to epoch value. +/// [quoteFromY] is a function to convert canvas Y to value(quote). +typedef OnCrosshairHover = void Function( + PointerHoverEvent ev, + EpochToX epochToX, + QuoteToY quoteToY, + EpochFromX epochFromX, + QuoteFromY quoteFromY, +); + +/// Called when the crosshair is moved +/// +/// [ev] is an instance of PointerHoverEvent +/// [epochToX] is a function to convert epoch value to canvas X. +/// [quoteToY] is a function to convert value(quote) value to canvas Y. +/// [epochFromX] is a function to convert canvas X to epoch value. +/// [quoteFromY] is a function to convert canvas Y to value(quote). +/// [config] is the config of the Indicator if it the hover is in BottomChart. +typedef OnCrosshairHoverCallback = void Function( + PointerHoverEvent ev, + EpochToX epochToX, + QuoteToY quoteToY, + EpochFromX epochFromX, + QuoteFromY quoteFromY, + AddOnConfig? config, +); diff --git a/lib/src/misc/chart_controller.dart b/lib/src/misc/chart_controller.dart index 3e483a702..2478aede3 100644 --- a/lib/src/misc/chart_controller.dart +++ b/lib/src/misc/chart_controller.dart @@ -1,13 +1,81 @@ +import 'package:deriv_chart/deriv_chart.dart'; + /// ScrollToLastTick callback. typedef OnScrollToLastTick = Function({required bool animate}); +/// Scale callback; +typedef OnScale = double? Function(double); + +/// Scroll callback; +typedef OnScroll = Function(double); + +/// To get X position +typedef GetXFromEpoch = double? Function(int); + +/// To get Y position +typedef GetYFromQuote = double? Function(double); + +/// To get epoch +typedef GetEpochFromX = int? Function(double); + +/// To get quote +typedef GetQuoteFromY = double? Function(double); + +/// To get overlay/bottom series +typedef GetSeriesList = List? Function(); + +/// To get overlay/bottom configs +typedef GetConfigsList = List? Function(); + +/// Toggles data fit mode +typedef ToggleDataFitMode = Function({required bool enableDataFit}); + +/// To get msPerPx +typedef GetMsPerPx = double? Function(); + /// Chart widget's controller. class ChartController { /// Called to scroll the current display chart to last tick. OnScrollToLastTick? onScrollToLastTick; + /// Called to scale the chart + OnScale? onScale; + + /// Called to scroll the chart + OnScroll? onScroll; + + /// Called to toggle data fit mode + ToggleDataFitMode? toggleDataFitMode; + + /// Called to get X position from epoch + GetXFromEpoch? getXFromEpoch; + + /// Called to get Y position from quote + GetYFromQuote? getYFromQuote; + + /// Called to get epoch from x position + GetEpochFromX? getEpochFromX; + + /// Called to get quote from y position + GetQuoteFromY? getQuoteFromY; + + /// Called to get overlay and bottom series + GetSeriesList? getSeriesList; + + /// Called to get overlay and bottom configs + GetConfigsList? getConfigsList; + + /// Called to get msPerPx + GetMsPerPx? getMsPerPx; + /// Scroll chart visible area to the newest data. void scrollToLastTick({bool animate = false}) => onScrollToLastTick?.call(animate: animate); + + /// Scales the chart. + double? scale(double scale) => onScale?.call(scale); + + /// Scroll chart visible area. + void scroll(double pxShift) => onScroll?.call(pxShift); } diff --git a/lib/src/theme/colors.dart b/lib/src/theme/colors.dart index 66a4ec7f3..b987db7a8 100644 --- a/lib/src/theme/colors.dart +++ b/lib/src/theme/colors.dart @@ -28,14 +28,14 @@ class DarkThemeColors { /// These colors suits the light theme of Deriv. // TODO(Ramin): replace values based on light theme when available class LightThemeColors { - static const Color base01 = Color(0xFFFFFFFF); - static const Color base02 = Color(0xFFEAECED); - static const Color base03 = Color(0xFFC2C2C2); - static const Color base04 = Color(0xFF6E6E6E); - static const Color base05 = Color(0xFF3E3E3E); - static const Color base06 = Color(0xFF323738); - static const Color base07 = Color(0xFF151717); - static const Color base08 = Color(0xFF0E0E0E); + static const Color base01 = Color(0xFF0E0E0E); + static const Color base02 = Color(0xFF151717); + static const Color base03 = Color(0xFF323738); + static const Color base04 = Color(0xFF3E3E3E); + static const Color base05 = Color(0xFF6E6E6E); + static const Color base06 = Color(0xFFC2C2C2); + static const Color base07 = Color(0xFFEAECED); + static const Color base08 = Color(0xFFFFFFFF); static const Color accentGreen = Color(0xFF00A79E); static const Color accentYellow = Color(0xFFFFAD3A); static const Color accentRed = Color(0xFFCC2E3D); diff --git a/lib/src/theme/painting_styles/bar_style.g.dart b/lib/src/theme/painting_styles/bar_style.g.dart index b314e12b1..fba93a9ae 100644 --- a/lib/src/theme/painting_styles/bar_style.g.dart +++ b/lib/src/theme/painting_styles/bar_style.g.dart @@ -6,14 +6,14 @@ part of 'bar_style.dart'; // JsonSerializableGenerator // ************************************************************************** -BarStyle _$BarStyleFromJson(Map json) { - return BarStyle( - positiveColor: - const ColorConverter().fromJson(json['positiveColor'] as int), - negativeColor: - const ColorConverter().fromJson(json['negativeColor'] as int), - ); -} +BarStyle _$BarStyleFromJson(Map json) => BarStyle( + positiveColor: json['positiveColor'] == null + ? const Color(0xFF4CAF50) + : const ColorConverter().fromJson(json['positiveColor'] as int), + negativeColor: json['negativeColor'] == null + ? const Color(0xFFCC2E3D) + : const ColorConverter().fromJson(json['negativeColor'] as int), + ); Map _$BarStyleToJson(BarStyle instance) => { 'positiveColor': const ColorConverter().toJson(instance.positiveColor), diff --git a/lib/src/theme/painting_styles/line_style.g.dart b/lib/src/theme/painting_styles/line_style.g.dart index 90ff5e8c0..11951739a 100644 --- a/lib/src/theme/painting_styles/line_style.g.dart +++ b/lib/src/theme/painting_styles/line_style.g.dart @@ -6,13 +6,13 @@ part of 'line_style.dart'; // JsonSerializableGenerator // ************************************************************************** -LineStyle _$LineStyleFromJson(Map json) { - return LineStyle( - color: const ColorConverter().fromJson(json['color'] as int), - thickness: (json['thickness'] as num).toDouble(), - hasArea: json['hasArea'] as bool, - ); -} +LineStyle _$LineStyleFromJson(Map json) => LineStyle( + color: json['color'] == null + ? const Color(0xFF85ACB0) + : const ColorConverter().fromJson(json['color'] as int), + thickness: (json['thickness'] as num?)?.toDouble() ?? 1, + hasArea: json['hasArea'] as bool? ?? false, + ); Map _$LineStyleToJson(LineStyle instance) => { 'color': const ColorConverter().toJson(instance.color), diff --git a/lib/src/theme/painting_styles/scatter_style.g.dart b/lib/src/theme/painting_styles/scatter_style.g.dart index 7875fdb7a..454e88b29 100644 --- a/lib/src/theme/painting_styles/scatter_style.g.dart +++ b/lib/src/theme/painting_styles/scatter_style.g.dart @@ -6,12 +6,12 @@ part of 'scatter_style.dart'; // JsonSerializableGenerator // ************************************************************************** -ScatterStyle _$ScatterStyleFromJson(Map json) { - return ScatterStyle( - color: const ColorConverter().fromJson(json['color'] as int), - radius: (json['radius'] as num).toDouble(), - ); -} +ScatterStyle _$ScatterStyleFromJson(Map json) => ScatterStyle( + color: json['color'] == null + ? const Color(0xFF85ACB0) + : const ColorConverter().fromJson(json['color'] as int), + radius: (json['radius'] as num?)?.toDouble() ?? 1.5, + ); Map _$ScatterStyleToJson(ScatterStyle instance) => { diff --git a/lib/src/widgets/bottom_indicator_title.dart b/lib/src/widgets/bottom_indicator_title.dart new file mode 100644 index 000000000..223751f9d --- /dev/null +++ b/lib/src/widgets/bottom_indicator_title.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +/// Widget that shows the bottom indicator title. +class BottomIndicatorTitle extends StatelessWidget { + /// Creates a widget that shows the title of the indicator. + const BottomIndicatorTitle(this.title, this.textStyle); + + /// The title of the indicator. + final String title; + + /// The style of the text. + final TextStyle textStyle; + + @override + Widget build(BuildContext context) => Text( + title, + style: textStyle, + ); +} diff --git a/test/deriv_chart/chart/helpers/functions/conversion_test.dart b/test/deriv_chart/chart/helpers/functions/conversion_test.dart index 2b310b3d5..18a388067 100644 --- a/test/deriv_chart/chart/helpers/functions/conversion_test.dart +++ b/test/deriv_chart/chart/helpers/functions/conversion_test.dart @@ -231,4 +231,68 @@ void main() { ); }); }); + + group('quoteFromCanvasY should return', () { + test('[topBoundQuote] when [y == topPadding]', () { + expect( + quoteFromCanvasY( + y: 0, + topBoundQuote: 1234.2345, + bottomBoundQuote: 123.439, + canvasHeight: 10033, + topPadding: 0, + bottomPadding: 133, + ), + equals(1234.2345), + ); + expect( + quoteFromCanvasY( + y: 1234.34, + topBoundQuote: 1234.2345, + bottomBoundQuote: 123.439, + canvasHeight: 10033, + topPadding: 1234.34, + bottomPadding: 133, + ), + equals(1234.2345), + ); + }); + test('[bottomBoundQuote] when [y == canvasHeight]', () { + expect( + quoteFromCanvasY( + y: 1024, + topBoundQuote: 102.2385, + bottomBoundQuote: 89.2345, + canvasHeight: 1024, + topPadding: 123, + bottomPadding: 0, + ), + equals(89.2345), + ); + expect( + quoteFromCanvasY( + y: 1024, + topBoundQuote: 102.2385, + bottomBoundQuote: 89.2345, + canvasHeight: 1024, + topPadding: 123, + bottomPadding: 0, + ), + equals(89.2345), + ); + }); + test('middle of drawing range', () { + expect( + quoteFromCanvasY( + y: 512, + topBoundQuote: 100, + bottomBoundQuote: 200, + canvasHeight: 1024, + topPadding: 12, + bottomPadding: 12, + ), + equals(150), + ); + }); + }); } From 7d4759582068886df68efce6588a835c04eada6d Mon Sep 17 00:00:00 2001 From: Bahar Date: Fri, 29 Sep 2023 15:44:10 +0800 Subject: [PATCH 19/29] bahar/Restore drawings (#242) - add restore drawing feature --- example/lib/main.dart | 1 + lib/deriv_chart.dart | 2 +- lib/src/add_ons/add_ons_repository.dart | 27 ++- .../channel/channel_drawing_tool_config.dart | 31 ++- .../channel_drawing_tool_config.g.dart | 11 ++ .../channel/channel_drawing_tool_item.dart | 1 - .../continuous_drawing_tool_config.dart | 28 ++- .../continuous_drawing_tool_config.g.dart | 11 ++ .../continuous_drawing_tool_item.dart | 2 +- .../drawing_tools_ui/drawing_tool_config.dart | 37 +++- .../drawing_tools_ui/drawing_tool_item.dart | 2 +- .../drawing_tools_dialog.dart | 17 +- .../fibfan/fibfan_drawing_tool_config.dart | 31 ++- .../fibfan/fibfan_drawing_tool_config.g.dart | 11 ++ .../fibfan/fibfan_drawing_tool_item.dart | 1 - .../horizontal_drawing_tool_config.dart | 30 ++- .../horizontal_drawing_tool_config.g.dart | 11 ++ .../horizontal_drawing_tool_item.dart | 1 - .../line/line_drawing_tool_config.dart | 28 ++- .../line/line_drawing_tool_config.g.dart | 11 ++ .../line/line_drawing_tool_item.dart | 2 +- .../ray/ray_drawing_tool_config.dart | 30 ++- .../ray/ray_drawing_tool_config.g.dart | 11 ++ .../ray/ray_drawing_tool_item.dart | 1 - .../rectangle_drawing_tool_config.dart | 31 ++- .../rectangle_drawing_tool_config.g.dart | 13 +- .../rectangle_drawing_tool_item.dart | 1 - .../trend/trend_drawing_tool_config.dart | 36 +++- .../trend/trend_drawing_tool_config.g.dart | 13 +- .../trend/trend_drawing_tool_item.dart | 1 - .../vertical_drawing_tool_config.dart | 30 ++- .../vertical_drawing_tool_config.g.dart | 11 ++ .../vertical/vertical_drawing_tool_item.dart | 2 +- .../cci_indicator_item.dart | 2 +- .../donchian_channel_indicator_config.dart | 2 +- .../ma_indicator/ma_indicator_item.dart | 2 +- .../rsi/rsi_indicator_config.dart | 2 +- .../indicators_ui/rsi/rsi_indicator_item.dart | 2 +- .../indicators_ui/smi/smi_indicator_item.dart | 2 +- .../zigzag_indicator_config.dart | 2 +- lib/src/add_ons/repository.dart | 10 +- lib/src/deriv_chart/chart/crosshair/find.dart | 37 ++++ .../bollinger_bands_series.dart | 10 +- .../donchian_channels_series.dart | 1 - .../fractals_series/arrow_painter.dart | 2 +- .../line_series/channel_fill_painter.dart | 2 +- .../line_series/line_painter.dart | 2 +- .../chart_series/series.dart | 5 +- .../channel/channel_drawing.dart | 57 ++++-- .../channel/channel_drawing.g.dart | 37 ++++ .../channel/channel_drawing_creator.dart | 7 +- .../continuous_drawing_creator.dart | 9 +- .../continuous/continuous_line_drawing.dart | 129 ++++++++++--- .../continuous/continuous_line_drawing.g.dart | 37 ++++ .../data_model/draggable_edge_point.dart | 17 ++ .../data_model/draggable_edge_point.g.dart | 23 +++ .../data_model/drawing_parts.dart | 4 + .../drawing_tools/data_model/edge_point.dart | 11 ++ .../data_model/edge_point.g.dart | 17 ++ .../drawing_tools/data_model/point.dart | 11 ++ .../drawing_tools/data_model/point.g.dart | 17 ++ .../drawing_tools/drawing.dart | 65 ++++++- .../drawing_tools/drawing_creator.dart | 1 + .../drawing_tools/drawing_data.dart | 36 ++-- .../drawing_tools/drawing_data.g.dart | 24 +++ .../drawing_tools/drawing_painter.dart | 90 +++++++-- .../drawing_tools/drawing_tool_widget.dart | 25 +-- .../drawing_tools/fibfan/fibfan_drawing.dart | 54 ++++-- .../fibfan/fibfan_drawing.g.dart | 35 ++++ .../fibfan/fibfan_drawing_creator.dart | 7 +- .../drawing_tools/fibfan/label.dart | 38 ++-- .../horizontal/horizontal_drawing.dart | 37 +++- .../horizontal/horizontal_drawing.g.dart | 32 ++++ .../horizontal_drawing_creator.dart | 9 +- .../drawing_tools/line/line_drawing.dart | 34 +++- .../drawing_tools/line/line_drawing.g.dart | 34 ++++ .../line/line_drawing_creator.dart | 9 +- .../ray/ray_drawing_creator.dart | 7 +- .../drawing_tools/ray/ray_line_drawing.dart | 97 +++++++--- .../drawing_tools/ray/ray_line_drawing.g.dart | 35 ++++ .../rectangle/rectangle_drawing.dart | 38 +++- .../rectangle/rectangle_drawing.g.dart | 39 ++++ .../rectangle/rectangle_drawing_creator.dart | 7 +- .../drawing_tools/trend/trend_drawing.dart | 176 ++++++++++++------ .../drawing_tools/trend/trend_drawing.g.dart | 36 ++++ .../trend/trend_drawing_creator.dart | 165 ++-------------- .../vertical/vertical_drawing.dart | 37 ++-- .../vertical/vertical_drawing.g.dart | 32 ++++ .../vertical/vertical_drawing_creator.dart | 2 +- .../markers/marker_area.dart | 2 +- lib/src/deriv_chart/deriv_chart.dart | 19 +- .../drawing_tool_chart.dart | 113 ++++++----- .../drawing_tool_chart/drawing_tools.dart | 85 +++++---- lib/src/misc/debounce.dart | 39 ++++ lib/src/models/chart_config.dart | 11 ++ lib/src/models/chart_config.g.dart | 18 ++ 96 files changed, 1856 insertions(+), 567 deletions(-) create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.g.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.g.dart create mode 100644 lib/src/misc/debounce.dart create mode 100644 lib/src/models/chart_config.g.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 275107208..a905b8c21 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -414,6 +414,7 @@ class _FullscreenChartState extends State { activeMarker: _activeMarker, markerIconPainter: MultipliersMarkerIconPainter(), ), + activeSymbol: _symbol.name, annotations: ticks.length > 4 ? >[ ..._sampleBarriers, diff --git a/lib/deriv_chart.dart b/lib/deriv_chart.dart index e093cb542..9edf786d2 100644 --- a/lib/deriv_chart.dart +++ b/lib/deriv_chart.dart @@ -58,7 +58,6 @@ export 'src/deriv_chart/chart/helpers/paint_functions/paint_text.dart'; export 'src/deriv_chart/chart/helpers/paint_functions/paint_line.dart'; export 'src/deriv_chart/chart/worm_chart/worm_chart.dart'; export 'src/deriv_chart/chart/x_axis/min_candle_duration_for_data_fit.dart'; -export 'src/deriv_chart/deriv_chart.dart'; export 'src/misc/chart_controller.dart'; export 'src/misc/callbacks.dart'; export 'src/models/candle.dart'; @@ -90,6 +89,7 @@ export 'src/add_ons/indicators_ui/roc/roc_indicator_config.dart'; export 'src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart'; export 'src/add_ons/indicators_ui/smi/smi_indicator_config.dart'; export 'src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.dart'; +export 'src/deriv_chart/deriv_chart.dart'; export 'src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.dart'; export 'src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart'; export 'src/add_ons/indicators_ui/indicator_config.dart'; diff --git a/lib/src/add_ons/add_ons_repository.dart b/lib/src/add_ons/add_ons_repository.dart index 1ca1c6b70..8903ed0f5 100644 --- a/lib/src/add_ons/add_ons_repository.dart +++ b/lib/src/add_ons/add_ons_repository.dart @@ -13,8 +13,14 @@ typedef CreateAddOn = T Function( class AddOnsRepository extends ChangeNotifier implements Repository { /// Initializes - AddOnsRepository({required this.createAddOn, this.onEditCallback}) - : _addOns = []; + AddOnsRepository({ + required this.createAddOn, + required this.currentSymbol, + this.onEditCallback, + }) : _addOns = []; + + /// Current symbol. + String currentSymbol; /// List containing addOns final List _addOns; @@ -25,7 +31,7 @@ class AddOnsRepository extends ChangeNotifier List get items => _addOns; /// Storage key of saved indicators/drawing tools. - String get addOnsKey => 'addOns_${T.toString()}'; + String get addOnsKey => 'addOns_${T.toString()}_$currentSymbol'; /// Called to create an AddOnConfig object from a map. CreateAddOn createAddOn; @@ -34,8 +40,11 @@ class AddOnsRepository extends ChangeNotifier VoidCallback? onEditCallback; /// Loads user selected indicators or drawing tools from shared preferences. - void loadFromPrefs(SharedPreferences prefs) { + void loadFromPrefs(SharedPreferences prefs, String symbol) { _prefs = prefs; + currentSymbol = symbol; + + items.clear(); if (!prefs.containsKey(addOnsKey)) { // No saved indicators or drawing tools. @@ -43,10 +52,14 @@ class AddOnsRepository extends ChangeNotifier } final List encodedAddOns = prefs.getStringList(addOnsKey)!; - items.clear(); - for (final String encodedAddOn in encodedAddOns) { - final T addOnConfig = createAddOn.call(jsonDecode(encodedAddOn)); + final List> decodedAddons = encodedAddOns + .map>( + (String encodedAddOn) => jsonDecode(encodedAddOn)) + .toList(); + + for (final Map decodedAddon in decodedAddons) { + final T addOnConfig = createAddOn.call(decodedAddon); items.add(addOnConfig); } } diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart index 9fbb20b95..f9cc34502 100644 --- a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart @@ -1,8 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'channel_drawing_tool_item.dart'; @@ -14,10 +16,17 @@ part 'channel_drawing_tool_config.g.dart'; class ChannelDrawingToolConfig extends DrawingToolConfig { /// Initializes const ChannelDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory ChannelDrawingToolConfig.fromJson(Map json) => @@ -50,4 +59,22 @@ class ChannelDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + ChannelDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + ChannelDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + fillStyle: fillStyle ?? this.fillStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart index 1632d4e76..8345da9fe 100644 --- a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'channel_drawing_tool_config.dart'; ChannelDrawingToolConfig _$ChannelDrawingToolConfigFromJson( Map json) => ChannelDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], fillStyle: json['fillStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.blue) : LineStyle.fromJson(json['fillStyle'] as Map), @@ -22,6 +30,9 @@ ChannelDrawingToolConfig _$ChannelDrawingToolConfigFromJson( Map _$ChannelDrawingToolConfigToJson( ChannelDrawingToolConfig instance) => { + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'fillStyle': instance.fillStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart index a5858c80c..2a7c13d5c 100644 --- a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; import 'channel_drawing_tool_config.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; /// Channel drawing tool item in the list of drawing tools class ChannelDrawingToolItem extends DrawingToolItem { diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart index f5b85da47..8a5d89413 100644 --- a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart @@ -2,6 +2,8 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dar import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -14,9 +16,16 @@ part 'continuous_drawing_tool_config.g.dart'; class ContinuousDrawingToolConfig extends DrawingToolConfig { /// Initializes const ContinuousDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory ContinuousDrawingToolConfig.fromJson(Map json) => @@ -46,4 +55,21 @@ class ContinuousDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + ContinuousDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + ContinuousDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart index 85e73ac68..20d0dc6ad 100644 --- a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'continuous_drawing_tool_config.dart'; ContinuousDrawingToolConfig _$ContinuousDrawingToolConfigFromJson( Map json) => ContinuousDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], lineStyle: json['lineStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.white) : LineStyle.fromJson(json['lineStyle'] as Map), @@ -19,6 +27,9 @@ ContinuousDrawingToolConfig _$ContinuousDrawingToolConfigFromJson( Map _$ContinuousDrawingToolConfigToJson( ContinuousDrawingToolConfig instance) => { + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart index c201c1c3a..fe2055221 100644 --- a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_item.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -6,7 +7,6 @@ import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'continuous_drawing_tool_config.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; /// Continuous drawing tool item in the list of drawing tools class ContinuousDrawingToolItem extends DrawingToolItem { diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index 77ac2c2da..ba766664a 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -3,6 +3,10 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; @@ -17,8 +21,13 @@ import 'vertical/vertical_drawing_tool_config.dart'; @immutable abstract class DrawingToolConfig extends AddOnConfig { /// Initializes - const DrawingToolConfig({bool isOverlay = true}) - : super(isOverlay: isOverlay); + const DrawingToolConfig({ + required this.configId, + required this.drawingData, + // TODO(Bahar-Deriv): Move edgePoints to drawingData. + required this.edgePoints, + bool isOverlay = true, + }) : super(isOverlay: isOverlay); /// Creates a concrete drawing tool config from JSON. factory DrawingToolConfig.fromJson(Map json) { @@ -32,7 +41,7 @@ abstract class DrawingToolConfig extends AddOnConfig { case ContinuousDrawingToolConfig.name: return ContinuousDrawingToolConfig.fromJson(json); case FibfanDrawingToolConfig.name: - return FibfanDrawingToolConfig.fromJson(json); + return FibfanDrawingToolConfig.fromJson(json); case HorizontalDrawingToolConfig.name: return HorizontalDrawingToolConfig.fromJson(json); case LineDrawingToolConfig.name: @@ -53,9 +62,31 @@ abstract class DrawingToolConfig extends AddOnConfig { } } + /// Drawing tool data. + final DrawingData? drawingData; + + /// Drawing tool edge points. + final List edgePoints; + + /// Drawing tool config id. + final String? configId; + /// Key of drawing tool name property in JSON. static const String nameKey = 'name'; + /// Key of drawing tool config id property in JSON. + static String configIdKey = 'configId'; + + /// Creates a copy of this object. + DrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }); + /// Creates drawing tool. DrawingToolItem getItem( UpdateDrawingTool updateDrawingTool, diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart index 03d2f357a..dd0896391 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_item.dart @@ -1,6 +1,6 @@ +import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:deriv_chart/src/add_ons/repository.dart'; import 'callbacks.dart'; import 'drawing_tool_config.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart index 9738ec50e..5e76ef902 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart @@ -86,7 +86,7 @@ class _DrawingToolsDialogState extends State { ) // TODO(maryia-binary): add the rest of drawing tools above ], - onChanged: (dynamic config) { + onChanged: (DrawingToolConfig? config) { setState(() { _selectedDrawingTool = config; }); @@ -95,8 +95,7 @@ class _DrawingToolsDialogState extends State { const SizedBox(width: 16), ElevatedButton( child: const Text('Add'), - onPressed: _selectedDrawingTool != null && - _selectedDrawingTool is DrawingToolConfig + onPressed: _selectedDrawingTool != null ? () { widget.drawingTools .onDrawingToolSelection(_selectedDrawingTool!); @@ -113,11 +112,17 @@ class _DrawingToolsDialogState extends State { itemBuilder: (BuildContext context, int index) => repo.items[index].getItem( (DrawingToolConfig updatedConfig) { - widget.drawingTools.onDrawingToolUpdate(index, updatedConfig); - repo.updateAt(index, updatedConfig); + DrawingToolConfig config = updatedConfig; + + config = config.copyWith( + configId: repo.items[index].configId, + edgePoints: repo.items[index].edgePoints, + drawingData: repo.items[index].drawingData, + ); + + repo.updateAt(index, config); }, () { - widget.drawingTools.onDrawingToolRemoval(index); repo.removeAt(index); }, ), diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart index d50c28003..fab44509e 100644 --- a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart @@ -1,7 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'fibfan_drawing_tool_item.dart'; @@ -13,9 +16,16 @@ part 'fibfan_drawing_tool_config.g.dart'; class FibfanDrawingToolConfig extends DrawingToolConfig { /// Initializes const FibfanDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory FibfanDrawingToolConfig.fromJson(Map json) => @@ -44,4 +54,21 @@ class FibfanDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + FibfanDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + FibfanDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + fillStyle: fillStyle ?? this.fillStyle, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart index d32fba2cd..24fefc37e 100644 --- a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'fibfan_drawing_tool_config.dart'; FibfanDrawingToolConfig _$FibfanDrawingToolConfigFromJson( Map json) => FibfanDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], fillStyle: json['fillStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.blue) : LineStyle.fromJson(json['fillStyle'] as Map), @@ -20,6 +28,9 @@ FibfanDrawingToolConfig _$FibfanDrawingToolConfigFromJson( Map _$FibfanDrawingToolConfigToJson( FibfanDrawingToolConfig instance) => { + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'fillStyle': instance.fillStyle, }; diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart index 0ec379807..807a2d9c1 100644 --- a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart @@ -4,7 +4,6 @@ import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dar import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; /// Fibfan drawing tool item in the list of drawing tools class FibfanDrawingToolItem extends DrawingToolItem { diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart index d93bab3d0..27856eb9c 100644 --- a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart @@ -1,8 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -15,9 +17,16 @@ part 'horizontal_drawing_tool_config.g.dart'; class HorizontalDrawingToolConfig extends DrawingToolConfig { /// Initializes const HorizontalDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory HorizontalDrawingToolConfig.fromJson(Map json) => @@ -46,4 +55,21 @@ class HorizontalDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + HorizontalDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + HorizontalDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart index 32a704f16..50006a706 100644 --- a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'horizontal_drawing_tool_config.dart'; HorizontalDrawingToolConfig _$HorizontalDrawingToolConfigFromJson( Map json) => HorizontalDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], lineStyle: json['lineStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.white) : LineStyle.fromJson(json['lineStyle'] as Map), @@ -19,6 +27,9 @@ HorizontalDrawingToolConfig _$HorizontalDrawingToolConfigFromJson( Map _$HorizontalDrawingToolConfigToJson( HorizontalDrawingToolConfig instance) => { + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart index 5cbb7ee55..0d9a33dd9 100644 --- a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart @@ -6,7 +6,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:flutter/material.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; import '../drawing_tool_item.dart'; /// Horizontal drawing tool item in the list of drawing tool which provide this diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart index 3d9a4b68c..974af69aa 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart @@ -2,6 +2,8 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dar import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -14,9 +16,16 @@ part 'line_drawing_tool_config.g.dart'; class LineDrawingToolConfig extends DrawingToolConfig { /// Initializes const LineDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory LineDrawingToolConfig.fromJson(Map json) => @@ -46,4 +55,21 @@ class LineDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + LineDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + LineDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart index 2d57e2366..b6d91581d 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'line_drawing_tool_config.dart'; LineDrawingToolConfig _$LineDrawingToolConfigFromJson( Map json) => LineDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], lineStyle: json['lineStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.white) : LineStyle.fromJson(json['lineStyle'] as Map), @@ -19,6 +27,9 @@ LineDrawingToolConfig _$LineDrawingToolConfigFromJson( Map _$LineDrawingToolConfigToJson( LineDrawingToolConfig instance) => { + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart index 8244e5e73..d4524ba11 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_item.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -6,7 +7,6 @@ import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'line_drawing_tool_config.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; /// Line drawing tool item in the list of drawing tools class LineDrawingToolItem extends DrawingToolItem { diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart index 37dfe9d5e..302774c9e 100644 --- a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart @@ -1,8 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/callbacks.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'ray_drawing_tool_item.dart'; @@ -14,9 +16,16 @@ part 'ray_drawing_tool_config.g.dart'; class RayDrawingToolConfig extends DrawingToolConfig { /// Initializes const RayDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory RayDrawingToolConfig.fromJson(Map json) => @@ -46,4 +55,21 @@ class RayDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + RayDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + RayDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart index 6a9a149e9..796ec554d 100644 --- a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'ray_drawing_tool_config.dart'; RayDrawingToolConfig _$RayDrawingToolConfigFromJson( Map json) => RayDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], lineStyle: json['lineStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.white) : LineStyle.fromJson(json['lineStyle'] as Map), @@ -19,6 +27,9 @@ RayDrawingToolConfig _$RayDrawingToolConfigFromJson( Map _$RayDrawingToolConfigToJson( RayDrawingToolConfig instance) => { + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart index 7c9eac23f..52c55514a 100644 --- a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:deriv_chart/deriv_chart.dart'; import 'ray_drawing_tool_config.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; /// Ray drawing tool item in the list of drawing tools class RayDrawingToolItem extends DrawingToolItem { diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart index dbc4ecb15..889ea98cb 100644 --- a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart @@ -1,8 +1,10 @@ -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -15,10 +17,17 @@ part 'rectangle_drawing_tool_config.g.dart'; class RectangleDrawingToolConfig extends DrawingToolConfig { /// Initializes const RectangleDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory RectangleDrawingToolConfig.fromJson(Map json) => @@ -50,4 +59,22 @@ class RectangleDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + RectangleDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + RectangleDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + fillStyle: fillStyle ?? this.fillStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart index ad1651f15..e2b9397e6 100644 --- a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'rectangle_drawing_tool_config.dart'; RectangleDrawingToolConfig _$RectangleDrawingToolConfigFromJson( Map json) => RectangleDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], fillStyle: json['fillStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.blue) : LineStyle.fromJson(json['fillStyle'] as Map), @@ -22,8 +30,11 @@ RectangleDrawingToolConfig _$RectangleDrawingToolConfigFromJson( Map _$RectangleDrawingToolConfigToJson( RectangleDrawingToolConfig instance) => { - 'fillStyle': instance.fillStyle, + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, + 'fillStyle': instance.fillStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart index 99bc3ca47..22478aff7 100644 --- a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart @@ -6,7 +6,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:flutter/material.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; import '../drawing_tool_item.dart'; /// Rectangle drawing tool item in the list of drawing tool which provide this diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart index 02ff9fbe3..e8ab05dc8 100644 --- a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart @@ -1,8 +1,9 @@ import 'package:deriv_chart/deriv_chart.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -15,10 +16,17 @@ part 'trend_drawing_tool_config.g.dart'; class TrendDrawingToolConfig extends DrawingToolConfig { /// Initializes const TrendDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.fillStyle = const LineStyle(thickness: 0.9, color: Colors.blue), this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory TrendDrawingToolConfig.fromJson(Map json) => @@ -31,12 +39,12 @@ class TrendDrawingToolConfig extends DrawingToolConfig { Map toJson() => _$TrendDrawingToolConfigToJson(this) ..putIfAbsent(DrawingToolConfig.nameKey, () => name); - /// Drawing tool line style - final LineStyle lineStyle; - /// Drawing tool fill style final LineStyle fillStyle; + /// Drawing tool line style + final LineStyle lineStyle; + /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' final DrawingPatterns pattern; @@ -50,4 +58,22 @@ class TrendDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + TrendDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? fillStyle, + LineStyle? lineStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + TrendDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + fillStyle: fillStyle ?? this.fillStyle, + lineStyle: lineStyle ?? this.lineStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart index 5d334eaed..721194fe3 100644 --- a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'trend_drawing_tool_config.dart'; TrendDrawingToolConfig _$TrendDrawingToolConfigFromJson( Map json) => TrendDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], fillStyle: json['fillStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.blue) : LineStyle.fromJson(json['fillStyle'] as Map), @@ -22,8 +30,11 @@ TrendDrawingToolConfig _$TrendDrawingToolConfigFromJson( Map _$TrendDrawingToolConfigToJson( TrendDrawingToolConfig instance) => { - 'lineStyle': instance.lineStyle, + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'fillStyle': instance.fillStyle, + 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart index 7e4608698..e93d87652 100644 --- a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart @@ -6,7 +6,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:flutter/material.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; import '../drawing_tool_item.dart'; /// Trend drawing tool item in the list of drawing tool which provide this diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart index 6545e328e..0efb56461 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart @@ -1,7 +1,9 @@ -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -15,9 +17,16 @@ part 'vertical_drawing_tool_config.g.dart'; class VerticalDrawingToolConfig extends DrawingToolConfig { /// Initializes const VerticalDrawingToolConfig({ + String? configId, + DrawingData? drawingData, + List edgePoints = const [], this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, - }) : super(); + }) : super( + configId: configId, + drawingData: drawingData, + edgePoints: edgePoints, + ); /// Initializes from JSON. factory VerticalDrawingToolConfig.fromJson(Map json) => @@ -46,4 +55,21 @@ class VerticalDrawingToolConfig extends DrawingToolConfig { updateDrawingTool: updateDrawingTool, deleteDrawingTool: deleteDrawingTool, ); + + @override + VerticalDrawingToolConfig copyWith({ + String? configId, + DrawingData? drawingData, + LineStyle? lineStyle, + LineStyle? fillStyle, + DrawingPatterns? pattern, + List? edgePoints, + }) => + VerticalDrawingToolConfig( + configId: configId ?? this.configId, + drawingData: drawingData ?? this.drawingData, + lineStyle: lineStyle ?? this.lineStyle, + pattern: pattern ?? this.pattern, + edgePoints: edgePoints ?? this.edgePoints, + ); } diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart index 10933b01c..0b2dbb75c 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart @@ -9,6 +9,14 @@ part of 'vertical_drawing_tool_config.dart'; VerticalDrawingToolConfig _$VerticalDrawingToolConfigFromJson( Map json) => VerticalDrawingToolConfig( + configId: json['configId'] as String?, + drawingData: json['drawingData'] == null + ? null + : DrawingData.fromJson(json['drawingData'] as Map), + edgePoints: (json['edgePoints'] as List?) + ?.map((e) => EdgePoint.fromJson(e as Map)) + .toList() ?? + const [], lineStyle: json['lineStyle'] == null ? const LineStyle(thickness: 0.9, color: Colors.white) : LineStyle.fromJson(json['lineStyle'] as Map), @@ -19,6 +27,9 @@ VerticalDrawingToolConfig _$VerticalDrawingToolConfigFromJson( Map _$VerticalDrawingToolConfigToJson( VerticalDrawingToolConfig instance) => { + 'drawingData': instance.drawingData, + 'edgePoints': instance.edgePoints, + 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, }; diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart index 1da592498..f94f22232 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -7,7 +8,6 @@ import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; -import '../drawing_tool_config.dart'; import '../drawing_tool_item.dart'; /// Vertical drawing tool item in the list of drawing tool which provide this diff --git a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart index 24bf55534..8a1100424 100644 --- a/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/commodity_channel_index/cci_indicator_item.dart @@ -3,11 +3,11 @@ import 'package:deriv_chart/src/add_ons/indicators_ui/oscillator_lines/oscillato import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/field_widget.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/oscillator_limit.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; -import '../indicator_config.dart'; import '../indicator_item.dart'; import 'cci_indicator_config.dart'; diff --git a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart index 51a261bee..bfd877eb6 100644 --- a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/src/add_ons/indicators_ui/callbacks.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/color_converter.dart'; @@ -9,7 +10,6 @@ import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; -import '../indicator_config.dart'; import '../indicator_item.dart'; import 'donchian_channel_indicator_item.dart'; diff --git a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart index 3a01521b5..356ebfb77 100644 --- a/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart @@ -1,11 +1,11 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; -import '../indicator_config.dart'; import '../indicator_item.dart'; import 'ma_indicator_config.dart'; diff --git a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart index a4c95f535..604ff3add 100644 --- a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/rsi_options.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/rsi_series.dart'; @@ -8,7 +9,6 @@ import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import '../callbacks.dart'; -import '../indicator_config.dart'; import '../indicator_item.dart'; import 'rsi_indicator_item.dart'; diff --git a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart index aae4ab987..622914c9c 100644 --- a/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/rsi/rsi_indicator_item.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/oscillator_lines/oscillator_lines_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/oscillator_limit.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; @@ -6,7 +7,6 @@ import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; -import '../indicator_config.dart'; import '../indicator_item.dart'; import 'rsi_indicator_config.dart'; diff --git a/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart b/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart index 6e86eb795..f7fb67f17 100644 --- a/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/dropdown_menu.dart' as deriv_dropdown; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/field_widget.dart'; @@ -7,7 +8,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_serie import 'package:flutter/material.dart' hide DropdownMenu; import '../callbacks.dart'; -import '../indicator_config.dart'; import '../indicator_item.dart'; import 'smi_indicator_config.dart'; diff --git a/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart b/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart index 1e77784fc..99592d0be 100644 --- a/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart +++ b/lib/src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/zigzag_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; @@ -6,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import '../callbacks.dart'; -import '../indicator_config.dart'; import '../indicator_item.dart'; import 'zigzag_indicator_item.dart'; diff --git a/lib/src/add_ons/repository.dart b/lib/src/add_ons/repository.dart index 626031fc8..fa5355232 100644 --- a/lib/src/add_ons/repository.dart +++ b/lib/src/add_ons/repository.dart @@ -8,10 +8,16 @@ abstract class Repository extends ChangeNotifier { /// To adds a new indicator or drawing tool. void add(T config); - /// To edit an indicator or drawing tool at [index]. + /// Edits an existing indicator or drawing tool at the specified [index]. + /// This method allows you to modify the settings and properties of the + /// indicator or tool + /// without changing its position in the list. void editAt(int index); - /// To update an indicator or drawing tool at [index]. + /// Updates an existing indicator or drawing tool at the + /// specified [index] with new configuration settings. + /// This method allows you to modify the indicator or tool's properties + /// while preserving its position in the list. void updateAt(int index, T config); /// Removes indicator or drawing tool at [index]. diff --git a/lib/src/deriv_chart/chart/crosshair/find.dart b/lib/src/deriv_chart/chart/crosshair/find.dart index 3f02ce1c2..b5f84e4ce 100644 --- a/lib/src/deriv_chart/chart/crosshair/find.dart +++ b/lib/src/deriv_chart/chart/crosshair/find.dart @@ -39,6 +39,43 @@ int findClosestIndex(double index, List ticks) { } } +/// Binary search to find closest index to the [epoch]. +int findClosestIndexBinarySearch(int epoch, List? entries) { + if (entries == null || entries.isEmpty) { + return 0; + } + + int lo = 0; + int hi = entries.length - 1; + int localEpoch = epoch; + + if (localEpoch > entries[hi].epoch) { + localEpoch = entries[hi].epoch; + return hi; + } + + while (lo <= hi) { + final int mid = (hi + lo) ~/ 2; + // int getEpochOf(T t, int index) => t.epoch; + if (localEpoch < entries[mid].epoch) { + hi = mid - 1; + } else if (localEpoch > entries[mid].epoch) { + lo = mid + 1; + } else { + return mid; + } + } + + // Check if hi is less than 0 + if (hi < 0) { + return lo; // or another appropriate value to indicate the closest index + } + + return (entries[lo].epoch - localEpoch) < (localEpoch - entries[hi].epoch) + ? lo + : hi; +} + /// Returns index of the [epoch] location in [ticks]. /// /// E.g. `3` if [epoch] matches epoch of `ticks[3]`. diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart index db1200a73..bb9b138f4 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart @@ -1,5 +1,10 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/indicator.dart'; @@ -10,11 +15,6 @@ import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter/material.dart'; -import '../../chart_data.dart'; -import '../data_series.dart'; -import '../series.dart'; -import '../series_painter.dart'; -import 'ma_series.dart'; import 'models/bollinger_bands_options.dart'; import 'single_indicator_series.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart index dae29e3b6..b21b4e06e 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/donchian_channels_series.dart @@ -1,4 +1,3 @@ - import 'package:deriv_chart/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart index dd5769b76..b625b781d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart @@ -1,10 +1,10 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/create_shape_path.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import '../../../chart_data.dart'; import '../../data_painter.dart'; import '../../data_series.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart index dae22b0cb..283af44f2 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart @@ -1,12 +1,12 @@ import 'dart:ui' as ui; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import '../../chart_data.dart'; import '../data_painter.dart'; import '../data_series.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart index 7e41671ef..3d9e54af3 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart @@ -1,11 +1,11 @@ import 'dart:ui' as ui; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import '../../chart_data.dart'; import '../data_painter.dart'; import '../data_series.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart index e82d31f98..b5c2c796f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/series.dart @@ -1,12 +1,11 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_chart/src/theme/painting_styles/chart_painting_style.dart'; import 'package:flutter/material.dart'; -import '../chart_data.dart'; -import 'series_painter.dart'; - /// Base class of all chart series. abstract class Series implements ChartData { /// Initializes a base class of all chart series. diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart index e2145f31e..200a3f9f1 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; @@ -10,11 +11,17 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'channel_drawing.g.dart'; /// Channel drawing tool. A channel is 2 parallel lines that /// created with 3 points. +@JsonSerializable() class ChannelDrawing extends Drawing with LineVectorDrawingMixin { /// Initializes ChannelDrawing({ @@ -25,6 +32,17 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { this.isDrawingFinished = false, }); + /// Initializes from JSON. + factory ChannelDrawing.fromJson(Map json) => + _$ChannelDrawingFromJson(json); + + @override + Map toJson() => _$ChannelDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'ChannelDrawing'; + /// Part of a drawing: 'marker' or 'line' final DrawingParts drawingPart; @@ -86,9 +104,13 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -100,16 +122,29 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { final DrawingPaintStyle paint = DrawingPaintStyle(); /// Get the latest config of any drawing tool which is used to draw the line - final ChannelDrawingToolConfig config = - drawingData.config as ChannelDrawingToolConfig; + config as ChannelDrawingToolConfig; final LineStyle lineStyle = config.lineStyle; final DrawingPatterns pattern = config.pattern; - - _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); - _middlePoint = - updatePositionCallback(middleEdgePoint, draggableMiddlePoint!); - _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + final List edgePoints = config.edgePoints; + + /// Since we want to draw a marker on the screen base on the user click, + /// we need to call the onPaint function each time any record added to + /// edgePoints list, so we need to check the edgePoints list lenght to + /// know which marker should be drawn + _startPoint = updatePositionCallback(edgePoints.first, draggableStartPoint); + if (edgePoints.length > 1) { + _middlePoint = + updatePositionCallback(edgePoints[1], draggableMiddlePoint!); + } else { + _middlePoint = + updatePositionCallback(middleEdgePoint, draggableMiddlePoint!); + } + if (edgePoints.length > 2) { + _endPoint = updatePositionCallback(edgePoints.last, draggableEndPoint!); + } else { + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + } final double startXCoord = _startPoint!.x; final double startQuoteToY = _startPoint!.y; @@ -165,7 +200,6 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { } else if (drawingPart == DrawingParts.line) { if (endEdgePoint.epoch != 0 && endQuoteToY != 0) { /// Draw second line - drawParallelogram( canvas, config, @@ -174,8 +208,8 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { _finalVector, ); if (pattern == DrawingPatterns.solid) { - /// Drawing the markers in the final spte again to hide the overlap - /// of fill coler and the markers + /// Drawing the markers in the final step again to hide the overlap + /// of fill color and the markers canvas ..drawCircle( Offset(startXCoord, startQuoteToY), @@ -293,6 +327,7 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => true; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.g.dart new file mode 100644 index 000000000..0eb3acdd3 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.g.dart @@ -0,0 +1,37 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'channel_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ChannelDrawing _$ChannelDrawingFromJson(Map json) => + ChannelDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + startEdgePoint: json['startEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['startEdgePoint'] as Map), + middleEdgePoint: json['middleEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['middleEdgePoint'] as Map), + endEdgePoint: json['endEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['endEdgePoint'] as Map), + isDrawingFinished: json['isDrawingFinished'] as bool? ?? false, + ); + +Map _$ChannelDrawingToJson(ChannelDrawing instance) => + { + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'startEdgePoint': instance.startEdgePoint, + 'middleEdgePoint': instance.middleEdgePoint, + 'endEdgePoint': instance.endEdgePoint, + 'isDrawingFinished': instance.isDrawingFinished, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart index 54ab510f2..dd942f7f7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart @@ -13,7 +13,7 @@ class ChannelDrawingCreator extends DrawingCreator { required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, required this.shouldStopDrawing, Key? key, }) : super( @@ -26,7 +26,7 @@ class ChannelDrawingCreator extends DrawingCreator { final VoidCallback clearDrawingToolSelection; /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + final VoidCallback removeUnfinishedDrawing; /// A flag to show when to stop drawing only for drawings which don't have /// fixed number of points like continuous drawing @@ -71,7 +71,7 @@ class _ChannelDrawingCreatorState extends DrawingCreatorState { if (edgePoints[1] == edgePoints.first) { /// If the initial point and the 2nd point are the same, /// remove the drawing and clean the drawing tool selection. - _widget.removeDrawing(drawingId); + _widget.removeUnfinishedDrawing(); _widget.clearDrawingToolSelection(); return; } else { @@ -118,6 +118,7 @@ class _ChannelDrawingCreatorState extends DrawingCreatorState { drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart index 369d120a6..844b778fc 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart @@ -13,7 +13,7 @@ class ContinuousDrawingCreator extends DrawingCreator { required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, required this.shouldStopDrawing, Key? key, }) : super( @@ -25,8 +25,8 @@ class ContinuousDrawingCreator extends DrawingCreator { /// Callback to clean drawing tool selection. final VoidCallback clearDrawingToolSelection; - /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + /// Callback to remove unfinished drawing from the list of drawings. + final VoidCallback removeUnfinishedDrawing; /// A flag to show when to stop drawing only for drawings which don't have /// fixed number of points like continuous drawing @@ -80,7 +80,7 @@ class _ContinuousDrawingCreatorState if (edgePoints[1] == edgePoints.first) { /// If the initial point and the 2nd point are the same, /// remove the drawing and clean the drawing tool selection. - _widget.removeDrawing(drawingId); + _widget.removeUnfinishedDrawing(); _widget.clearDrawingToolSelection(); return; } else { @@ -113,6 +113,7 @@ class _ContinuousDrawingCreatorState drawingParts, isDrawingFinished: isDrawingFinished, isInfiniteDrawing: true, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart index 78b12fb9b..bb3b53a28 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -9,26 +10,58 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'continuous_line_drawing.g.dart'; /// Line drawing tool. A line is a vector defined by two points that is /// infinite in both directions. +@JsonSerializable() class ContinuousLineDrawing extends Drawing { /// Initializes ContinuousLineDrawing({ - required DrawingParts drawingPart, - EdgePoint startEdgePoint = const EdgePoint(), - EdgePoint endEdgePoint = const EdgePoint(), - bool exceedStart = false, - bool exceedEnd = false, + required this.drawingPart, + this.startEdgePoint = const EdgePoint(), + this.endEdgePoint = const EdgePoint(), + this.exceedStart = false, + this.exceedEnd = false, }) : _lineDrawing = LineDrawing( - drawingPart: drawingPart, - startEdgePoint: startEdgePoint, - endEdgePoint: endEdgePoint, - exceedStart: exceedStart, - exceedEnd: exceedEnd); + drawingPart: drawingPart, + startEdgePoint: startEdgePoint, + endEdgePoint: endEdgePoint, + exceedStart: exceedStart, + exceedEnd: exceedEnd, + ); + + /// Initializes from JSON. + factory ContinuousLineDrawing.fromJson(Map json) => + _$ContinuousLineDrawingFromJson(json); + + @override + Map toJson() => _$ContinuousLineDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Drawing part. + final DrawingParts drawingPart; + + /// Start edge point. + final EdgePoint startEdgePoint; + + /// End edge point. + final EdgePoint endEdgePoint; + + /// Whether the start point is exceeded. + final bool exceedStart; + + /// Whether the end point is exceeded. + final bool exceedEnd; + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'ContinuousLineDrawing'; final LineDrawing _lineDrawing; @@ -37,6 +70,7 @@ class ContinuousLineDrawing extends Drawing { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => _lineDrawing.needsRepaint( @@ -52,9 +86,13 @@ class ContinuousLineDrawing extends Drawing { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -63,28 +101,75 @@ class ContinuousLineDrawing extends Drawing { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { - final ContinuousDrawingToolConfig config = - drawingData.config as ContinuousDrawingToolConfig; + config as ContinuousDrawingToolConfig; - final LineStyle lineStyle = config.lineStyle; - final DrawingPatterns pattern = config.pattern; + final DrawingData lineDrawingData = DrawingData( + id: drawingData.id, + drawingParts: drawingData.drawingParts, + isDrawingFinished: drawingData.isDrawingFinished, + isSelected: drawingData.isSelected, + ); - _lineDrawing.onPaint( + /// Draw first line of the continuous drawing which need 2 taps to draw + if (config.edgePoints.length <= 2) { + _lineDrawing.onPaint( canvas, size, theme, + epochFromX, + quoteFromY, epochToX, quoteToY, - DrawingData( - id: drawingData.id, - config: LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), - drawingParts: drawingData.drawingParts, - isDrawingFinished: drawingData.isDrawingFinished, - isSelected: drawingData.isSelected, + LineDrawingToolConfig( + configId: config.configId, + drawingData: config.drawingData, + lineStyle: config.lineStyle, + pattern: config.pattern, + edgePoints: config.edgePoints, ), + lineDrawingData, + series, updatePositionCallback, draggableStartPoint, - draggableEndPoint: draggableEndPoint); + draggableEndPoint: draggableEndPoint, + ); + } + + /// Draw other lines of continuous which need more than 1 tap to draw + if (config.edgePoints.length > 2) { + config.edgePoints + .where((EdgePoint element) => + config.edgePoints.indexOf(element) > 1 && + config.edgePoints.indexOf(element) == + config.edgePoints.length - 1) + .forEach((EdgePoint edgePoint) { + final int index = config.edgePoints.indexOf(edgePoint); + _lineDrawing.onPaint( + canvas, + size, + theme, + epochFromX, + quoteFromY, + epochToX, + quoteToY, + LineDrawingToolConfig( + configId: config.configId, + drawingData: config.drawingData, + lineStyle: config.lineStyle, + pattern: config.pattern, + + /// Limit the edge points to only 2 points, since line drawing + /// needs only 2 points + edgePoints: [config.edgePoints[index - 1], edgePoint], + ), + lineDrawingData, + series, + updatePositionCallback, + draggableStartPoint, + draggableEndPoint: draggableEndPoint, + ); + }); + } } /// Calculation for detemining whether a user's touch or click intersects diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.g.dart new file mode 100644 index 000000000..871825eb2 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.g.dart @@ -0,0 +1,37 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'continuous_line_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ContinuousLineDrawing _$ContinuousLineDrawingFromJson( + Map json) => + ContinuousLineDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + startEdgePoint: json['startEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['startEdgePoint'] as Map), + endEdgePoint: json['endEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['endEdgePoint'] as Map), + exceedStart: json['exceedStart'] as bool? ?? false, + exceedEnd: json['exceedEnd'] as bool? ?? false, + ); + +Map _$ContinuousLineDrawingToJson( + ContinuousLineDrawing instance) => + { + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'startEdgePoint': instance.startEdgePoint, + 'endEdgePoint': instance.endEdgePoint, + 'exceedStart': instance.exceedStart, + 'exceedEnd': instance.exceedEnd, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart index 8c9cd142b..bb3296a0f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart @@ -3,12 +3,16 @@ import 'dart:ui'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'draggable_edge_point.g.dart'; /// A class that holds draggable edge point data. /// Draggable edge points are part of the drawings which added by user clicks /// And we want to hanle difftent types of drag events on them. /// For example with dots are draggable edge points for the line /// ⎯⎯⚪️⎯⎯⎯⚪️⎯⎯ +@JsonSerializable() class DraggableEdgePoint extends EdgePoint { /// Initializes DraggableEdgePoint({ @@ -18,6 +22,13 @@ class DraggableEdgePoint extends EdgePoint { this.isDragged = false, }) : super(epoch: epoch, quote: quote); + /// Initializes from JSON. + factory DraggableEdgePoint.fromJson(Map map) => + _$DraggableEdgePointFromJson(map); + + @override + Map toJson() => _$DraggableEdgePointToJson(this); + /// Represents whether the whole drawing is currently being dragged or not final bool isDrawingDragged; @@ -69,6 +80,12 @@ class DraggableEdgePoint extends EdgePoint { quoteFromCanvasY(localPosition.dy)); } + /// Returns the current position of the edge point when it is being dragged. + EdgePoint getEdgePoint() => EdgePoint( + epoch: _draggedPosition.dx.toInt(), + quote: _draggedPosition.dy, + ); + /// Creates a copy of this object. DraggableEdgePoint copyWith({ int? epoch, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.g.dart new file mode 100644 index 000000000..61f160fdb --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'draggable_edge_point.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DraggableEdgePoint _$DraggableEdgePointFromJson(Map json) => + DraggableEdgePoint( + epoch: json['epoch'] as int? ?? 0, + quote: (json['quote'] as num?)?.toDouble() ?? 0, + isDrawingDragged: json['isDrawingDragged'] as bool? ?? false, + isDragged: json['isDragged'] as bool? ?? false, + ); + +Map _$DraggableEdgePointToJson(DraggableEdgePoint instance) => + { + 'epoch': instance.epoch, + 'quote': instance.quote, + 'isDrawingDragged': instance.isDrawingDragged, + 'isDragged': instance.isDragged, + }; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart index b734d1629..19182694a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart @@ -1,9 +1,13 @@ +import 'package:json_annotation/json_annotation.dart'; + /// Differnet types of drawing parts. enum DrawingParts { /// Used to show the marker. + @JsonValue('marker') marker, /// Used to show the line. + @JsonValue('line') line, /// Used to show the rectangle diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart index c5798968c..9529582f0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart @@ -1,6 +1,10 @@ import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'edge_point.g.dart'; /// A class that holds epoch and yCoord of the edge points. +@JsonSerializable() class EdgePoint with EquatableMixin { /// Initializes const EdgePoint({ @@ -8,6 +12,13 @@ class EdgePoint with EquatableMixin { this.quote = 0, }); + /// Initializes from JSON. + factory EdgePoint.fromJson(Map json) => + _$EdgePointFromJson(json); + + /// Serialization to JSON. Serves as value in key-value storage. + Map toJson() => _$EdgePointToJson(this); + /// Epoch. final int epoch; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.g.dart new file mode 100644 index 000000000..2cb0373ce --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'edge_point.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +EdgePoint _$EdgePointFromJson(Map json) => EdgePoint( + epoch: json['epoch'] as int? ?? 0, + quote: (json['quote'] as num?)?.toDouble() ?? 0, + ); + +Map _$EdgePointToJson(EdgePoint instance) => { + 'epoch': instance.epoch, + 'quote': instance.quote, + }; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart index 0fee4a3a3..c6fb1c423 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart @@ -1,8 +1,13 @@ import 'dart:math'; import 'dart:ui'; +import 'package:json_annotation/json_annotation.dart'; + +part 'point.g.dart'; + /// A class that holds point data /// This class is equivalent to Offest but with customized methods +@JsonSerializable() class Point { /// Initializes const Point({ @@ -10,6 +15,12 @@ class Point { required this.y, }); + /// Initializes from JSON. + factory Point.fromJson(Map json) => _$PointFromJson(json); + + /// Converts to JSON. + Map toJson() => _$PointToJson(this); + /// Related x for the point final double x; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.g.dart new file mode 100644 index 000000000..6fb922109 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'point.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Point _$PointFromJson(Map json) => Point( + x: (json['x'] as num).toDouble(), + y: (json['y'] as num).toDouble(), + ); + +Map _$PointToJson(Point instance) => { + 'x': instance.x, + 'y': instance.y, + }; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index 463d774ce..87b78ee34 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -1,13 +1,66 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'continuous/continuous_line_drawing.dart'; +import 'vertical/vertical_drawing.dart'; + /// Base class to draw a particular drawing abstract class Drawing { + /// Initializes [Drawing]. + const Drawing(); + + /// Initializes from JSON. + /// For restoring drawings add them to the switch statement. + factory Drawing.fromJson(Map json) { + if (!json.containsKey(classNameKey)) { + throw ArgumentError.value(json, 'json', 'Missing indicator name.'); + } + + switch (json[classNameKey]) { + case ChannelDrawing.nameKey: + return ChannelDrawing.fromJson(json); + case ContinuousLineDrawing.nameKey: + return ContinuousLineDrawing.fromJson(json); + case FibfanDrawing.nameKey: + return FibfanDrawing.fromJson(json); + case HorizontalDrawing.nameKey: + return HorizontalDrawing.fromJson(json); + case LineDrawing.nameKey: + return LineDrawing.fromJson(json); + case RayLineDrawing.nameKey: + return RayLineDrawing.fromJson(json); + case RectangleDrawing.nameKey: + return RectangleDrawing.fromJson(json); + case TrendDrawing.nameKey: + return TrendDrawing.fromJson(json); + case VerticalDrawing.nameKey: + return VerticalDrawing.fromJson(json); + + default: + throw ArgumentError.value(json, 'json', 'Invalid indicator name.'); + } + } + + /// Creates a concrete drawing tool from JSON. + Map toJson(); + + /// Key of drawing tool name property in JSON. + static const String classNameKey = 'class_name_key'; + /// Will be called when the drawing is moved by the user gesture. /// /// Some drawing tools might required to handle some logic after the drawing @@ -16,9 +69,14 @@ abstract class Drawing { /// /// The method has an empty implementation so only the [Drawing] subclasses /// that require this life-cycle method can override it. + /// + // TODO(Bahar-Deriv): Decide if we need to pass the [draggableMiddlePoint] + /// and change the method name void onDrawingMoved( + int Function(double x) epochFromX, List ticks, EdgePoint startPoint, { + EdgePoint? middlePoint, EdgePoint? endPoint, }) {} @@ -37,6 +95,7 @@ abstract class Drawing { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }); @@ -45,9 +104,13 @@ abstract class Drawing { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart index cf0833cba..e7f1f4072 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart @@ -15,6 +15,7 @@ typedef OnAddDrawing = void Function( List drawingParts, { bool isDrawingFinished, bool isInfiniteDrawing, + List? edgePoints, }); /// This class is an abstract representation of a drawing creator, and it's a diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart index ccdf8c869..401acc046 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart @@ -1,28 +1,29 @@ -import 'package:deriv_chart/deriv_chart.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'drawing_data.g.dart'; /// A class that hold drawing data. +@JsonSerializable() class DrawingData { /// Initializes DrawingData({ required this.id, - required this.config, required this.drawingParts, this.isDrawingFinished = false, this.isSelected = true, - this.series, }); - /// Unique id of the current drawing. - final String id; + /// Initializes from JSON. + factory DrawingData.fromJson(Map json) => + _$DrawingDataFromJson(json); - /// Configuration of the current drawing. - final DrawingToolConfig config; + /// Serialization to JSON. Serves as value in key-value storage. + Map toJson() => _$DrawingDataToJson(this); - /// Series of ticks - List? series; + /// Unique id of the current drawing. + final String id; /// Drawing list. final List drawingParts; @@ -33,26 +34,14 @@ class DrawingData { /// If the drawing is selected by the user. bool isSelected; - /// Updates configuration. - DrawingData updateConfig(DrawingToolConfig config) => DrawingData( - id: id, - config: config, - drawingParts: drawingParts, - isDrawingFinished: isDrawingFinished, - ); - - /// Updates drawing list. - DrawingData updateDrawingPartList(List drawingParts) => - DrawingData(id: id, config: config, drawingParts: drawingParts); - /// Determines if this [DrawingData] needs to be repainted. - /// /// Returns `true` if any of the [drawingParts] needs to be repainted. bool shouldRepaint( DrawingData oldDrawingData, int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { for (final Drawing drawing in drawingParts) { @@ -60,6 +49,7 @@ class DrawingData { leftEpoch, rightEpoch, draggableStartPoint, + draggableMiddlePoint: draggableMiddlePoint, draggableEndPoint: draggableEndPoint, )) { return true; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart new file mode 100644 index 000000000..9ac745cc3 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'drawing_data.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DrawingData _$DrawingDataFromJson(Map json) => DrawingData( + id: json['id'] as String, + drawingParts: (json['drawingParts'] as List) + .map((e) => Drawing.fromJson(e as Map)) + .toList(), + isDrawingFinished: json['isDrawingFinished'] as bool? ?? false, + isSelected: json['isSelected'] as bool? ?? true, + ); + +Map _$DrawingDataToJson(DrawingData instance) => + { + 'id': instance.id, + 'drawingParts': instance.drawingParts, + 'isDrawingFinished': instance.isDrawingFinished, + 'isSelected': instance.isSelected, + }; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index 448f09827..6cc9a9998 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -1,10 +1,14 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; +import 'package:deriv_chart/src/misc/debounce.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -15,11 +19,13 @@ class DrawingPainter extends StatefulWidget { /// Initializes const DrawingPainter({ required this.drawingData, + required this.drawingConfig, required this.quoteToCanvasY, required this.quoteFromCanvasY, required this.onMoveDrawing, required this.setIsDrawingSelected, required this.selectedDrawingTool, + required this.series, Key? key, }) : super(key: key); @@ -29,6 +35,9 @@ class DrawingPainter extends StatefulWidget { /// Contains each drawing data final DrawingData? drawingData; + /// Drawing tool config. + final DrawingToolConfig? drawingConfig; + /// Conversion function for converting quote to chart's canvas' Y position. final double Function(double) quoteToCanvasY; @@ -44,6 +53,9 @@ class DrawingPainter extends StatefulWidget { /// Callback to set if drawing is selected (tapped). final void Function(DrawingData drawing) setIsDrawingSelected; + + /// Series of tick + final DataSeries series; } class _DrawingPainterState extends State { @@ -52,11 +64,53 @@ class _DrawingPainterState extends State { DraggableEdgePoint _draggableMiddlePoint = DraggableEdgePoint(); DraggableEdgePoint _draggableEndPoint = DraggableEdgePoint(); Offset? _previousPosition; + final Debounce _updateDebounce = Debounce(); @override Widget build(BuildContext context) { final XAxisModel xAxis = context.watch(); + final Repository repo = + context.watch>(); + + /// In this method, we are updating the restored drawing tool + /// config with latest data from the chart. + void updateDrawingToolConfig() { + _updateDebounce.run(() { + DrawingToolConfig updatedConfig; + + updatedConfig = widget.drawingConfig!.copyWith( + edgePoints: [ + _draggableStartPoint.getEdgePoint(), + // TODO(Bahar-Deriv): Change the way storing edge points + if (widget.drawingConfig!.configId!.contains('Channel')) + _draggableMiddlePoint.getEdgePoint(), + _draggableEndPoint.getEdgePoint(), + ], + ); + final int index = repo.items.indexOf(widget.drawingConfig!); + repo.updateAt(index, updatedConfig); + }); + } + + void _updateDrawingsMovement() { + if (widget.drawingData == null) { + return; + } + + for (final Drawing drawing in widget.drawingData!.drawingParts) { + drawing.onDrawingMoved( + xAxis.epochFromX, + widget.series.entries!, + _draggableStartPoint, + middlePoint: _draggableMiddlePoint, + endPoint: _draggableEndPoint, + ); + } + + setState(() {}); + } + void _onPanUpdate(DragUpdateDetails details) { if (widget.drawingData!.isSelected && widget.drawingData!.isDrawingFinished) { @@ -95,6 +149,9 @@ class _DrawingPainterState extends State { _draggableMiddlePoint.isDragged, ); }); + + /// Updating restored DrawingToolConfig with latest data from the chart + updateDrawingToolConfig(); } } @@ -108,7 +165,7 @@ class _DrawingPainterState extends State { ); } - return widget.drawingData != null + return (widget.drawingData != null || widget.drawingConfig != null) ? RepaintBoundary( child: GestureDetector( onTapUp: (TapUpDetails details) { @@ -167,7 +224,10 @@ class _DrawingPainterState extends State { child: CustomPaint( foregroundPainter: _DrawingPainter( drawingData: widget.drawingData!, + series: widget.series, + config: widget.drawingConfig!, theme: context.watch(), + epochFromX: xAxis.epochFromX, epochToX: xAxis.xFromEpoch, quoteToY: widget.quoteToCanvasY, quoteFromY: widget.quoteFromCanvasY, @@ -205,28 +265,15 @@ class _DrawingPainterState extends State { ) : const SizedBox(); } - - void _updateDrawingsMovement() { - if (widget.drawingData == null) { - return; - } - - for (final Drawing drawing in widget.drawingData!.drawingParts) { - drawing.onDrawingMoved( - widget.drawingData!.series!, - _draggableStartPoint, - endPoint: _draggableEndPoint, - ); - } - - setState(() {}); - } } class _DrawingPainter extends CustomPainter { _DrawingPainter({ required this.drawingData, + required this.series, + required this.config, required this.theme, + required this.epochFromX, required this.epochToX, required this.quoteToY, required this.quoteFromY, @@ -243,8 +290,11 @@ class _DrawingPainter extends CustomPainter { }); final DrawingData drawingData; + final DataSeries series; + final DrawingToolConfig config; final ChartTheme theme; final bool isDrawingToolSelected; + final int Function(double x) epochFromX; final double Function(int x) epochToX; final double Function(double y) quoteToY; final DraggableEdgePoint draggableStartPoint; @@ -273,9 +323,13 @@ class _DrawingPainter extends CustomPainter { canvas, size, theme, + epochFromX, + quoteFromY, epochToX, quoteToY, + config, drawingData, + series, updatePositionCallback, draggableStartPoint, draggableMiddlePoint: draggableMiddlePoint, @@ -303,7 +357,7 @@ class _DrawingPainter extends CustomPainter { position, epochToX, quoteToY, - drawingData.config, + config, draggableStartPoint, setIsStartPointDragged, draggableMiddlePoint: draggableMiddlePoint, diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart index e079abbdb..f80833c28 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart @@ -1,6 +1,7 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing_creator.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_drawing_creator.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart'; @@ -10,6 +11,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; @@ -24,7 +26,7 @@ class DrawingToolWidget extends StatelessWidget { required this.selectedDrawingTool, required this.quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, required this.chartConfig, required this.series, this.shouldStopDrawing, @@ -46,6 +48,7 @@ class DrawingToolWidget extends StatelessWidget { List drawingParts, { bool isDrawingFinished, bool isInfiniteDrawing, + List? edgePoints, }) onAddDrawing; /// Conversion function for converting quote to chart's canvas' Y position. @@ -54,8 +57,8 @@ class DrawingToolWidget extends StatelessWidget { /// Callback to clean drawing tool selection. final VoidCallback clearDrawingToolSelection; - /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + /// Callback to remove unfinished drawing from the list of drawings. + final VoidCallback removeUnfinishedDrawing; /// A flag to show when to stop drawing only for drawings which don't have /// fixed number of points like continuous drawing @@ -73,7 +76,7 @@ class DrawingToolWidget extends StatelessWidget { onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, clearDrawingToolSelection: clearDrawingToolSelection, - removeDrawing: removeDrawing, + removeUnfinishedDrawing: removeUnfinishedDrawing, shouldStopDrawing: shouldStopDrawing!, ); case 'dt_continuous': @@ -81,7 +84,7 @@ class DrawingToolWidget extends StatelessWidget { onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, clearDrawingToolSelection: clearDrawingToolSelection, - removeDrawing: removeDrawing, + removeUnfinishedDrawing: removeUnfinishedDrawing, shouldStopDrawing: shouldStopDrawing!, ); case 'dt_fibfan': @@ -89,7 +92,7 @@ class DrawingToolWidget extends StatelessWidget { onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, clearDrawingToolSelection: clearDrawingToolSelection, - removeDrawing: removeDrawing, + removeUnfinishedDrawing: removeUnfinishedDrawing, ); case 'dt_horizontal': return HorizontalDrawingCreator( @@ -102,28 +105,28 @@ class DrawingToolWidget extends StatelessWidget { onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, clearDrawingToolSelection: clearDrawingToolSelection, - removeDrawing: removeDrawing, + removeUnfinishedDrawing: removeUnfinishedDrawing, ); case 'dt_ray': return RayDrawingCreator( onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, clearDrawingToolSelection: clearDrawingToolSelection, - removeDrawing: removeDrawing, + removeUnfinishedDrawing: removeUnfinishedDrawing, ); case 'dt_rectangle': return RectangleDrawingCreator( onAddDrawing: onAddDrawing, quoteFromCanvasY: quoteFromCanvasY, clearDrawingToolSelection: clearDrawingToolSelection, - removeDrawing: removeDrawing, + removeUnfinishedDrawing: removeUnfinishedDrawing, ); case 'dt_trend': return TrendDrawingCreator( onAddDrawing: onAddDrawing, clearDrawingToolSelection: clearDrawingToolSelection, quoteFromCanvasY: quoteFromCanvasY, - removeDrawing: removeDrawing, + removeUnfinishedDrawing: removeUnfinishedDrawing, series: series, ); case 'dt_vertical': diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart index 97bcb357c..abf8a8db0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; @@ -12,10 +13,16 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'fibfan_drawing.g.dart'; /// Fibfan drawing tool. +@JsonSerializable() class FibfanDrawing extends Drawing with LineVectorDrawingMixin { /// Initializes FibfanDrawing({ @@ -26,6 +33,17 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { this.exceedEnd = false, }); + /// Initializes from JSON. + factory FibfanDrawing.fromJson(Map json) => + _$FibfanDrawingFromJson(json); + + @override + Map toJson() => _$FibfanDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'FibfanDrawing'; + /// Part of a drawing: 'marker' or 'line' final DrawingParts drawingPart; @@ -118,6 +136,7 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => true; @@ -128,9 +147,13 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -140,14 +163,19 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); - final FibfanDrawingToolConfig config = - drawingData.config as FibfanDrawingToolConfig; + config as FibfanDrawingToolConfig; + final LineStyle lineStyle = config.lineStyle; final Paint linePaintStyle = paint.linePaintStyle(lineStyle.color, lineStyle.thickness); + final List edgePoints = config.edgePoints; - _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); - _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + _startPoint = updatePositionCallback(edgePoints.first, draggableStartPoint); + if (edgePoints.length > 1) { + _endPoint = updatePositionCallback(edgePoints.last, draggableEndPoint!); + } else { + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + } final double startXCoord = _startPoint!.x; final double startQuoteToY = _startPoint!.y; @@ -266,15 +294,15 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { startXCoord: startXCoord.toInt(), endXCoord: endXCoord.toInt(), ) - ..drawLabel( - canvas, lineStyle, zeroDegreeVectorPercentage, _zeroDegreeVector) - ..drawLabel(canvas, lineStyle, initialInnerVectorPercentage, + ..drawLabel(canvas, size, lineStyle, zeroDegreeVectorPercentage, + _zeroDegreeVector) + ..drawLabel(canvas, size, lineStyle, initialInnerVectorPercentage, _initialInnerVector) - ..drawLabel( - canvas, lineStyle, middleInnerVectorPercentage, _middleInnerVector) - ..drawLabel( - canvas, lineStyle, finalInnerVectorPercentage, _finalInnerVector) - ..drawLabel(canvas, lineStyle, baseVectorPercentage, _baseVector); + ..drawLabel(canvas, size, lineStyle, middleInnerVectorPercentage, + _middleInnerVector) + ..drawLabel(canvas, size, lineStyle, finalInnerVectorPercentage, + _finalInnerVector) + ..drawLabel(canvas, size, lineStyle, baseVectorPercentage, _baseVector); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.g.dart new file mode 100644 index 000000000..9b654ce05 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'fibfan_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +FibfanDrawing _$FibfanDrawingFromJson(Map json) => + FibfanDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + startEdgePoint: json['startEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['startEdgePoint'] as Map), + endEdgePoint: json['endEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['endEdgePoint'] as Map), + exceedStart: json['exceedStart'] as bool? ?? false, + exceedEnd: json['exceedEnd'] as bool? ?? false, + ); + +Map _$FibfanDrawingToJson(FibfanDrawing instance) => + { + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'startEdgePoint': instance.startEdgePoint, + 'endEdgePoint': instance.endEdgePoint, + 'exceedStart': instance.exceedStart, + 'exceedEnd': instance.exceedEnd, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart index df63c3b7a..b4619b3bb 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing_creator.dart @@ -13,7 +13,7 @@ class FibfanDrawingCreator extends DrawingCreator { required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, Key? key, }) : super( key: key, @@ -25,7 +25,7 @@ class FibfanDrawingCreator extends DrawingCreator { final VoidCallback clearDrawingToolSelection; /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + final VoidCallback removeUnfinishedDrawing; @override DrawingCreatorState createState() => @@ -76,7 +76,7 @@ class _FibfanDrawingCreatorState extends DrawingCreatorState { if (edgePoints[1] == edgePoints.first) { /// If the initial point and the 2nd point are the same, /// remove the drawing and clean the drawing tool selection. - _widget.removeDrawing(drawingId); + _widget.removeUnfinishedDrawing(); _widget.clearDrawingToolSelection(); return; } else { @@ -101,6 +101,7 @@ class _FibfanDrawingCreatorState extends DrawingCreatorState { drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart index 624c73ab3..8abcf9562 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/label.dart @@ -18,6 +18,13 @@ const String finalInnerVectorPercentage = '61.8'; /// A constant for the base vector label const String baseVectorPercentage = '100'; +/// A constant for the label horizontal padding +const double labelHorizontalPadding = 25; + +/// A constant for the chart horizontal padding +// TODO(NA): Remove this constant and calculate the y axis label width +const double chartPadding = 60; + /// A class for drawing the vector label class Label { /// Initializes the vector label class @@ -34,25 +41,17 @@ class Label { /// Returns the x position of the label double _getX( - String label, { + String label, + Size size, { bool isRightSide = false, - }) { - switch (label) { - case zeroDegreeVectorPercentage: - return isRightSide ? 345 : 25; - case initialInnerVectorPercentage: - case finalInnerVectorPercentage: - return isRightSide ? 325 : 40; - case middleInnerVectorPercentage: - return isRightSide ? 335 : 30; - default: - return isRightSide ? 330 : 10; - } - } + }) => + isRightSide + ? size.width - chartPadding - labelHorizontalPadding + : labelHorizontalPadding; /// Returns the position of the label on the right side of the chart - Offset _getRightSideLabelsPosition(String label, Vector vector) { - final double x = _getX(label, isRightSide: true); + Offset _getRightSideLabelsPosition(String label, Size size, Vector vector) { + final double x = _getX(label, size, isRightSide: true); final double y = (((vector.y1 - vector.y0) / (vector.x1 - vector.x0)) * (x - vector.x0)) + @@ -62,7 +61,7 @@ class Label { /// Returns the position of the label on the left side of the chart Offset _getLeftSideLabelsPosition(String label, Vector vector) { - final double x = _getX(label); + final double x = _getX(label, Size.zero); final double y = (((vector.y1 - vector.y0) / (vector.x1 - vector.x0)) * (x - vector.x0)) + @@ -92,14 +91,15 @@ class Label { /// Draw the vector label void drawLabel( Canvas canvas, + Size size, LineStyle lineStyle, String label, Vector endVector, ) { final Offset labelOffset = (startXCoord > endXCoord && startXCoord > 10) ? _getLeftSideLabelsPosition(label, endVector) - : (startXCoord < endXCoord && startXCoord < 325) - ? _getRightSideLabelsPosition(label, endVector) + : (startXCoord < endXCoord && startXCoord < size.width - chartPadding) + ? _getRightSideLabelsPosition(label, size, endVector) : Offset.zero; if (labelOffset != Offset.zero) { getTextPainter(label, labelOffset, lineStyle.color) diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart index 504efdc85..d6aac77a5 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/distance_constants.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; @@ -11,20 +12,36 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'horizontal_drawing.g.dart'; /// Horizontal drawing tool. /// A tool used to draw straight infinite horizontal line on the chart +@JsonSerializable() class HorizontalDrawing extends Drawing { /// Initializes HorizontalDrawing({ required this.drawingPart, - required this.quoteFromCanvasY, required this.chartConfig, required this.edgePoint, }); + /// Initializes from JSON. + factory HorizontalDrawing.fromJson(Map json) => + _$HorizontalDrawingFromJson(json); + + @override + Map toJson() => _$HorizontalDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'HorizontalDrawing'; + /// Chart config to get pipSize final ChartConfig? chartConfig; @@ -37,15 +54,13 @@ class HorizontalDrawing extends Drawing { /// Keeps the latest position of the horizontal line Point? startPoint; - /// Conversion function for converting quote from chart's canvas' Y position. - final double Function(double)? quoteFromCanvasY; - // TODO(NA): Return true when the horizontal drawing is in the epoch range. @override bool needsRepaint( int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => true; @@ -56,9 +71,13 @@ class HorizontalDrawing extends Drawing { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -68,13 +87,13 @@ class HorizontalDrawing extends Drawing { DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); - final HorizontalDrawingToolConfig config = - drawingData.config as HorizontalDrawingToolConfig; + config as HorizontalDrawingToolConfig; final LineStyle lineStyle = config.lineStyle; final DrawingPatterns pattern = config.pattern; + final List edgePoints = config.edgePoints; - startPoint = updatePositionCallback(edgePoint, draggableStartPoint); + startPoint = updatePositionCallback(edgePoints.first, draggableStartPoint); final double pointYCoord = startPoint!.y; final double pointXCoord = startPoint!.x; @@ -97,7 +116,7 @@ class HorizontalDrawing extends Drawing { 'horizontal', theme, chartConfig!, - quoteFromY: quoteFromCanvasY, + quoteFromY: quoteFromY, ); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.g.dart new file mode 100644 index 000000000..0a289f3cc --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.g.dart @@ -0,0 +1,32 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'horizontal_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HorizontalDrawing _$HorizontalDrawingFromJson(Map json) => + HorizontalDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + chartConfig: json['chartConfig'] == null + ? null + : ChartConfig.fromJson(json['chartConfig'] as Map), + edgePoint: EdgePoint.fromJson(json['edgePoint'] as Map), + )..startPoint = json['startPoint'] == null + ? null + : Point.fromJson(json['startPoint'] as Map); + +Map _$HorizontalDrawingToJson(HorizontalDrawing instance) => + { + 'chartConfig': instance.chartConfig, + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'edgePoint': instance.edgePoint, + 'startPoint': instance.startPoint, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart index e690aa606..05b67830d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing_creator.dart @@ -44,15 +44,16 @@ class _HorizontalDrawingCreatorState isDrawingFinished = true; drawingParts.add(HorizontalDrawing( - drawingPart: DrawingParts.line, - edgePoint: edgePoints.first, - chartConfig: widget.chartConfig, - quoteFromCanvasY: widget.quoteFromCanvasY)); + drawingPart: DrawingParts.line, + edgePoint: edgePoints.first, + chartConfig: widget.chartConfig, + )); widget.onAddDrawing( drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index 73e08e553..22a87d511 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; @@ -12,12 +13,17 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line_vector_drawing_mixin.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'line_drawing.g.dart'; /// Line drawing tool. A line is a vector defined by two points that is /// infinite in both directions. +@JsonSerializable() class LineDrawing extends Drawing with LineVectorDrawingMixin { /// Initializes LineDrawing({ @@ -28,6 +34,17 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { this.exceedEnd = false, }); + /// Initializes from JSON. + factory LineDrawing.fromJson(Map json) => + _$LineDrawingFromJson(json); + + @override + Map toJson() => _$LineDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'LineDrawing'; + /// Part of a drawing: 'marker' or 'line' final DrawingParts drawingPart; @@ -57,6 +74,7 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => true; @@ -67,9 +85,13 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -81,14 +103,18 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { final DrawingPaintStyle paint = DrawingPaintStyle(); /// Get the latest config of any drawing tool which is used to draw the line - final LineDrawingToolConfig config = - drawingData.config as LineDrawingToolConfig; + config as LineDrawingToolConfig; final LineStyle lineStyle = config.lineStyle; final DrawingPatterns pattern = config.pattern; + final List edgePoints = config.edgePoints; - _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); - _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + _startPoint = updatePositionCallback(edgePoints.first, draggableStartPoint); + if (edgePoints.length > 1) { + _endPoint = updatePositionCallback(edgePoints.last, draggableEndPoint!); + } else { + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + } final double startXCoord = _startPoint!.x; final double startQuoteToY = _startPoint!.y; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.g.dart new file mode 100644 index 000000000..c187e958b --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'line_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LineDrawing _$LineDrawingFromJson(Map json) => LineDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + startEdgePoint: json['startEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['startEdgePoint'] as Map), + endEdgePoint: json['endEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['endEdgePoint'] as Map), + exceedStart: json['exceedStart'] as bool? ?? false, + exceedEnd: json['exceedEnd'] as bool? ?? false, + ); + +Map _$LineDrawingToJson(LineDrawing instance) => + { + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'startEdgePoint': instance.startEdgePoint, + 'endEdgePoint': instance.endEdgePoint, + 'exceedStart': instance.exceedStart, + 'exceedEnd': instance.exceedEnd, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart index 1674fc876..905cc3b73 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing_creator.dart @@ -13,7 +13,7 @@ class LineDrawingCreator extends DrawingCreator { required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, Key? key, }) : super( key: key, @@ -24,8 +24,8 @@ class LineDrawingCreator extends DrawingCreator { /// Callback to clean drawing tool selection. final VoidCallback clearDrawingToolSelection; - /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + /// Callback to remove unfinished drawing from the list of drawings. + final VoidCallback removeUnfinishedDrawing; @override DrawingCreatorState createState() => _LineDrawingCreatorState(); @@ -76,7 +76,7 @@ class _LineDrawingCreatorState extends DrawingCreatorState { if (edgePoints[1] == edgePoints.first) { /// If the initial point and the 2nd point are the same, /// remove the drawing and clean the drawing tool selection. - _widget.removeDrawing(drawingId); + _widget.removeUnfinishedDrawing(); _widget.clearDrawingToolSelection(); return; } else { @@ -101,6 +101,7 @@ class _LineDrawingCreatorState extends DrawingCreatorState { drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart index 8efc500a2..ff74ced7f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_drawing_creator.dart @@ -13,7 +13,7 @@ class RayDrawingCreator extends DrawingCreator { required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, Key? key, }) : super( key: key, @@ -25,7 +25,7 @@ class RayDrawingCreator extends DrawingCreator { final VoidCallback clearDrawingToolSelection; /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + final VoidCallback removeUnfinishedDrawing; @override DrawingCreatorState createState() => @@ -76,7 +76,7 @@ class _RayDrawingCreatorState extends DrawingCreatorState { if (edgePoints[1] == edgePoints.first) { /// If the initial point and the 2nd point are the same, /// remove the drawing and clean the drawing tool selection. - _widget.removeDrawing(drawingId); + _widget.removeUnfinishedDrawing(); _widget.clearDrawingToolSelection(); return; } else { @@ -100,6 +100,7 @@ class _RayDrawingCreatorState extends DrawingCreatorState { drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart index 36db25e92..ee864515a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart @@ -1,6 +1,7 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -9,19 +10,25 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'ray_line_drawing.g.dart'; /// Ray drawing tool. Ray is a vector defined by two points that is /// infinite in end direction. +@JsonSerializable() class RayLineDrawing extends Drawing { /// Initializes RayLineDrawing({ - required DrawingParts drawingPart, - EdgePoint startEdgePoint = const EdgePoint(), - EdgePoint endEdgePoint = const EdgePoint(), - bool exceedStart = false, - bool exceedEnd = false, + required this.drawingPart, + this.startEdgePoint = const EdgePoint(), + this.endEdgePoint = const EdgePoint(), + this.exceedStart = false, + this.exceedEnd = false, }) : _lineDrawing = LineDrawing( drawingPart: drawingPart, startEdgePoint: startEdgePoint, @@ -29,6 +36,32 @@ class RayLineDrawing extends Drawing { exceedStart: exceedStart, exceedEnd: exceedEnd); + /// Initializes from JSON. + factory RayLineDrawing.fromJson(Map json) => + _$RayLineDrawingFromJson(json); + + @override + Map toJson() => _$RayLineDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Drawing part. + final DrawingParts drawingPart; + + /// Start edge point. + final EdgePoint startEdgePoint; + + /// End edge point. + final EdgePoint endEdgePoint; + + /// Whether the start point is exceeded. + final bool exceedStart; + + /// Whether the end point is exceeded. + final bool exceedEnd; + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'RayLineDrawing'; + final LineDrawing _lineDrawing; @override @@ -36,12 +69,14 @@ class RayLineDrawing extends Drawing { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => _lineDrawing.needsRepaint( leftEpoch, rightEpoch, draggableStartPoint, + draggableMiddlePoint: draggableMiddlePoint, draggableEndPoint: draggableEndPoint, ); @@ -51,9 +86,13 @@ class RayLineDrawing extends Drawing { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -62,29 +101,33 @@ class RayLineDrawing extends Drawing { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { - final RayDrawingToolConfig config = - drawingData.config as RayDrawingToolConfig; - - final LineStyle lineStyle = config.lineStyle; - final DrawingPatterns pattern = config.pattern; + config as RayDrawingToolConfig; _lineDrawing.onPaint( - canvas, - size, - theme, - epochToX, - quoteToY, - DrawingData( - id: drawingData.id, - config: LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), - drawingParts: drawingData.drawingParts, - isDrawingFinished: drawingData.isDrawingFinished, - isSelected: drawingData.isSelected, - ), - updatePositionCallback, - draggableStartPoint, - draggableEndPoint: draggableEndPoint, - ); + canvas, + size, + theme, + epochFromX, + quoteFromY, + epochToX, + quoteToY, + LineDrawingToolConfig( + configId: config.configId, + drawingData: config.drawingData, + lineStyle: config.lineStyle, + pattern: config.pattern, + edgePoints: config.edgePoints, + ), + DrawingData( + id: drawingData.id, + drawingParts: drawingData.drawingParts, + isDrawingFinished: drawingData.isDrawingFinished, + isSelected: drawingData.isSelected, + ), + series, + updatePositionCallback, + draggableStartPoint, + draggableEndPoint: draggableEndPoint); } /// Calculation for detemining whether a user's touch or click intersects diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.g.dart new file mode 100644 index 000000000..6ef697e7d --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ray_line_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RayLineDrawing _$RayLineDrawingFromJson(Map json) => + RayLineDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + startEdgePoint: json['startEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['startEdgePoint'] as Map), + endEdgePoint: json['endEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['endEdgePoint'] as Map), + exceedStart: json['exceedStart'] as bool? ?? false, + exceedEnd: json['exceedEnd'] as bool? ?? false, + ); + +Map _$RayLineDrawingToJson(RayLineDrawing instance) => + { + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'startEdgePoint': instance.startEdgePoint, + 'endEdgePoint': instance.endEdgePoint, + 'exceedStart': instance.exceedStart, + 'exceedEnd': instance.exceedEnd, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart index 95a2b6e12..340f5f82b 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; @@ -10,10 +11,16 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'rectangle_drawing.g.dart'; /// Rectangle drawing tool. +@JsonSerializable() class RectangleDrawing extends Drawing { /// Initializes RectangleDrawing({ @@ -22,6 +29,17 @@ class RectangleDrawing extends Drawing { this.endEdgePoint = const EdgePoint(), }); + /// Initializes from JSON. + factory RectangleDrawing.fromJson(Map json) => + _$RectangleDrawingFromJson(json); + + @override + Map toJson() => _$RectangleDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'RectangleDrawing'; + /// Instance of enum including all possible drawing parts(marker,rectangle) final DrawingParts drawingPart; @@ -97,9 +115,13 @@ class RectangleDrawing extends Drawing { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -109,16 +131,19 @@ class RectangleDrawing extends Drawing { DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); - final RectangleDrawingToolConfig config = - drawingData.config as RectangleDrawingToolConfig; + config as RectangleDrawingToolConfig; final LineStyle lineStyle = config.lineStyle; final LineStyle fillStyle = config.fillStyle; - final DrawingPatterns pattern = config.pattern; + final List edgePoints = config.edgePoints; - _startPoint = updatePositionCallback(startEdgePoint, draggableStartPoint); - _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + _startPoint = updatePositionCallback(edgePoints.first, draggableStartPoint); + if (edgePoints.length > 1) { + _endPoint = updatePositionCallback(edgePoints.last, draggableEndPoint!); + } else { + _endPoint = updatePositionCallback(endEdgePoint, draggableEndPoint!); + } startXCoord = _startPoint!.x; startYCoord = _startPoint!.y; @@ -220,6 +245,7 @@ class RectangleDrawing extends Drawing { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => true; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.g.dart new file mode 100644 index 000000000..839c1d885 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.g.dart @@ -0,0 +1,39 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'rectangle_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RectangleDrawing _$RectangleDrawingFromJson(Map json) => + RectangleDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + startEdgePoint: json['startEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['startEdgePoint'] as Map), + endEdgePoint: json['endEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['endEdgePoint'] as Map), + ) + ..startXCoord = (json['startXCoord'] as num).toDouble() + ..startYCoord = (json['startYCoord'] as num).toDouble() + ..endXCoord = (json['endXCoord'] as num).toDouble() + ..endYCoord = (json['endYCoord'] as num).toDouble(); + +Map _$RectangleDrawingToJson(RectangleDrawing instance) => + { + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'startXCoord': instance.startXCoord, + 'startYCoord': instance.startYCoord, + 'endXCoord': instance.endXCoord, + 'endYCoord': instance.endYCoord, + 'startEdgePoint': instance.startEdgePoint, + 'endEdgePoint': instance.endEdgePoint, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart index 6fe453853..0f475171e 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing_creator.dart @@ -13,7 +13,7 @@ class RectangleDrawingCreator extends DrawingCreator { required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, Key? key, }) : super( key: key, @@ -25,7 +25,7 @@ class RectangleDrawingCreator extends DrawingCreator { final VoidCallback clearDrawingToolSelection; /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + final VoidCallback removeUnfinishedDrawing; @override _RectangleDrawingCreatorState createState() => @@ -77,7 +77,7 @@ class _RectangleDrawingCreatorState final EdgePoint endEdgePoint = edgePoints[1]; if (endEdgePoint == startEdgePoint) { - _widget.removeDrawing(drawingId); + _widget.removeUnfinishedDrawing(); _widget.clearDrawingToolSelection(); return; } else { @@ -99,6 +99,7 @@ class _RectangleDrawingCreatorState drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart index 719b322ea..1ab624ff7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart @@ -2,6 +2,8 @@ import 'dart:math'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/crosshair/find.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; @@ -12,41 +14,38 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/functions/min_max_calculator.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'trend_drawing.g.dart'; /// Trend drawing tool. +@JsonSerializable() class TrendDrawing extends Drawing { /// Initializes TrendDrawing({ required this.drawingPart, - required this.epochFromX, - required this.setCalculator, - required this.isClickedOnRectangleBoundary, - required this.touchTolerance, this.startEdgePoint = const EdgePoint(), this.endEdgePoint = const EdgePoint(), }); - /// Function to check if the clicked position (Offset) is on - /// boundary of the rectangle - final bool Function(Rect rect, Offset position) isClickedOnRectangleBoundary; + /// Initializes from JSON. + factory TrendDrawing.fromJson(Map json) => + _$TrendDrawingFromJson(json); - /// Callback that returns the minmax calculator between start and end epoch - final MinMaxCalculator? Function( - int minimumEpoch, int maximumEpoch, List? series) setCalculator; + @override + Map toJson() => _$TrendDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); - /// Get epoch from x. - int Function(double x)? epochFromX; + /// Key of drawing tool name property in JSON. + static const String nameKey = 'TrendDrawing'; /// Instance of enum including all possible drawing parts(marker,rectangle) final DrawingParts drawingPart; - /// The area impacted upon touch on all lines within the - /// trend drawing tool. .i.e outer rectangle , inner rectangle - /// and center line. - final double touchTolerance; - /// Marker radius. final double _markerRadius = 10; @@ -86,39 +85,87 @@ class TrendDrawing extends Drawing { /// side is dragged to the right of the right side bool _isRectangleSwapped = false; - @override - void onDrawingMoved( - List ticks, - EdgePoint startPoint, { - EdgePoint? endPoint, - }) { - final int minimumEpoch = - startXCoord == 0 ? startEdgePoint.epoch : epochFromX!(startXCoord); + /// The area impacted upon touch on all lines within the + /// trend drawing tool. .i.e outer rectangle , inner rectangle + /// and center line. + final double _touchTolerance = 5; + + /// Setting the minmax calculator between the range of + /// start and end epoch + MinMaxCalculator? _setCalculator( + int minimumEpoch, + int maximumEpoch, + List? series, + ) { + int minimumEpochIndex = findClosestIndexBinarySearch(minimumEpoch, series); + int maximumEpochIndex = findClosestIndexBinarySearch(maximumEpoch, series); + + if (minimumEpochIndex > maximumEpochIndex) { + final int tempEpochIndex = minimumEpochIndex; + minimumEpochIndex = maximumEpochIndex; + maximumEpochIndex = tempEpochIndex; + } - // Minimum epoch of the drawing - final int maximumEpoch = - endXCoord == 0 ? endEdgePoint.epoch : epochFromX!(endXCoord); + final List? epochRange = + series!.sublist(minimumEpochIndex, maximumEpochIndex); - if (maximumEpoch != 0 && minimumEpoch != 0) { - _calculator = setCalculator(minimumEpoch, maximumEpoch, ticks); - } + double minValueOf(Tick t) => t.quote; + double maxValueOf(Tick t) => t.quote; + + return MinMaxCalculator(minValueOf, maxValueOf)..calculate(epochRange!); + } + + /// Function to check if the clicked position (Offset) is on + /// boundary of the rectangle + bool _isClickedOnRectangleBoundary(Rect rect, Offset position) { + /// Width of the rectangle line + const double lineWidth = 3; + + final Rect topLineBounds = Rect.fromLTWH( + rect.left - _touchTolerance, + rect.top - _touchTolerance, + rect.width + _touchTolerance * 2, + lineWidth + _touchTolerance * 2, + ); + + final Rect leftLineBounds = Rect.fromLTWH( + rect.left - _touchTolerance, + rect.top - _touchTolerance, + lineWidth + _touchTolerance * 2, + rect.height + _touchTolerance * 2, + ); + + final Rect rightLineBounds = Rect.fromLTWH( + rect.right - lineWidth - _touchTolerance * 2, + rect.top - _touchTolerance, + lineWidth + _touchTolerance * 2, + rect.height + _touchTolerance * 2, + ); + + final Rect bottomLineBounds = Rect.fromLTWH( + rect.left - _touchTolerance, + rect.bottom - lineWidth - _touchTolerance * 2, + rect.width + _touchTolerance * 2 + 2, + lineWidth + _touchTolerance * 2 + 2, + ); + + return topLineBounds.inflate(2).contains(position) || + leftLineBounds.inflate(2).contains(position) || + rightLineBounds.inflate(2).contains(position) || + bottomLineBounds.inflate(2).contains(position); } + // TODO(Bahar-deriv): implement onDrawingMoved here later + @override bool needsRepaint( int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - }) { - if (draggableStartPoint.isInViewPortRange(leftEpoch, rightEpoch) || - (draggableEndPoint == null || - draggableEndPoint.isInViewPortRange(leftEpoch, rightEpoch))) { - return true; - } - - return false; - } + }) => + true; /// Paint the trend drawing tools @override @@ -126,9 +173,13 @@ class TrendDrawing extends Drawing { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -137,24 +188,32 @@ class TrendDrawing extends Drawing { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) { + config as TrendDrawingToolConfig; + final DrawingPaintStyle paint = DrawingPaintStyle(); + final List edgePoints = config.edgePoints; // Maximum epoch of the drawing - final int minimumEpoch = - startXCoord == 0 ? startEdgePoint.epoch : epochFromX!(startXCoord); + final int minimumEpoch = draggableStartPoint.getEdgePoint().epoch != 0 + ? draggableStartPoint.getEdgePoint().epoch + : edgePoints.first.epoch; // Minimum epoch of the drawing - final int maximumEpoch = - endXCoord == 0 ? endEdgePoint.epoch : epochFromX!(endXCoord); + final int maximumEpoch = draggableEndPoint != null && + draggableEndPoint.getEdgePoint().epoch != 0 + ? draggableEndPoint.getEdgePoint().epoch + : (edgePoints.length > 1 ? edgePoints.last.epoch : endEdgePoint.epoch); if (maximumEpoch != 0 && minimumEpoch != 0) { - // center of rectangle - _rectCenter = quoteToY(_calculator!.min) + - ((quoteToY(_calculator!.max) - quoteToY(_calculator!.min)) / 2); - } + // setting calculator + _calculator = _setCalculator(minimumEpoch, maximumEpoch, series.entries); - final TrendDrawingToolConfig config = - drawingData.config as TrendDrawingToolConfig; + if (_calculator != null) { + // center of rectangle + _rectCenter = quoteToY(_calculator!.min) + + ((quoteToY(_calculator!.max) - quoteToY(_calculator!.min)) / 2); + } + } final LineStyle lineStyle = config.lineStyle; final LineStyle fillStyle = config.fillStyle; @@ -163,14 +222,16 @@ class TrendDrawing extends Drawing { if (_calculator != null) { _startPoint = updatePositionCallback( EdgePoint( - epoch: startEdgePoint.epoch, + epoch: edgePoints.first.epoch, quote: _calculator!.min + (_calculator!.max - _calculator!.min) / 2), draggableStartPoint); _endPoint = updatePositionCallback( EdgePoint( - epoch: endEdgePoint.epoch, + epoch: (edgePoints.length > 1 + ? edgePoints.last.epoch + : endEdgePoint.epoch), quote: _calculator!.min + (_calculator!.max - _calculator!.min) / 2), draggableEndPoint!); @@ -200,7 +261,8 @@ class TrendDrawing extends Drawing { if (drawingPart == DrawingParts.marker) { if (endEdgePoint.epoch == 0) { _startPoint = updatePositionCallback( - EdgePoint(epoch: startEdgePoint.epoch, quote: startEdgePoint.quote), + EdgePoint( + epoch: edgePoints.first.epoch, quote: edgePoints.last.quote), draggableStartPoint); startXCoord = _startPoint!.x; @@ -350,11 +412,11 @@ class TrendDrawing extends Drawing { final double lineHeight = 2 * lineArea / baseArea; if (endEdgePoint.epoch != 0) { - return isClickedOnRectangleBoundary(_mainRect, position) || - isClickedOnRectangleBoundary(_middleRect, position) || + return _isClickedOnRectangleBoundary(_mainRect, position) || + _isClickedOnRectangleBoundary(_middleRect, position) || startPointDistance <= _markerRadius || endPointDistance <= _markerRadius || - lineHeight <= touchTolerance; + lineHeight <= _touchTolerance; } return false; } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.g.dart new file mode 100644 index 000000000..2f976b353 --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.g.dart @@ -0,0 +1,36 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'trend_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TrendDrawing _$TrendDrawingFromJson(Map json) => TrendDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + startEdgePoint: json['startEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['startEdgePoint'] as Map), + endEdgePoint: json['endEdgePoint'] == null + ? const EdgePoint() + : EdgePoint.fromJson(json['endEdgePoint'] as Map), + ) + ..startXCoord = (json['startXCoord'] as num).toDouble() + ..startYCoord = (json['startYCoord'] as num).toDouble() + ..endXCoord = (json['endXCoord'] as num).toDouble(); + +Map _$TrendDrawingToJson(TrendDrawing instance) => + { + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'startXCoord': instance.startXCoord, + 'startYCoord': instance.startYCoord, + 'endXCoord': instance.endXCoord, + 'startEdgePoint': instance.startEdgePoint, + 'endEdgePoint': instance.endEdgePoint, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart index 2d038c6ff..7744246dd 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart @@ -1,10 +1,10 @@ // ignore_for_file: use_setters_to_change_properties import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/crosshair/find.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/functions/min_max_calculator.dart'; import 'package:flutter/material.dart'; import '../data_model/drawing_parts.dart'; @@ -16,7 +16,7 @@ class TrendDrawingCreator extends DrawingCreator { required OnAddDrawing onAddDrawing, required double Function(double) quoteFromCanvasY, required this.clearDrawingToolSelection, - required this.removeDrawing, + required this.removeUnfinishedDrawing, required this.series, Key? key, }) : super( @@ -32,7 +32,7 @@ class TrendDrawingCreator extends DrawingCreator { final VoidCallback clearDrawingToolSelection; /// Callback to remove specific drawing from the list of drawings. - final void Function(String drawingId) removeDrawing; + final VoidCallback removeUnfinishedDrawing; @override DrawingCreatorState createState() => @@ -46,121 +46,8 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { /// Stores coordinate of first point on the graph int? _startingPointEpoch; - /// Stores the previously changed minimum epoch - int prevMinimumEpoch = 0; - - /// Stores the previously changed maximum epoch - int prevMaximumEpoch = 0; - - /// Instance of MinMaxCalculator class that holds the minimum - /// and maximum quote in the trend range w.r.t epoch - MinMaxCalculator? _calculator; - static const int touchDistanceThreshold = 200; - /// The area impacted upon touch on all lines within the - /// trend drawing tool. .i.e outer rectangle , inner rectangle - /// and center line. - final double _touchTolerance = 5; - - /// Binary search to find closest index to the [epoch]. - int _findClosestIndex(int epoch, List? entries) { - int lo = 0; - int hi = entries!.length - 1; - int localEpoch = epoch; - - if (localEpoch > entries[hi].epoch) { - localEpoch = entries[hi].epoch; - } - - while (lo <= hi) { - final int mid = (hi + lo) ~/ 2; - // int getEpochOf(T t, int index) => t.epoch; - if (localEpoch < entries[mid].epoch) { - hi = mid - 1; - } else if (localEpoch > entries[mid].epoch) { - lo = mid + 1; - } else { - return mid; - } - } - - return (entries[lo].epoch - localEpoch) < (localEpoch - entries[hi].epoch) - ? lo - : hi; - } - - /// Setting the minmax calculator between the range of - /// start and end epoch - MinMaxCalculator? _setCalculator( - int minimumEpoch, - int maximumEpoch, - List? series, - ) { - if (prevMaximumEpoch != maximumEpoch || prevMinimumEpoch != minimumEpoch) { - prevMaximumEpoch = maximumEpoch; - prevMinimumEpoch = minimumEpoch; - int minimumEpochIndex = _findClosestIndex(minimumEpoch, series); - int maximumEpochIndex = _findClosestIndex(maximumEpoch, series); - - if (minimumEpochIndex > maximumEpochIndex) { - final int tempEpochIndex = minimumEpochIndex; - minimumEpochIndex = maximumEpochIndex; - maximumEpochIndex = tempEpochIndex; - } - - final List? epochRange = - series!.sublist(minimumEpochIndex, maximumEpochIndex); - - double minValueOf(Tick t) => t.quote; - double maxValueOf(Tick t) => t.quote; - - _calculator = MinMaxCalculator(minValueOf, maxValueOf) - ..calculate(epochRange!); - } - return _calculator; - } - - /// Function to check if the clicked position (Offset) is on - /// boundary of the rectangle - bool _isClickedOnRectangleBoundary(Rect rect, Offset position) { - /// Width of the rectangle line - const double lineWidth = 3; - - final Rect topLineBounds = Rect.fromLTWH( - rect.left - _touchTolerance, - rect.top - _touchTolerance, - rect.width + _touchTolerance * 2, - lineWidth + _touchTolerance * 2, - ); - - final Rect leftLineBounds = Rect.fromLTWH( - rect.left - _touchTolerance, - rect.top - _touchTolerance, - lineWidth + _touchTolerance * 2, - rect.height + _touchTolerance * 2, - ); - - final Rect rightLineBounds = Rect.fromLTWH( - rect.right - lineWidth - _touchTolerance * 2, - rect.top - _touchTolerance, - lineWidth + _touchTolerance * 2, - rect.height + _touchTolerance * 2, - ); - - final Rect bottomLineBounds = Rect.fromLTWH( - rect.left - _touchTolerance, - rect.bottom - lineWidth - _touchTolerance * 2, - rect.width + _touchTolerance * 2 + 2, - lineWidth + _touchTolerance * 2 + 2, - ); - - return topLineBounds.inflate(2).contains(position) || - leftLineBounds.inflate(2).contains(position) || - rightLineBounds.inflate(2).contains(position) || - bottomLineBounds.inflate(2).contains(position); - } - @override void onTap(TapUpDetails details) { super.onTap(details); @@ -173,7 +60,7 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { position = details.localPosition; if (!_isPenDown) { // index of the start point in the series - final int startPointIndex = _findClosestIndex( + final int startPointIndex = findClosestIndexBinarySearch( epochFromX!(position!.dx), _widget.series.entries); // starting point on graph @@ -193,16 +80,9 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { drawingParts.add( TrendDrawing( - epochFromX: epochFromX, drawingPart: DrawingParts.marker, startEdgePoint: edgePoints.first, - setCalculator: _setCalculator, - isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, - touchTolerance: _touchTolerance, - )..onDrawingMoved( - _widget.series.input, - edgePoints.first, - ), + ), ); } else if (!isDrawingFinished) { edgePoints.add( @@ -216,14 +96,14 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { _isPenDown = false; isDrawingFinished = true; final EdgePoint startingEdgePoint = edgePoints.first; - final EdgePoint endingEdgePoint = edgePoints[1]; + final EdgePoint endingEdgePoint = edgePoints.last; // When the second point is on the same y // coordinate as the first point if ((_startingPointEpoch! - endingEdgePoint.epoch).abs() <= touchDistanceThreshold) { /// remove the drawing and clean the drawing tool selection. - _widget.removeDrawing(drawingId); + _widget.removeUnfinishedDrawing(); _widget.clearDrawingToolSelection(); return; } @@ -232,44 +112,20 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { ..removeAt(0) ..addAll([ TrendDrawing( - epochFromX: epochFromX, drawingPart: DrawingParts.rectangle, startEdgePoint: startingEdgePoint, endEdgePoint: endingEdgePoint, - setCalculator: _setCalculator, - isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, - touchTolerance: _touchTolerance, - )..onDrawingMoved( - _widget.series.input, - startingEdgePoint, - endPoint: endingEdgePoint, - ), + ), TrendDrawing( - epochFromX: epochFromX, drawingPart: DrawingParts.line, startEdgePoint: startingEdgePoint, endEdgePoint: endingEdgePoint, - setCalculator: _setCalculator, - isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, - touchTolerance: _touchTolerance, - )..onDrawingMoved( - _widget.series.input, - startingEdgePoint, - endPoint: endingEdgePoint, - ), + ), TrendDrawing( - epochFromX: epochFromX, drawingPart: DrawingParts.marker, startEdgePoint: startingEdgePoint, endEdgePoint: endingEdgePoint, - setCalculator: _setCalculator, - isClickedOnRectangleBoundary: _isClickedOnRectangleBoundary, - touchTolerance: _touchTolerance, - )..onDrawingMoved( - _widget.series.input, - startingEdgePoint, - endPoint: endingEdgePoint, - ) + ) ]); } @@ -277,6 +133,7 @@ class _TrendDrawingCreatorState extends DrawingCreatorState { drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index 8909b7224..57909bccb 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -1,5 +1,6 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; @@ -11,27 +12,39 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'vertical_drawing.g.dart'; /// Vertical drawing tool. A vertical is a vertical line defined by one point /// that is infinite in both directions. +@JsonSerializable() class VerticalDrawing extends Drawing { /// Initializes VerticalDrawing({ required this.drawingPart, required this.chartConfig, required this.edgePoint, - required this.epochFromX, }); + /// Initializes from JSON. + factory VerticalDrawing.fromJson(Map json) => + _$VerticalDrawingFromJson(json); + + @override + Map toJson() => _$VerticalDrawingToJson(this) + ..putIfAbsent(Drawing.classNameKey, () => nameKey); + + /// Key of drawing tool name property in JSON. + static const String nameKey = 'VerticalDrawing'; + /// Chart config to get pipSize final ChartConfig? chartConfig; - /// Get epoch from x. - int Function(double x)? epochFromX; - /// Part of a drawing: 'vertical' final DrawingParts drawingPart; @@ -46,12 +59,10 @@ class VerticalDrawing extends Drawing { int leftEpoch, int rightEpoch, DraggableEdgePoint draggableStartPoint, { + DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, }) => - draggableStartPoint.isInViewPortRange( - leftEpoch, - rightEpoch, - ); + true; /// Paint @override @@ -59,9 +70,13 @@ class VerticalDrawing extends Drawing { Canvas canvas, Size size, ChartTheme theme, + int Function(double x) epochFromX, + double Function(double) quoteFromY, double Function(int x) epochToX, double Function(double y) quoteToY, + DrawingToolConfig config, DrawingData drawingData, + DataSeries series, Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -71,13 +86,13 @@ class VerticalDrawing extends Drawing { DraggableEdgePoint? draggableEndPoint, }) { final DrawingPaintStyle paint = DrawingPaintStyle(); - final VerticalDrawingToolConfig config = - drawingData.config as VerticalDrawingToolConfig; + config as VerticalDrawingToolConfig; final LineStyle lineStyle = config.lineStyle; final DrawingPatterns pattern = config.pattern; + final List edgePoints = config.edgePoints; - startPoint = updatePositionCallback(edgePoint, draggableStartPoint); + startPoint = updatePositionCallback(edgePoints.first, draggableStartPoint); final double xCoord = startPoint!.x; final double startQuoteToY = startPoint!.y; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.g.dart new file mode 100644 index 000000000..9f1f4568f --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.g.dart @@ -0,0 +1,32 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'vertical_drawing.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +VerticalDrawing _$VerticalDrawingFromJson(Map json) => + VerticalDrawing( + drawingPart: $enumDecode(_$DrawingPartsEnumMap, json['drawingPart']), + chartConfig: json['chartConfig'] == null + ? null + : ChartConfig.fromJson(json['chartConfig'] as Map), + edgePoint: EdgePoint.fromJson(json['edgePoint'] as Map), + )..startPoint = json['startPoint'] == null + ? null + : Point.fromJson(json['startPoint'] as Map); + +Map _$VerticalDrawingToJson(VerticalDrawing instance) => + { + 'chartConfig': instance.chartConfig, + 'drawingPart': _$DrawingPartsEnumMap[instance.drawingPart]!, + 'edgePoint': instance.edgePoint, + 'startPoint': instance.startPoint, + }; + +const _$DrawingPartsEnumMap = { + DrawingParts.marker: 'marker', + DrawingParts.line: 'line', + DrawingParts.rectangle: 'rectangle', +}; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart index 676f0171e..1cccf2c08 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing_creator.dart @@ -47,13 +47,13 @@ class _VerticalDrawingCreatorState drawingPart: DrawingParts.line, edgePoint: edgePoints.first, chartConfig: widget.chartConfig, - epochFromX: epochFromX, )); widget.onAddDrawing( drawingId, drawingParts, isDrawingFinished: isDrawingFinished, + edgePoints: [...edgePoints], ); }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart index 8c9227dc3..f2e3fd013 100644 --- a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart +++ b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart @@ -1,3 +1,4 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; @@ -5,7 +6,6 @@ import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../chart_data.dart'; import 'animated_active_marker.dart'; import 'marker.dart'; import 'marker_series.dart'; diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index c8e7534d7..ec0d3556e 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -32,6 +32,7 @@ class DerivChart extends StatefulWidget { const DerivChart({ required this.mainSeries, required this.granularity, + required this.activeSymbol, this.markerSeries, this.controller, this.onCrosshairAppeared, @@ -67,6 +68,9 @@ class DerivChart extends StatefulWidget { /// Open position marker series. final MarkerSeries? markerSeries; + /// Current active symbol. + final String activeSymbol; + /// Chart's controller final ChartController? controller; @@ -102,7 +106,6 @@ class DerivChart extends StatefulWidget { final ChartAxisConfig chartAxisConfig; /// Whether the chart should be showing live data or not. - /// /// In case of being true the chart will keep auto-scrolling when its visible /// area is on the newest ticks/candles. final bool isLive; @@ -167,20 +170,30 @@ class _DerivChartState extends State { void initState() { super.initState(); - loadSavedIndicatorsAndDrawingTools(); _initRepos(); + loadSavedIndicatorsAndDrawingTools(); + } + + @override + void didUpdateWidget(covariant DerivChart oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.activeSymbol != oldWidget.activeSymbol) { + loadSavedIndicatorsAndDrawingTools(); + } } void _initRepos() { _indicatorsRepo = AddOnsRepository( createAddOn: (Map map) => IndicatorConfig.fromJson(map), onEditCallback: showIndicatorsDialog, + currentSymbol: widget.activeSymbol, ); _drawingToolsRepo = AddOnsRepository( createAddOn: (Map map) => DrawingToolConfig.fromJson(map), onEditCallback: showDrawingToolsDialog, + currentSymbol: widget.activeSymbol, ); } @@ -191,7 +204,7 @@ class _DerivChartState extends State { _stateRepos.asMap().forEach((int index, dynamic element) { try { - element.loadFromPrefs(prefs); + element.loadFromPrefs(prefs, widget.activeSymbol); } on Exception { // ignore: unawaited_futures showDialog( diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index 7acf79585..5414f4668 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -1,11 +1,14 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; -import 'package:deriv_chart/src/models/chart_config.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; /// A wigdet for encapsulating drawing tools related business logic class DrawingToolChart extends StatefulWidget { @@ -35,60 +38,78 @@ class DrawingToolChart extends StatefulWidget { } class _DrawingToolChartState extends State { + late Repository repo; + + /// A method to get the list of drawing data from the repository + List getDrawingData() => repo.items + .map((DrawingToolConfig config) => config.drawingData) + .toList(); + /// Sets drawing as selected and unselects the rest of drawings /// if any of the drawing is not finished , it selects the unfinished drawing void _setIsDrawingSelected(DrawingData drawing) { - drawing.isSelected = !drawing.isSelected; + setState(() { + drawing.isSelected = !drawing.isSelected; - for (final DrawingData data in widget.drawingTools.drawings) { - if (data.id != drawing.id) { - data.isSelected = false; + for (final DrawingData? data in getDrawingData()) { + if (data!.id != drawing.id) { + data.isSelected = false; + } } - } + }); } /// Removes specific drawing from the list of drawings - void removeDrawing(String drawingId) { - widget.drawingTools.drawings - .removeWhere((DrawingData data) => data.id == drawingId); + void removeUnfinishedDrawing() { + final List unfinishedDrawings = getDrawingData() + .where((DrawingData? data) => !data!.isDrawingFinished) + .toList(); + repo.removeAt(getDrawingData().indexOf(unfinishedDrawings.first)); } @override - void didUpdateWidget(DrawingToolChart oldWidget) { - super.didUpdateWidget(oldWidget); - for (final DrawingData data in widget.drawingTools.drawings) { - data.series = widget.series.entries; - } - } + Widget build(BuildContext context) { + repo = context.watch>(); - @override - Widget build(BuildContext context) => ClipRect( - child: Stack( - fit: StackFit.expand, - children: [ - ...widget.drawingTools.drawings.map( - (DrawingData drawingData) => DrawingPainter( - drawingData: drawingData, - quoteToCanvasY: widget.chartQuoteToCanvasY, - quoteFromCanvasY: widget.chartQuoteFromCanvasY, - onMoveDrawing: widget.drawingTools.onMoveDrawing, - setIsDrawingSelected: _setIsDrawingSelected, - selectedDrawingTool: widget.drawingTools.selectedDrawingTool, - ), + final List configs = repo.items.toList(); + + final List drawings = configs + .map((DrawingToolConfig config) => config.drawingData) + .toList(); + + return ClipRect( + child: Stack( + fit: StackFit.expand, + children: [ + if (drawings.isNotEmpty) + ...drawings.map((DrawingData? drawingData) => DrawingPainter( + key: ValueKey(drawingData!.id), + drawingData: drawingData, + drawingConfig: configs + .where((DrawingToolConfig config) => + config.configId == drawingData.id) + .firstOrNull, + quoteToCanvasY: widget.chartQuoteToCanvasY, + quoteFromCanvasY: widget.chartQuoteFromCanvasY, + onMoveDrawing: widget.drawingTools.onMoveDrawing, + setIsDrawingSelected: _setIsDrawingSelected, + selectedDrawingTool: widget.drawingTools.selectedDrawingTool, + series: widget.series, + )), + if (widget.drawingTools.selectedDrawingTool != null) + DrawingToolWidget( + onAddDrawing: widget.drawingTools.onAddDrawing, + selectedDrawingTool: widget.drawingTools.selectedDrawingTool!, + quoteFromCanvasY: widget.chartQuoteFromCanvasY, + chartConfig: context.watch(), + clearDrawingToolSelection: + widget.drawingTools.clearDrawingToolSelection, + series: widget.series, + removeUnfinishedDrawing: removeUnfinishedDrawing, + shouldStopDrawing: widget.drawingTools.shouldStopDrawing, ), - if (widget.drawingTools.selectedDrawingTool != null) - DrawingToolWidget( - onAddDrawing: widget.drawingTools.onAddDrawing, - selectedDrawingTool: widget.drawingTools.selectedDrawingTool!, - quoteFromCanvasY: widget.chartQuoteFromCanvasY, - chartConfig: context.watch(), - clearDrawingToolSelection: - widget.drawingTools.clearDrawingToolSelection, - series: widget.series, - removeDrawing: removeDrawing, - shouldStopDrawing: widget.drawingTools.shouldStopDrawing, - ), - ], - ), - ); + ], + ), + ); + } } diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart index 9721642f0..806f20c55 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart @@ -4,12 +4,11 @@ import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import '../chart/data_visualization/drawing_tools/data_model/edge_point.dart'; + /// This calss is used to keep all the methods and data related to drawing tools /// Which need to be shared between the DerivChart and the DrawingToolsDialog class DrawingTools { - /// Existing drawings. - final List drawings = []; - /// A flag to show when to stop drawing only for drawings which don't have /// fixed number of points like continuous drawing bool? shouldStopDrawing; @@ -30,13 +29,17 @@ class DrawingTools { if (selectedDrawingTool != null) { shouldStopDrawing = true; } - drawings.removeWhere((DrawingData data) => !data.isDrawingFinished); - selectedDrawingTool = null; - } - /// Callback to remove a drawing. - void onDrawingToolRemoval(int index) { - drawings.removeAt(index); + /// Remove unfinshed drawings before openning the dialog. + final List unfinishedDrawings = getDrawingData() + .where((DrawingData? data) => !data!.isDrawingFinished) + .toList(); + if (unfinishedDrawings.isNotEmpty) { + drawingToolsRepo! + .removeAt(getDrawingData().indexOf(unfinishedDrawings.first)); + } + + clearDrawingToolSelection(); } /// Callback for keeping the selected drawing tool data. @@ -45,11 +48,6 @@ class DrawingTools { selectedDrawingTool = config; } - /// Callback for updating the drawing data list. - void onDrawingToolUpdate(int index, DrawingToolConfig config) { - drawings[index] = drawings[index].updateConfig(config); - } - /// Callback to add the new drawing to the list of drawings /// isInfiniteDrawing used for drawings which don't have fixed number of /// points @@ -58,39 +56,45 @@ class DrawingTools { List drawingParts, { bool isDrawingFinished = false, bool isInfiniteDrawing = false, + List? edgePoints, }) { - final DrawingData? existingDrawing = drawings.firstWhereOrNull( - (DrawingData drawing) => drawing.id == drawingId, + final DrawingData? existingDrawing = getDrawingData().firstWhereOrNull( + (DrawingData? drawing) => drawing!.id == drawingId, ); if (existingDrawing == null) { - drawings.add(DrawingData( - id: drawingId, - config: selectedDrawingTool!, - drawingParts: drawingParts, - isDrawingFinished: isDrawingFinished, - )); - } else { - existingDrawing - ..updateDrawingPartList(drawingParts) - ..isSelected = true - ..isDrawingFinished = isDrawingFinished; + selectedDrawingTool = selectedDrawingTool!.copyWith( + configId: drawingId, + edgePoints: edgePoints, + drawingData: DrawingData( + id: drawingId, + drawingParts: drawingParts, + isDrawingFinished: isDrawingFinished, + ), + ); } - if (isDrawingFinished) { + if (drawingToolsRepo!.items + .where((DrawingToolConfig element) => element.configId == drawingId) + .isEmpty) { drawingToolsRepo!.add(selectedDrawingTool!); + } - if (isInfiniteDrawing && shouldStopDrawing!) { - selectedDrawingTool = null; - } - if (!isInfiniteDrawing) { - selectedDrawingTool = null; - } + /// Clear the selected drawing tool if the drawing is finished + if (isDrawingFinished && + ((isInfiniteDrawing && shouldStopDrawing!) || !isInfiniteDrawing)) { + clearDrawingToolSelection(); } - if (drawings.length > 1) { - drawings.removeWhere((DrawingData data) => - data.id != drawingId && !data.isDrawingFinished); + /// Remove any unfinished drawing before adding a new one. + final List unfinishedDrawings = getDrawingData() + .where((DrawingData? data) => + data!.id != drawingId && !data.isDrawingFinished) + .toList(); + + if (unfinishedDrawings.isNotEmpty) { + drawingToolsRepo! + .removeAt(getDrawingData().indexOf(unfinishedDrawings.first)); } } @@ -104,4 +108,11 @@ class DrawingTools { void onMoveDrawing({bool isDrawingMoved = false}) { isDrawingMoving = isDrawingMoved; } + + /// A method to get the list of drawing data from the repository + List getDrawingData() => drawingToolsRepo != null + ? drawingToolsRepo!.items + .map((DrawingToolConfig config) => config.drawingData) + .toList() + : []; } diff --git a/lib/src/misc/debounce.dart b/lib/src/misc/debounce.dart new file mode 100644 index 000000000..8cc80a2b3 --- /dev/null +++ b/lib/src/misc/debounce.dart @@ -0,0 +1,39 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; + +/// A utility class for implementing debouncing of function calls. +class Debounce { + /// Creates a [Debounce] instance. + /// + /// The [delay] parameter determines the duration of the debounce window. + Debounce({ + this.delay = const Duration(milliseconds: 1000), + }); + + /// The duration of the debounce. + /// Default debounce duration is 1000 milliseconds (1 second) + final Duration delay; + + /// The function to be executed after the debounce window. + VoidCallback? action; + + /// Internal timer to manage the debounce window. + Timer? _timer; + + /// Runs the provided [action] function after the debounce window. + /// + /// If another call to [run] is made before the debounce window completes, + /// the previous timer is canceled and a new timer is started. + void run(VoidCallback action) { + /// Cancel the previous timer if it exists + if (_timer != null) { + _timer!.cancel(); + } + + /// Start a new timer with the specified [delay] and [action] + _timer = Timer( + delay, + action, + ); + } +} diff --git a/lib/src/models/chart_config.dart b/lib/src/models/chart_config.dart index b4f2d2b60..da0c89fe4 100644 --- a/lib/src/models/chart_config.dart +++ b/lib/src/models/chart_config.dart @@ -1,8 +1,12 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'chart_config.g.dart'; /// Chart's general configuration. @immutable +@JsonSerializable() class ChartConfig { /// Initializes chart's general configuration. const ChartConfig({ @@ -11,6 +15,13 @@ class ChartConfig { this.pipSize = 4, }); + /// Initializes from JSON. + factory ChartConfig.fromJson(Map json) => + _$ChartConfigFromJson(json); + + /// Serialization to JSON. Serves as value in key-value storage. + Map toJson() => _$ChartConfigToJson(this); + /// PipSize, number of decimal digits when showing prices on the chart. final int pipSize; diff --git a/lib/src/models/chart_config.g.dart b/lib/src/models/chart_config.g.dart new file mode 100644 index 000000000..1c59a6904 --- /dev/null +++ b/lib/src/models/chart_config.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'chart_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ChartConfig _$ChartConfigFromJson(Map json) => ChartConfig( + granularity: json['granularity'] as int, + pipSize: json['pipSize'] as int? ?? 4, + ); + +Map _$ChartConfigToJson(ChartConfig instance) => + { + 'pipSize': instance.pipSize, + 'granularity': instance.granularity, + }; From 12c926fbf0600c55ec5000170d1f8cdbff75ab2a Mon Sep 17 00:00:00 2001 From: ahmadtaimoor-deriv <129935294+ahmadtaimoor-deriv@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:50:27 +0800 Subject: [PATCH 20/29] Feat drawingtool integration (#247) - drawing tools integration --- lib/deriv_chart.dart | 16 +- lib/src/add_ons/add_ons_repository.dart | 14 +- .../channel/channel_drawing_tool_config.dart | 1 + .../channel/channel_drawing_tool_item.dart | 4 +- .../continuous_drawing_tool_config.dart | 1 + .../drawing_tools_ui/distance_constants.dart | 1 - .../drawing_tools_ui/drawing_tool_config.dart | 1 + .../fibfan/fibfan_drawing_tool_config.dart | 1 + .../fibfan/fibfan_drawing_tool_item.dart | 4 +- .../horizontal_drawing_tool_config.dart | 6 + .../horizontal_drawing_tool_config.g.dart | 2 + .../horizontal_drawing_tool_item.dart | 29 +- .../line/line_drawing_tool_config.dart | 1 + .../ray/ray_drawing_tool_config.dart | 1 + .../ray/ray_drawing_tool_item.dart | 4 +- .../rectangle_drawing_tool_config.dart | 1 + .../rectangle_drawing_tool_item.dart | 4 +- .../trend/trend_drawing_tool_config.dart | 2 +- .../trend/trend_drawing_tool_item.dart | 5 +- .../vertical_drawing_tool_config.dart | 8 +- .../vertical_drawing_tool_config.g.dart | 2 + .../vertical/vertical_drawing_tool_item.dart | 23 +- .../indicators_ui/smi/smi_indicator_item.dart | 1 - lib/src/add_ons/repository.dart | 3 + lib/src/deriv_chart/chart/chart.dart | 13 +- .../chart/crosshair/crosshair_area_web.dart | 50 +-- .../indicators_series/adx_series.dart | 11 +- .../indicators_series/alligator_series.dart | 23 +- .../bollinger_bands_series.dart | 17 +- .../fractals_series/arrow_painter.dart | 2 +- .../ichimoku_cloud_series.dart | 14 +- .../indicators_series/ma_env_series.dart | 13 +- .../channel/channel_drawing.dart | 40 +-- .../continuous/continuous_line_drawing.dart | 11 +- .../data_model/draggable_edge_point.dart | 30 +- .../drawing_tools/data_model/extensions.dart | 4 +- .../drawing_tools/drawing.dart | 7 +- .../drawing_tools/drawing_data.dart | 11 +- .../drawing_tools/drawing_data.g.dart | 2 - .../drawing_tools/drawing_painter.dart | 308 +++++++++++------- .../drawing_tools/fibfan/fibfan_drawing.dart | 25 +- .../horizontal/horizontal_drawing.dart | 29 +- .../drawing_tools/line/line_drawing.dart | 23 +- .../drawing_tools/paint_drawing_label.dart | 91 +++--- .../drawing_tools/ray/ray_line_drawing.dart | 24 +- .../rectangle/rectangle_drawing.dart | 23 +- .../drawing_tools/trend/trend_drawing.dart | 44 ++- .../trend/trend_drawing_creator.dart | 3 +- .../vertical/vertical_drawing.dart | 30 +- .../markers/marker_area.dart | 2 +- lib/src/deriv_chart/chart/main_chart.dart | 15 +- lib/src/deriv_chart/deriv_chart.dart | 18 +- .../drawing_tool_chart.dart | 15 +- .../drawing_tool_chart/drawing_tools.dart | 34 +- lib/src/misc/callbacks.dart | 12 +- lib/src/misc/debounce.dart | 2 +- ...abstract_single_indicator_series_test.dart | 2 - 57 files changed, 687 insertions(+), 396 deletions(-) diff --git a/lib/deriv_chart.dart b/lib/deriv_chart.dart index 9edf786d2..e378c6a35 100644 --- a/lib/deriv_chart.dart +++ b/lib/deriv_chart.dart @@ -1,6 +1,7 @@ library deriv_chart; export 'generated/l10n.dart'; +export 'src/deriv_chart/deriv_chart.dart'; export 'src/deriv_chart/chart/chart.dart'; export 'src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_indicator.dart'; export 'src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_closed_indicator.dart'; @@ -68,6 +69,8 @@ export 'src/models/tick.dart'; export 'src/add_ons/add_on_config.dart'; export 'src/add_ons/add_ons_repository.dart'; export 'src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +export 'src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; +export 'src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart'; export 'src/add_ons/indicators_ui/adx/adx_indicator_config.dart'; export 'src/add_ons/indicators_ui/alligator/alligator_indicator_config.dart'; export 'src/add_ons/indicators_ui/aroon/aroon_indicator_config.dart'; @@ -89,7 +92,6 @@ export 'src/add_ons/indicators_ui/roc/roc_indicator_config.dart'; export 'src/add_ons/indicators_ui/rsi/rsi_indicator_config.dart'; export 'src/add_ons/indicators_ui/smi/smi_indicator_config.dart'; export 'src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_config.dart'; -export 'src/deriv_chart/deriv_chart.dart'; export 'src/add_ons/indicators_ui/williams_r/williams_r_indicator_config.dart'; export 'src/add_ons/indicators_ui/zigzag_indicator/zigzag_indicator_config.dart'; export 'src/add_ons/indicators_ui/indicator_config.dart'; @@ -107,3 +109,15 @@ export 'src/widgets/market_selector/market_selector.dart'; export 'src/widgets/market_selector/market_selector_button.dart'; export 'src/widgets/market_selector/models.dart'; export 'src/widgets/market_selector/symbol_icon.dart'; +export 'src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; +export 'src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/distance_constants.dart'; +export 'src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; +export 'src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; +export 'src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; diff --git a/lib/src/add_ons/add_ons_repository.dart b/lib/src/add_ons/add_ons_repository.dart index 8903ed0f5..5e9d75109 100644 --- a/lib/src/add_ons/add_ons_repository.dart +++ b/lib/src/add_ons/add_ons_repository.dart @@ -89,6 +89,8 @@ class AddOnsRepository extends ChangeNotifier notifyListeners(); } + + /// Removes indicator/drawing tool at [index] from repository and /// updates storage. @override @@ -101,6 +103,16 @@ class AddOnsRepository extends ChangeNotifier notifyListeners(); } + + /// Removes all indicator/drawing tool from repository and + /// updates storage. + @override + void clear() { + items.clear(); + _writeToPrefs(); + notifyListeners(); + } + /// Swaps two elements of a list and updates storage. @override void swap(int index1, int index2) { @@ -113,7 +125,7 @@ class AddOnsRepository extends ChangeNotifier if (_prefs != null) { await _prefs!.setStringList( addOnsKey, - items.map((AddOnConfig config) => jsonEncode(config)).toList(), + items.map((T config) => jsonEncode(config.toJson())).toList(), ); } } diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart index f9cc34502..9783ecac1 100644 --- a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_config.dart @@ -68,6 +68,7 @@ class ChannelDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => ChannelDrawingToolConfig( configId: configId ?? this.configId, diff --git a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart index 2a7c13d5c..3a4a4c0e3 100644 --- a/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/channel/channel_drawing_tool_item.dart @@ -1,8 +1,10 @@ +import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'channel_drawing_tool_config.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart index 8a5d89413..4eb908a7a 100644 --- a/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/continuous/continuous_drawing_tool_config.dart @@ -64,6 +64,7 @@ class ContinuousDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => ContinuousDrawingToolConfig( configId: configId ?? this.configId, diff --git a/lib/src/add_ons/drawing_tools_ui/distance_constants.dart b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart index 9b144b0e4..88125821e 100644 --- a/lib/src/add_ons/drawing_tools_ui/distance_constants.dart +++ b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart @@ -1,4 +1,3 @@ - /// This involves calculating the distance from the marker to its /// edges for the purpose of creating lines ( horizontal , line drawing tool) diff --git a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart index ba766664a..851d5596c 100644 --- a/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/drawing_tool_config.dart @@ -85,6 +85,7 @@ abstract class DrawingToolConfig extends AddOnConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }); /// Creates drawing tool. diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart index fab44509e..8dcd41589 100644 --- a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart @@ -63,6 +63,7 @@ class FibfanDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => FibfanDrawingToolConfig( configId: configId ?? this.configId, diff --git a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart index 807a2d9c1..a7d26f070 100644 --- a/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_item.dart @@ -1,8 +1,10 @@ +import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/fibfan/fibfan_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import '../callbacks.dart'; /// Fibfan drawing tool item in the list of drawing tools diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart index 27856eb9c..3783d1b35 100644 --- a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart @@ -22,6 +22,7 @@ class HorizontalDrawingToolConfig extends DrawingToolConfig { List edgePoints = const [], this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, + this.enableLabel = true, }) : super( configId: configId, drawingData: drawingData, @@ -45,6 +46,9 @@ class HorizontalDrawingToolConfig extends DrawingToolConfig { /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' final DrawingPatterns pattern; + /// For enabling the label + final bool enableLabel; + @override DrawingToolItem getItem( UpdateDrawingTool updateDrawingTool, @@ -64,6 +68,7 @@ class HorizontalDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => HorizontalDrawingToolConfig( configId: configId ?? this.configId, @@ -71,5 +76,6 @@ class HorizontalDrawingToolConfig extends DrawingToolConfig { lineStyle: lineStyle ?? this.lineStyle, pattern: pattern ?? this.pattern, edgePoints: edgePoints ?? this.edgePoints, + enableLabel: enableLabel ?? this.enableLabel, ); } diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart index 50006a706..61823260f 100644 --- a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.g.dart @@ -22,6 +22,7 @@ HorizontalDrawingToolConfig _$HorizontalDrawingToolConfigFromJson( : LineStyle.fromJson(json['lineStyle'] as Map), pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? DrawingPatterns.solid, + enableLabel: json['enableLabel'] as bool? ?? true, ); Map _$HorizontalDrawingToolConfigToJson( @@ -32,6 +33,7 @@ Map _$HorizontalDrawingToolConfigToJson( 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + 'enableLabel': instance.enableLabel, }; const _$DrawingPatternsEnumMap = { diff --git a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart index 0d9a33dd9..816d67713 100644 --- a/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_item.dart @@ -1,7 +1,9 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/horizontal/horizontal_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; @@ -35,18 +37,39 @@ class HorizontalDrawingToolItemState extends DrawingToolItemState { LineStyle? _lineStyle; DrawingPatterns? _pattern; + bool? _enableLabel; @override HorizontalDrawingToolConfig createDrawingToolConfig() => HorizontalDrawingToolConfig( lineStyle: _currentLineStyle, pattern: _currentPattern, + enableLabel: _getEnableLanel, ); @override Widget getDrawingToolOptions() => Column( children: [ _buildColorField(), + _buildEnableLabel(), + ], + ); + + Widget _buildEnableLabel() => Row( + children: [ + const Text( + 'Enable Label', + style: TextStyle(fontSize: 10), + ), + Switch( + value: _getEnableLanel, + onChanged: (bool value) { + setState(() { + _enableLabel = value; + }); + updateDrawingTool(); + }, + ), ], ); @@ -73,4 +96,8 @@ class HorizontalDrawingToolItemState DrawingPatterns get _currentPattern => _pattern ?? (widget.config as HorizontalDrawingToolConfig).pattern; + + bool get _getEnableLanel => + _enableLabel ?? + (widget.config as HorizontalDrawingToolConfig).enableLabel; } diff --git a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart index 974af69aa..baa84cea2 100644 --- a/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart @@ -64,6 +64,7 @@ class LineDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => LineDrawingToolConfig( configId: configId ?? this.configId, diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart index 302774c9e..e26ab8c50 100644 --- a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart @@ -64,6 +64,7 @@ class RayDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => RayDrawingToolConfig( configId: configId ?? this.configId, diff --git a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart index 52c55514a..e3e98a6db 100644 --- a/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_item.dart @@ -1,8 +1,10 @@ +import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'ray_drawing_tool_config.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart index 889ea98cb..91c914cc5 100644 --- a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart @@ -68,6 +68,7 @@ class RectangleDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => RectangleDrawingToolConfig( configId: configId ?? this.configId, diff --git a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart index 22478aff7..49afedccb 100644 --- a/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_item.dart @@ -1,7 +1,9 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/rectangle/rectangle_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart index e8ab05dc8..266e92f4b 100644 --- a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart @@ -3,7 +3,6 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_item.dart' import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -67,6 +66,7 @@ class TrendDrawingToolConfig extends DrawingToolConfig { LineStyle? lineStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => TrendDrawingToolConfig( configId: configId ?? this.configId, diff --git a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart index e93d87652..3917d1789 100644 --- a/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_item.dart @@ -1,8 +1,9 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/color_selector.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; - +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart index 0efb56461..b6aafb901 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart @@ -22,6 +22,7 @@ class VerticalDrawingToolConfig extends DrawingToolConfig { List edgePoints = const [], this.lineStyle = const LineStyle(thickness: 0.9, color: Colors.white), this.pattern = DrawingPatterns.solid, + this.enableLabel = true, }) : super( configId: configId, drawingData: drawingData, @@ -45,6 +46,9 @@ class VerticalDrawingToolConfig extends DrawingToolConfig { /// Drawing tool line pattern: 'solid', 'dotted', 'dashed' final DrawingPatterns pattern; + /// For enabling the label + final bool enableLabel; + @override DrawingToolItem getItem( UpdateDrawingTool updateDrawingTool, @@ -64,12 +68,14 @@ class VerticalDrawingToolConfig extends DrawingToolConfig { LineStyle? fillStyle, DrawingPatterns? pattern, List? edgePoints, + bool? enableLabel, }) => VerticalDrawingToolConfig( configId: configId ?? this.configId, drawingData: drawingData ?? this.drawingData, + edgePoints: edgePoints ?? this.edgePoints, lineStyle: lineStyle ?? this.lineStyle, pattern: pattern ?? this.pattern, - edgePoints: edgePoints ?? this.edgePoints, + enableLabel: enableLabel ?? this.enableLabel, ); } diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart index 0b2dbb75c..4a1028d77 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.g.dart @@ -22,6 +22,7 @@ VerticalDrawingToolConfig _$VerticalDrawingToolConfigFromJson( : LineStyle.fromJson(json['lineStyle'] as Map), pattern: $enumDecodeNullable(_$DrawingPatternsEnumMap, json['pattern']) ?? DrawingPatterns.solid, + enableLabel: json['enableLabel'] as bool? ?? true, ); Map _$VerticalDrawingToolConfigToJson( @@ -32,6 +33,7 @@ Map _$VerticalDrawingToolConfigToJson( 'configId': instance.configId, 'lineStyle': instance.lineStyle, 'pattern': _$DrawingPatternsEnumMap[instance.pattern]!, + 'enableLabel': instance.enableLabel, }; const _$DrawingPatternsEnumMap = { diff --git a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart index f94f22232..77693b843 100644 --- a/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart +++ b/lib/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_item.dart @@ -37,18 +37,36 @@ class VerticalDrawingToolItemState extends DrawingToolItemState { LineStyle? _lineStyle; DrawingPatterns? _pattern; + bool? _enableLabel; @override VerticalDrawingToolConfig createDrawingToolConfig() => VerticalDrawingToolConfig( lineStyle: _currentLineStyle, pattern: _currentPattern, + enableLabel: _getEnableLanel, ); @override Widget getDrawingToolOptions() => Column( + children: [_buildColorField(), _buildEnableLabel()], + ); + + Widget _buildEnableLabel() => Row( children: [ - _buildColorField(), + const Text( + 'Enable Label', + style: TextStyle(fontSize: 10), + ), + Switch( + value: _getEnableLanel, + onChanged: (bool value) { + setState(() { + _enableLabel = value; + }); + updateDrawingTool(); + }, + ), ], ); @@ -75,4 +93,7 @@ class VerticalDrawingToolItemState DrawingPatterns get _currentPattern => _pattern ?? (widget.config as VerticalDrawingToolConfig).pattern; + + bool get _getEnableLanel => + _enableLabel ?? (widget.config as VerticalDrawingToolConfig).enableLabel; } diff --git a/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart b/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart index f7fb67f17..90e559e8a 100644 --- a/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/smi/smi_indicator_item.dart @@ -4,7 +4,6 @@ import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/dropdown_menu.dart as deriv_dropdown; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/field_widget.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_series.dart'; - import 'package:flutter/material.dart' hide DropdownMenu; import '../callbacks.dart'; diff --git a/lib/src/add_ons/repository.dart b/lib/src/add_ons/repository.dart index fa5355232..b34c1c9a5 100644 --- a/lib/src/add_ons/repository.dart +++ b/lib/src/add_ons/repository.dart @@ -25,4 +25,7 @@ abstract class Repository extends ChangeNotifier { /// Swaps two elements of a list. void swap(int index1, int index2); + + /// Clears all indicator and drawing tools + void clear(); } diff --git a/lib/src/deriv_chart/chart/chart.dart b/lib/src/deriv_chart/chart/chart.dart index fd5ce18dd..14fd8cb24 100644 --- a/lib/src/deriv_chart/chart/chart.dart +++ b/lib/src/deriv_chart/chart/chart.dart @@ -7,7 +7,6 @@ import 'package:deriv_chart/src/models/chart_axis_config.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/theme/chart_default_light_theme.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; @@ -294,14 +293,16 @@ class _ChartState extends State with WidgetsBindingObserver { showCrosshair: widget.showCrosshair, onCrosshairDisappeared: widget.onCrosshairDisappeared, onCrosshairHover: ( - PointerHoverEvent ev, + Offset globalPosition, + Offset localPosition, EpochToX epochToX, QuoteToY quoteToY, EpochFromX epochFromX, QuoteFromY quoteFromY, ) => widget.onCrosshairHover?.call( - ev, + globalPosition, + localPosition, epochToX, quoteToY, epochFromX, @@ -339,14 +340,16 @@ class _ChartState extends State with WidgetsBindingObserver { widget.bottomConfigs![index + offset]), onCrosshairDisappeared: widget.onCrosshairDisappeared, onCrosshairHover: ( - PointerHoverEvent ev, + Offset globalPosition, + Offset localPosition, EpochToX epochToX, QuoteToY quoteToY, EpochFromX epochFromX, QuoteFromY quoteFromY, ) => widget.onCrosshairHover?.call( - ev, + globalPosition, + localPosition, epochToX, quoteToY, epochFromX, diff --git a/lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart b/lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart index 36a137dde..3f2c63825 100644 --- a/lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart +++ b/lib/src/deriv_chart/chart/crosshair/crosshair_area_web.dart @@ -64,29 +64,39 @@ class CrosshairAreaWeb extends StatefulWidget { class _CrosshairAreaWebState extends State { XAxisModel get xAxis => context.read(); + void _onCrosshairHover(Offset globalPosition, Offset localPosition) { + if (widget.onCrosshairHover == null) { + return; + } + + widget.onCrosshairHover?.call( + globalPosition, + localPosition, + widget.epochToCanvasX, + widget.quoteToCanvasY, + widget.epochFromCanvasX, + widget.quoteFromCanvasY, + ); + } + @override Widget build(BuildContext context) => Positioned.fill( right: widget.quoteLabelsTouchAreaWidth, - child: MouseRegion( - cursor: widget.showCrosshairCursor - ? SystemMouseCursors.precise - : SystemMouseCursors.basic, - child: const SizedBox.expand(), - onExit: (PointerExitEvent ev) => - widget.onCrosshairDisappeared?.call(), - onHover: (PointerHoverEvent ev) { - if (widget.onCrosshairHover == null) { - return; - } - - widget.onCrosshairHover?.call( - ev, - widget.epochToCanvasX, - widget.quoteToCanvasY, - widget.epochFromCanvasX, - widget.quoteFromCanvasY, - ); - }, + child: Listener( + behavior: HitTestBehavior.translucent, + onPointerMove: (PointerMoveEvent ev) => + _onCrosshairHover(ev.position, ev.localPosition), + child: MouseRegion( + opaque: false, + cursor: widget.showCrosshairCursor + ? SystemMouseCursors.precise + : SystemMouseCursors.basic, + onExit: (PointerExitEvent ev) => + widget.onCrosshairDisappeared?.call(), + onHover: (PointerHoverEvent ev) => + _onCrosshairHover(ev.position, ev.localPosition), + child: const SizedBox.expand(), + ), ), ); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart index d661f15af..25ee5ebb9 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/adx_series.dart @@ -40,7 +40,8 @@ class ADXSeries extends Series { /// ADX histogram series late SingleIndicatorSeries adxHistogramSeries; - late List _adxSeriesList; + /// ADX Series List + late List adxSeriesList; /// List of [Tick]s to calculate ADX on. final IndicatorDataInput ticks; @@ -125,14 +126,14 @@ class ADXSeries extends Series { ), ); - _adxSeriesList = [ + adxSeriesList = [ adxSeries, positiveDISeries, negativeDISeries, ]; if (config.showHistogram) { - _adxSeriesList.add(adxHistogramSeries); + adxSeriesList.add(adxHistogramSeries); } if (config.showShading) { @@ -169,14 +170,14 @@ class ADXSeries extends Series { @override void onUpdate(int leftEpoch, int rightEpoch) { - for (final SingleIndicatorSeries series in _adxSeriesList) { + for (final SingleIndicatorSeries series in adxSeriesList) { series.update(leftEpoch, rightEpoch); } } @override List recalculateMinMax() => - [_adxSeriesList.getMinValue(), _adxSeriesList.getMaxValue()]; + [adxSeriesList.getMinValue(), adxSeriesList.getMaxValue()]; @override bool shouldRepaint(ChartData? previous) { diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart index 3b3b64c14..4384affeb 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/alligator_series.dart @@ -48,8 +48,11 @@ class AlligatorSeries extends Series { /// Lips series SingleIndicatorSeries? lipsSeries; - SingleIndicatorSeries? _bullishSeries; - SingleIndicatorSeries? _bearishSeries; + /// Bullish Series + SingleIndicatorSeries? bullishSeries; + + /// Bearish Series + SingleIndicatorSeries? bearishSeries; @override SeriesPainter? createPainter() { @@ -107,7 +110,7 @@ class AlligatorSeries extends Series { } if (alligatorOptions.showFractal) { - _bearishSeries = SingleIndicatorSeries( + bearishSeries = SingleIndicatorSeries( painterCreator: ( Series series, ) => @@ -117,7 +120,7 @@ class AlligatorSeries extends Series { options: alligatorOptions, style: const LineStyle(color: Colors.redAccent), ); - _bullishSeries = SingleIndicatorSeries( + bullishSeries = SingleIndicatorSeries( painterCreator: ( Series series, ) => @@ -143,9 +146,9 @@ class AlligatorSeries extends Series { lipsSeries?.didUpdate(series?.lipsSeries) ?? false; final bool _bearishUpdated = - _bearishSeries?.didUpdate(series?._bearishSeries) ?? false; + bearishSeries?.didUpdate(series?.bearishSeries) ?? false; final bool _bullishUpdated = - _bullishSeries?.didUpdate(series?._bullishSeries) ?? false; + bullishSeries?.didUpdate(series?.bullishSeries) ?? false; return _jawUpdated || _teethUpdated || @@ -159,8 +162,8 @@ class AlligatorSeries extends Series { jawSeries?.update(leftEpoch, rightEpoch); teethSeries?.update(leftEpoch, rightEpoch); lipsSeries?.update(leftEpoch, rightEpoch); - _bullishSeries?.update(leftEpoch, rightEpoch); - _bearishSeries?.update(leftEpoch, rightEpoch); + bullishSeries?.update(leftEpoch, rightEpoch); + bearishSeries?.update(leftEpoch, rightEpoch); } @override @@ -202,9 +205,9 @@ class AlligatorSeries extends Series { canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); lipsSeries?.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _bearishSeries?.paint( + bearishSeries?.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); - _bullishSeries?.paint( + bullishSeries?.paint( canvas, size, epochToX, quoteToY, animationInfo, chartConfig, theme); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart index bb9b138f4..edc9497d0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart @@ -50,12 +50,17 @@ class BollingerBandSeries extends Series { /// Upper series late SingleIndicatorSeries upperSeries; + + /// Inner Series + final List innerSeries = []; + + /// Bollinger bands options final BollingerBandsOptions bbOptions; + /// Field Indicator final Indicator _fieldIndicator; - final List _innerSeries = []; @override SeriesPainter? createPainter() { @@ -112,7 +117,7 @@ class BollingerBandSeries extends Series { ), ); - _innerSeries + innerSeries ..add(lowerSeries) ..add(middleSeries) ..add(upperSeries); @@ -148,10 +153,10 @@ class BollingerBandSeries extends Series { // Can just use lowerSeries minValue for min and upperSeries maxValue // for max. But to be safe we calculate min and max. from all three series [ - _innerSeries + innerSeries .map((Series series) => series.minValue) .reduce((double a, double b) => safeMin(a, b)), - _innerSeries + innerSeries .map((Series series) => series.maxValue) .reduce((double a, double b) => safeMax(a, b)), ]; @@ -191,8 +196,8 @@ class BollingerBandSeries extends Series { } @override - int? getMinEpoch() => _innerSeries.getMinEpoch(); + int? getMinEpoch() => innerSeries.getMinEpoch(); @override - int? getMaxEpoch() => _innerSeries.getMaxEpoch(); + int? getMaxEpoch() => innerSeries.getMaxEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart index b625b781d..732c56cd5 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/fractals_series/arrow_painter.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/create_shape_path.dart'; import 'package:deriv_chart/src/models/tick.dart'; @@ -6,7 +7,6 @@ import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import '../../data_painter.dart'; -import '../../data_series.dart'; /// A [DataPainter] for painting arrow data. class ArrowPainter extends DataPainter> { diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart index ce5f9cb43..6043d4c92 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ichimoku_cloud_series.dart @@ -42,7 +42,9 @@ class IchimokuCloudSeries extends Series { /// SpanB line series. late SingleIndicatorSeries spanBSeries; - final List _ichimokuSeries = []; + + /// Ichimoku series. + final List ichimokuSeries = []; /// List of [Tick]s to calculate IchimokuCloud on. final IndicatorDataInput ticks; @@ -152,7 +154,7 @@ class IchimokuCloudSeries extends Series { ), ); - _ichimokuSeries + ichimokuSeries ..add(conversionLineSeries) ..add(baseLineSeries) ..add(laggingSpanSeries) @@ -198,11 +200,11 @@ class IchimokuCloudSeries extends Series { @override List recalculateMinMax() { - final double minValue = _ichimokuSeries + final double minValue = ichimokuSeries .map((SingleIndicatorSeries series) => series.minValue) .reduce(safeMin); - final double maxValue = _ichimokuSeries + final double maxValue = ichimokuSeries .map((SingleIndicatorSeries series) => series.maxValue) .reduce(safeMax); @@ -249,8 +251,8 @@ class IchimokuCloudSeries extends Series { } @override - int? getMaxEpoch() => _ichimokuSeries.getMaxEpoch(); + int? getMaxEpoch() => ichimokuSeries.getMaxEpoch(); @override - int? getMinEpoch() => _ichimokuSeries.getMinEpoch(); + int? getMinEpoch() => ichimokuSeries.getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart index 6465c41b5..11e5e863b 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart @@ -55,7 +55,8 @@ class MAEnvSeries extends Series { /// Upper series late SingleIndicatorSeries upperSeries; - final List _innerSeries = []; + /// Inner Series + final List innerSeries = []; @override SeriesPainter? createPainter() { @@ -111,7 +112,7 @@ class MAEnvSeries extends Series { ), ); - _innerSeries + innerSeries ..add(lowerSeries) ..add(middleSeries) ..add(upperSeries); @@ -148,10 +149,10 @@ class MAEnvSeries extends Series { // for max. But to be safe we calculate min and max. from all three series // TODO(Ramin): Maybe later we can have these code and getMin/MaxEpochs in a parent class for Indicators like MAEnv, Ichimoku, Bollinger, etc [ - _innerSeries + innerSeries .map((Series series) => series.minValue) .reduce((double a, double b) => safeMin(a, b)), - _innerSeries + innerSeries .map((Series series) => series.maxValue) .reduce((double a, double b) => safeMax(a, b)), ]; @@ -192,8 +193,8 @@ class MAEnvSeries extends Series { } @override - int? getMaxEpoch() => _innerSeries.getMaxEpoch(); + int? getMaxEpoch() => innerSeries.getMaxEpoch(); @override - int? getMinEpoch() => _innerSeries.getMinEpoch(); + int? getMinEpoch() => innerSeries.getMinEpoch(); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart index 200a3f9f1..ae911ed87 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/channel/channel_drawing.dart @@ -177,7 +177,7 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawCircle( Offset(middleXCoord, middleQuoteToY - height), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } else if (startEdgePoint.epoch != 0 && startQuoteToY != 0) { @@ -185,7 +185,7 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawCircle( Offset(startXCoord, startQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } else if (middleEdgePoint.epoch != 0 && middleQuoteToY != 0) { @@ -193,7 +193,7 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawCircle( Offset(middleXCoord, middleQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } @@ -214,19 +214,19 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { ..drawCircle( Offset(startXCoord, startQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()) ..drawCircle( Offset(middleXCoord, middleQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()) ..drawCircle( Offset(middleXCoord, middleQuoteToY - height), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()) @@ -234,7 +234,7 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { ..drawLine( Offset(_initialVector.x0, _initialVector.y0), Offset(_initialVector.x1, _initialVector.y1), - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle( lineStyle.color, lineStyle.thickness) : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), @@ -242,7 +242,7 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { ..drawLine( Offset(_finalVector.x0, _finalVector.y0), Offset(_finalVector.x1, _finalVector.y1), - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle( lineStyle.color, lineStyle.thickness) : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), @@ -254,7 +254,7 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawLine( Offset(_initialVector.x0, _initialVector.y0), Offset(_initialVector.x1, _initialVector.y1), - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle( lineStyle.color, lineStyle.thickness) : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), @@ -275,16 +275,12 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { - setIsStartPointDragged(isDragged: false); - setIsMiddlePointDragged!(isDragged: false); - setIsEndPointDragged!(isDragged: false); - final double middleXCoord = _middlePoint!.x; final double middleQuoteToY = _middlePoint!.y; @@ -295,12 +291,16 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { /// Check if start point clicked if (_startPoint!.isClicked(position, markerRadius)) { - setIsStartPointDragged(isDragged: true); + setIsOverStartPoint(isOverPoint: true); + } else { + setIsOverStartPoint(isOverPoint: false); } /// Check if middle point clicked if (_middlePoint!.isClicked(position, markerRadius)) { - setIsMiddlePointDragged(isDragged: true); + setIsOverMiddlePoint!(isOverPoint: true); + } else { + setIsOverMiddlePoint!(isOverPoint: false); } /// Check if end point clicked, since the endPoint position is dependendat @@ -309,7 +309,9 @@ class ChannelDrawing extends Drawing with LineVectorDrawingMixin { /// Check if end point clicked if (endPoint.isClicked(position, markerRadius)) { - setIsEndPointDragged(isDragged: true); + setIsOverEndPoint!(isOverPoint: true); + } else { + setIsOverEndPoint!(isOverPoint: false); } /// Detect the area between 2 parallel lines diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart index bb3b53a28..3cab8e564 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -108,6 +108,7 @@ class ContinuousLineDrawing extends Drawing { drawingParts: drawingData.drawingParts, isDrawingFinished: drawingData.isDrawingFinished, isSelected: drawingData.isSelected, + isHovered: drawingData.isHovered, ); /// Draw first line of the continuous drawing which need 2 taps to draw @@ -183,11 +184,11 @@ class ContinuousLineDrawing extends Drawing { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { config as ContinuousDrawingToolConfig; @@ -200,8 +201,8 @@ class ContinuousLineDrawing extends Drawing { quoteToY, LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), draggableStartPoint, - setIsStartPointDragged, + setIsOverStartPoint, draggableEndPoint: draggableEndPoint, - setIsEndPointDragged: setIsEndPointDragged); + setIsOverEndPoint: setIsOverEndPoint); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart index bb3296a0f..33c0b6a87 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart @@ -36,28 +36,28 @@ class DraggableEdgePoint extends EdgePoint { final bool isDragged; /// Holds the current position of the edge point when it is being dragged. - Offset _draggedPosition = Offset.zero; + EdgePoint _draggedEdgePoint = const EdgePoint(); /// Updated position of the edge point when it is being dragged. /// /// This would be obsolete when we keep [epoch] and [quote] fields updated /// with user's dragging. And we can use them instead of this field. - Offset get draggedPosition => _draggedPosition; + EdgePoint get draggedEdgePoint => _draggedEdgePoint; /// A callback method that takes the relative x and y positions as parameter, /// sets the draggedPosition field to its value and return epoch and quote /// values. Point updatePosition( int epoch, - double yCoord, + double quote, double Function(int x) epochToX, double Function(double y) quoteToY, ) { - final Offset oldPosition = Offset(epoch.toDouble(), yCoord); - _draggedPosition = isDrawingDragged ? _draggedPosition : oldPosition; + final EdgePoint oldEdgePoint = EdgePoint(epoch: epoch, quote: quote); + _draggedEdgePoint = isDrawingDragged ? _draggedEdgePoint : oldEdgePoint; - final double x = epochToX(_draggedPosition.dx.toInt()); - final double y = quoteToY(_draggedPosition.dy); + final double x = epochToX(_draggedEdgePoint.epoch); + final double y = quoteToY(_draggedEdgePoint.quote); return Point(x: x, y: y); } @@ -72,18 +72,20 @@ class DraggableEdgePoint extends EdgePoint { required bool isOtherEndDragged, }) { final Offset localPosition = Offset( - xAxis.xFromEpoch(_draggedPosition.dx.toInt()), - quoteToY(_draggedPosition.dy)) + + xAxis.xFromEpoch(_draggedEdgePoint.epoch), + quoteToY(_draggedEdgePoint.quote)) + (isOtherEndDragged ? Offset.zero : delta); - _draggedPosition = Offset(xAxis.epochFromX(localPosition.dx).toDouble(), - quoteFromCanvasY(localPosition.dy)); + _draggedEdgePoint = EdgePoint( + epoch: xAxis.epochFromX(localPosition.dx), + quote: quoteFromCanvasY(localPosition.dy), + ); } /// Returns the current position of the edge point when it is being dragged. EdgePoint getEdgePoint() => EdgePoint( - epoch: _draggedPosition.dx.toInt(), - quote: _draggedPosition.dy, + epoch: _draggedEdgePoint.epoch, + quote: _draggedEdgePoint.quote, ); /// Creates a copy of this object. @@ -98,5 +100,5 @@ class DraggableEdgePoint extends EdgePoint { quote: quote ?? this.quote, isDrawingDragged: isDrawingDragged ?? this.isDrawingDragged, isDragged: isDragged ?? this.isDragged, - ).._draggedPosition = _draggedPosition; + ).._draggedEdgePoint = _draggedEdgePoint; } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart index e5469400b..131238fb7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart @@ -21,6 +21,6 @@ extension DraggableEdgePointExtension on DraggableEdgePoint { /// The view port range is defined by the left and right epoch values. /// returns true if the edge point is on the view port range. bool isInViewPortRange(int leftEpoch, int rightEpoch) => - draggedPosition.dx >= (leftEpoch - _edgePointOffScreenSafeDistance) && - draggedPosition.dx <= (rightEpoch + _edgePointOffScreenSafeDistance); + draggedEdgePoint.epoch >= (leftEpoch - _edgePointOffScreenSafeDistance) && + draggedEdgePoint.epoch <= (rightEpoch + _edgePointOffScreenSafeDistance); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart index 87b78ee34..2c6d5ff3a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart @@ -10,7 +10,6 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart'; -import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; import 'package:flutter/material.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; @@ -128,10 +127,10 @@ abstract class Drawing { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart index 401acc046..16a2f0afa 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart @@ -12,7 +12,8 @@ class DrawingData { required this.id, required this.drawingParts, this.isDrawingFinished = false, - this.isSelected = true, + this.isHovered = false, + this.isSelected = false, }); /// Initializes from JSON. @@ -32,8 +33,16 @@ class DrawingData { bool isDrawingFinished; /// If the drawing is selected by the user. + @JsonKey(includeFromJson: false, includeToJson: false) bool isSelected; + /// If the drawing is hovered by the user. + @JsonKey(includeFromJson: false, includeToJson: false) + bool isHovered; + + /// If the drawing should be highlighted or not. + bool get shouldHighlight => isSelected || isHovered; + /// Determines if this [DrawingData] needs to be repainted. /// Returns `true` if any of the [drawingParts] needs to be repainted. bool shouldRepaint( diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart index 9ac745cc3..ff555a6ac 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.g.dart @@ -12,7 +12,6 @@ DrawingData _$DrawingDataFromJson(Map json) => DrawingData( .map((e) => Drawing.fromJson(e as Map)) .toList(), isDrawingFinished: json['isDrawingFinished'] as bool? ?? false, - isSelected: json['isSelected'] as bool? ?? true, ); Map _$DrawingDataToJson(DrawingData instance) => @@ -20,5 +19,4 @@ Map _$DrawingDataToJson(DrawingData instance) => 'id': instance.id, 'drawingParts': instance.drawingParts, 'isDrawingFinished': instance.isDrawingFinished, - 'isSelected': instance.isSelected, }; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart index 6cc9a9998..d7da45f02 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart @@ -19,12 +19,14 @@ class DrawingPainter extends StatefulWidget { /// Initializes const DrawingPainter({ required this.drawingData, - required this.drawingConfig, required this.quoteToCanvasY, required this.quoteFromCanvasY, required this.onMoveDrawing, required this.setIsDrawingSelected, + required this.isDrawingMoving, required this.selectedDrawingTool, + required this.onMouseEnter, + required this.onMouseExit, required this.series, Key? key, }) : super(key: key); @@ -35,12 +37,12 @@ class DrawingPainter extends StatefulWidget { /// Contains each drawing data final DrawingData? drawingData; - /// Drawing tool config. - final DrawingToolConfig? drawingConfig; - /// Conversion function for converting quote to chart's canvas' Y position. final double Function(double) quoteToCanvasY; + /// Whether a drawing is moved or not. + final bool isDrawingMoving; + @override _DrawingPainterState createState() => _DrawingPainterState(); @@ -54,6 +56,12 @@ class DrawingPainter extends StatefulWidget { /// Callback to set if drawing is selected (tapped). final void Function(DrawingData drawing) setIsDrawingSelected; + /// Callback to notify mouse enter over the addon. + final void Function() onMouseEnter; + + /// Callback to notify mouse exit over the addon. + final void Function() onMouseExit; + /// Series of tick final DataSeries series; } @@ -64,8 +72,27 @@ class _DrawingPainterState extends State { DraggableEdgePoint _draggableMiddlePoint = DraggableEdgePoint(); DraggableEdgePoint _draggableEndPoint = DraggableEdgePoint(); Offset? _previousPosition; + bool isTouchHeld = false; + bool isOverStartPoint = false; + bool isOverMiddlePoint = false; + bool isOverEndPoint = false; + final Debounce _updateDebounce = Debounce(); + void _onMouseEnter() { + setState(() { + widget.drawingData!.isHovered = true; + }); + widget.onMouseEnter(); + } + + void _onMouseExit() { + setState(() { + widget.drawingData!.isHovered = false; + }); + widget.onMouseExit(); + } + @override Widget build(BuildContext context) { final XAxisModel xAxis = context.watch(); @@ -77,19 +104,25 @@ class _DrawingPainterState extends State { /// config with latest data from the chart. void updateDrawingToolConfig() { _updateDebounce.run(() { - DrawingToolConfig updatedConfig; - - updatedConfig = widget.drawingConfig!.copyWith( - edgePoints: [ - _draggableStartPoint.getEdgePoint(), - // TODO(Bahar-Deriv): Change the way storing edge points - if (widget.drawingConfig!.configId!.contains('Channel')) - _draggableMiddlePoint.getEdgePoint(), - _draggableEndPoint.getEdgePoint(), - ], + final DrawingData drawingData = widget.drawingData!; + final int index = repo.items.indexWhere( + (DrawingToolConfig item) => item.configId == drawingData.id, ); - final int index = repo.items.indexOf(widget.drawingConfig!); - repo.updateAt(index, updatedConfig); + + if (index > -1) { + final DrawingToolConfig config = repo.items[index]; + + final DrawingToolConfig updatedConfig = config.copyWith( + edgePoints: [ + _draggableStartPoint.getEdgePoint(), + // TODO(Bahar-Deriv): Change the way storing edge points + if (config.configId!.contains('Channel')) + _draggableMiddlePoint.getEdgePoint(), + _draggableEndPoint.getEdgePoint(), + ], + ); + repo.updateAt(index, updatedConfig); + } }); } @@ -165,49 +198,67 @@ class _DrawingPainterState extends State { ); } - return (widget.drawingData != null || widget.drawingConfig != null) - ? RepaintBoundary( - child: GestureDetector( - onTapUp: (TapUpDetails details) { - widget.setIsDrawingSelected(widget.drawingData!); - _updateDrawingsMovement(); - }, - onLongPressDown: (LongPressDownDetails details) { - widget.onMoveDrawing(isDrawingMoved: true); - _previousPosition = details.localPosition; - _updateDrawingsMovement(); - }, - onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { - final DragUpdateDetails dragDetails = - convertLongPressToDrag(details, _previousPosition); - _previousPosition = details.localPosition; - - _onPanUpdate(dragDetails); - _updateDrawingsMovement(); - }, - onLongPressUp: () { - widget.onMoveDrawing(isDrawingMoved: false); - _draggableStartPoint = _draggableStartPoint.copyWith( - isDragged: false, - ); - _draggableMiddlePoint = _draggableMiddlePoint.copyWith( - isDragged: false, - ); - _draggableEndPoint = _draggableEndPoint.copyWith( - isDragged: false, - ); - _updateDrawingsMovement(); - }, - onPanStart: (DragStartDetails details) { - widget.onMoveDrawing(isDrawingMoved: true); - _updateDrawingsMovement(); - }, - onPanUpdate: (DragUpdateDetails details) { - _onPanUpdate(details); - _updateDrawingsMovement(); - }, - onPanEnd: (DragEndDetails details) { - setState(() { + return widget.drawingData != null + ? MouseRegion( + onEnter: (PointerEnterEvent event) { + if (!isTouchHeld && !widget.isDrawingMoving) { + _onMouseEnter(); + } + }, + onExit: (PointerExitEvent event) { + if (!isTouchHeld && !widget.isDrawingMoving) { + _onMouseExit(); + } + }, + hitTestBehavior: HitTestBehavior.deferToChild, + child: RepaintBoundary( + child: GestureDetector( + onTapDown: (TapDownDetails details) { + isTouchHeld = true; + if (details.kind == PointerDeviceKind.mouse && + !widget.drawingData!.isSelected) { + widget.setIsDrawingSelected(widget.drawingData!); + _updateDrawingsMovement(); + } + + _draggableStartPoint = _draggableStartPoint.copyWith( + isDragged: isOverStartPoint, + ); + + _draggableMiddlePoint = _draggableMiddlePoint.copyWith( + isDragged: isOverMiddlePoint, + ); + + _draggableEndPoint = _draggableEndPoint.copyWith( + isDragged: isOverEndPoint, + ); + }, + onTapUp: (TapUpDetails details) { + isTouchHeld = false; + if (details.kind != PointerDeviceKind.mouse) { + widget.setIsDrawingSelected(widget.drawingData!); + _updateDrawingsMovement(); + } + widget.onMoveDrawing(isDrawingMoved: false); + }, + onLongPressDown: (LongPressDownDetails details) { + widget.onMoveDrawing(isDrawingMoved: true); + isTouchHeld = true; + _previousPosition = details.localPosition; + _updateDrawingsMovement(); + }, + onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { + final DragUpdateDetails dragDetails = + convertLongPressToDrag(details, _previousPosition); + _previousPosition = details.localPosition; + + _onPanUpdate(dragDetails); + _updateDrawingsMovement(); + }, + onLongPressUp: () { + widget.onMoveDrawing(isDrawingMoved: false); + _onMouseExit(); + isTouchHeld = false; _draggableStartPoint = _draggableStartPoint.copyWith( isDragged: false, ); @@ -217,52 +268,77 @@ class _DrawingPainterState extends State { _draggableEndPoint = _draggableEndPoint.copyWith( isDragged: false, ); - }); - widget.onMoveDrawing(isDrawingMoved: false); - _updateDrawingsMovement(); - }, - child: CustomPaint( - foregroundPainter: _DrawingPainter( - drawingData: widget.drawingData!, - series: widget.series, - config: widget.drawingConfig!, - theme: context.watch(), - epochFromX: xAxis.epochFromX, - epochToX: xAxis.xFromEpoch, - quoteToY: widget.quoteToCanvasY, - quoteFromY: widget.quoteFromCanvasY, - draggableStartPoint: _draggableStartPoint, - draggableMiddlePoint: _draggableMiddlePoint, - isDrawingToolSelected: widget.selectedDrawingTool != null, - draggableEndPoint: _draggableEndPoint, - leftEpoch: xAxis.leftBoundEpoch, - rightEpoch: xAxis.rightBoundEpoch, - updatePositionCallback: ( - EdgePoint edgePoint, - DraggableEdgePoint draggableEdgePoint, - ) => - draggableEdgePoint.updatePosition( - edgePoint.epoch, - edgePoint.quote, - xAxis.xFromEpoch, - widget.quoteToCanvasY, + _updateDrawingsMovement(); + }, + onPanStart: (DragStartDetails details) { + widget.onMoveDrawing(isDrawingMoved: true); + isTouchHeld = true; + _updateDrawingsMovement(); + }, + onPanUpdate: (DragUpdateDetails details) { + _onPanUpdate(details); + _updateDrawingsMovement(); + }, + onPanEnd: (DragEndDetails details) { + isTouchHeld = false; + setState(() { + _draggableStartPoint = _draggableStartPoint.copyWith( + isDragged: false, + ); + _draggableMiddlePoint = _draggableMiddlePoint.copyWith( + isDragged: false, + ); + _draggableEndPoint = _draggableEndPoint.copyWith( + isDragged: false, + ); + }); + widget.onMoveDrawing(isDrawingMoved: false); + _onMouseExit(); + _updateDrawingsMovement(); + }, + child: CustomPaint( + foregroundPainter: _DrawingPainter( + drawingData: widget.drawingData!, + series: widget.series, + config: repo.items + .where((DrawingToolConfig config) => + config.configId == widget.drawingData!.id) + .first, + theme: context.watch(), + epochFromX: xAxis.epochFromX, + epochToX: xAxis.xFromEpoch, + quoteToY: widget.quoteToCanvasY, + quoteFromY: widget.quoteFromCanvasY, + draggableStartPoint: _draggableStartPoint, + draggableMiddlePoint: _draggableMiddlePoint, + isTouchHeld: isTouchHeld, + isDrawingToolSelected: widget.selectedDrawingTool != null, + draggableEndPoint: _draggableEndPoint, + leftEpoch: xAxis.leftBoundEpoch, + rightEpoch: xAxis.rightBoundEpoch, + updatePositionCallback: ( + EdgePoint edgePoint, + DraggableEdgePoint draggableEdgePoint, + ) => + draggableEdgePoint.updatePosition( + edgePoint.epoch, + edgePoint.quote, + xAxis.xFromEpoch, + widget.quoteToCanvasY, + ), + setIsOverStartPoint: ({required bool isOverPoint}) { + isOverStartPoint = isOverPoint; + }, + setIsOverMiddlePoint: ({required bool isOverPoint}) { + isOverMiddlePoint = isOverPoint; + }, + setIsOverEndPoint: ({required bool isOverPoint}) { + isOverEndPoint = isOverPoint; + }, ), - setIsStartPointDragged: ({required bool isDragged}) { - _draggableStartPoint = - _draggableStartPoint.copyWith(isDragged: isDragged); - }, - setIsMiddlePointDragged: ({required bool isDragged}) { - _draggableMiddlePoint = - _draggableMiddlePoint.copyWith(isDragged: isDragged); - }, - setIsEndPointDragged: ({required bool isDragged}) { - _draggableEndPoint = - _draggableEndPoint.copyWith(isDragged: isDragged); - }, ), ), - ), - ) + )) : const SizedBox(); } } @@ -278,15 +354,16 @@ class _DrawingPainter extends CustomPainter { required this.quoteToY, required this.quoteFromY, required this.draggableStartPoint, - required this.setIsStartPointDragged, + required this.setIsOverStartPoint, required this.updatePositionCallback, required this.leftEpoch, required this.rightEpoch, this.isDrawingToolSelected = false, + this.isTouchHeld = false, this.draggableMiddlePoint, this.draggableEndPoint, - this.setIsMiddlePointDragged, - this.setIsEndPointDragged, + this.setIsOverMiddlePoint, + this.setIsOverEndPoint, }); final DrawingData drawingData; @@ -294,15 +371,16 @@ class _DrawingPainter extends CustomPainter { final DrawingToolConfig config; final ChartTheme theme; final bool isDrawingToolSelected; + final bool isTouchHeld; final int Function(double x) epochFromX; final double Function(int x) epochToX; final double Function(double y) quoteToY; final DraggableEdgePoint draggableStartPoint; final DraggableEdgePoint? draggableMiddlePoint; final DraggableEdgePoint? draggableEndPoint; - final void Function({required bool isDragged}) setIsStartPointDragged; - final void Function({required bool isDragged})? setIsMiddlePointDragged; - final void Function({required bool isDragged})? setIsEndPointDragged; + final void Function({required bool isOverPoint}) setIsOverStartPoint; + final void Function({required bool isOverPoint})? setIsOverMiddlePoint; + final void Function({required bool isOverPoint})? setIsOverEndPoint; final Point Function( EdgePoint edgePoint, DraggableEdgePoint draggableEdgePoint, @@ -359,11 +437,11 @@ class _DrawingPainter extends CustomPainter { quoteToY, config, draggableStartPoint, - setIsStartPointDragged, + setIsOverStartPoint, draggableMiddlePoint: draggableMiddlePoint, draggableEndPoint: draggableEndPoint, - setIsMiddlePointDragged: setIsMiddlePointDragged, - setIsEndPointDragged: setIsEndPointDragged, + setIsOverMiddlePoint: setIsOverMiddlePoint, + setIsOverEndPoint: setIsOverEndPoint, )) { if (isDrawingToolSelected) { return false; @@ -372,8 +450,12 @@ class _DrawingPainter extends CustomPainter { } } - /// For deselecting the drawing when tapping outside of the drawing. - drawingData.isSelected = false; + if (!isTouchHeld && drawingData.isDrawingFinished) { + /// For deselecting the drawing when tapping outside of the drawing. + drawingData + ..isSelected = false + ..isHovered = false; + } return false; } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart index abf8a8db0..96e9188a7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/fibfan/fibfan_drawing.dart @@ -189,7 +189,7 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawCircle( Offset(endXCoord, endQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } else if (startEdgePoint.epoch != 0 && startQuoteToY != 0) { @@ -197,7 +197,7 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawCircle( Offset(startXCoord, startQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } @@ -252,13 +252,13 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { ..drawCircle( Offset(startXCoord, startQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()) ..drawCircle( Offset(endXCoord, endQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()) @@ -316,27 +316,28 @@ class FibfanDrawing extends Drawing with LineVectorDrawingMixin { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { final LineStyle lineStyle = config.toJson()['lineStyle']; bool _isVectorHit(Vector vector) => isVectorHit(vector, position, lineStyle); - setIsStartPointDragged(isDragged: false); - setIsEndPointDragged!(isDragged: false); - /// Check if start point clicked if (_startPoint!.isClicked(position, markerRadius)) { - setIsStartPointDragged(isDragged: true); + setIsOverStartPoint(isOverPoint: true); + } else { + setIsOverStartPoint(isOverPoint: false); } /// Check if end point clicked if (_endPoint!.isClicked(position, markerRadius)) { - setIsEndPointDragged(isDragged: true); + setIsOverEndPoint!(isOverPoint: true); + } else { + setIsOverEndPoint!(isOverPoint: false); } return _isVectorHit(_baseVector) || _isVectorHit(_finalInnerVector) || diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart index d6aac77a5..bf3f605c8 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/horizontal/horizontal_drawing.dart @@ -105,19 +105,22 @@ class HorizontalDrawing extends Drawing { canvas.drawLine( Offset(startX, pointYCoord), Offset(endingX, pointYCoord), - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle(lineStyle.color, lineStyle.thickness) : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), ); - paintDrawingLabel( - canvas, - size, - pointYCoord, - 'horizontal', - theme, - chartConfig!, - quoteFromY: quoteFromY, - ); + if (config.enableLabel) { + paintDrawingLabel( + canvas, + size, + pointYCoord, + 'horizontal', + theme, + chartConfig!, + quoteFromY: quoteFromY, + color: lineStyle.color, + ); + } } } @@ -130,11 +133,11 @@ class HorizontalDrawing extends Drawing { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { config as HorizontalDrawingToolConfig; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart index 22a87d511..077997e2f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart @@ -128,7 +128,7 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawCircle( Offset(endXCoord, endQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } else if (startEdgePoint.epoch != 0 && startQuoteToY != 0) { @@ -136,7 +136,7 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawCircle( Offset(startXCoord, startQuoteToY), markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } @@ -154,7 +154,7 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { canvas.drawLine( Offset(_vector.x0, _vector.y0), Offset(_vector.x1, _vector.y1), - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle(lineStyle.color, lineStyle.thickness) : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), ); @@ -173,15 +173,12 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { - setIsStartPointDragged(isDragged: false); - setIsEndPointDragged!(isDragged: false); - config as LineDrawingToolConfig; final LineStyle lineStyle = config.lineStyle; @@ -194,12 +191,16 @@ class LineDrawing extends Drawing with LineVectorDrawingMixin { /// Check if start point clicked if (_startPoint!.isClicked(position, markerRadius)) { - setIsStartPointDragged(isDragged: true); + setIsOverStartPoint(isOverPoint: true); + } else { + setIsOverStartPoint(isOverPoint: false); } /// Check if end point clicked if (_endPoint!.isClicked(position, markerRadius)) { - setIsEndPointDragged(isDragged: true); + setIsOverEndPoint!(isOverPoint: true); + } else { + setIsOverEndPoint!(isOverPoint: false); } startXCoord = _vector.x0; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart index 9e5d88097..a0438ef8f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/paint_drawing_label.dart @@ -15,76 +15,77 @@ void paintDrawingLabel( ChartConfig config, { int Function(double x)? epochFromX, double Function(double)? quoteFromY, + Color color = Colors.white, }) { - /// Outline Rectangle of the label - Rect _labelRect = Rect.zero; - - /// Offset from where the text starts - Offset _textOffset = Offset.zero; - /// Name of the of label String _labelString = ''; - /// Width of the rectangle - const double _width = 46; - - /// Height of the rectangle - const double _height = 24; - final HorizontalBarrierStyle horizontalBarrierStyle = theme.horizontalBarrierStyle; if (drawingType == 'horizontal') { - _labelRect = Rect.fromCenter( - center: Offset(size.width - 26, coord), - width: _width, - height: _height, - ); - _labelString = quoteFromY!(coord).toStringAsFixed(config.pipSize); - - _textOffset = Offset( - size.width - 44, - coord - 5, - ); } else { - _labelRect = Rect.fromCenter( - center: Offset( - coord, - size.height - 6, - ), - width: _width, - height: _height, - ); - final DateTime _dateTime = DateTime.fromMillisecondsSinceEpoch(epochFromX!(coord), isUtc: true); - _labelString = DateFormat('HH:mm:ss').format(_dateTime); - - _textOffset = Offset( - coord - 19, - size.height - 13, - ); + _labelString = DateFormat('MM-dd HH:mm:ss').format(_dateTime); } + const double padding = 6; final TextPainter textPainter = TextPainter( text: TextSpan( text: _labelString, style: TextStyle( - color: horizontalBarrierStyle.titleBackgroundColor, - fontSize: 10, + color: calculateTextColor(color), + fontSize: horizontalBarrierStyle.textStyle.fontSize, + height: horizontalBarrierStyle.textStyle.height, + fontFeatures: horizontalBarrierStyle.textStyle.fontFeatures, + fontWeight: horizontalBarrierStyle.textStyle.fontWeight, ), ), textDirection: ui.TextDirection.ltr, + maxLines: 1, )..layout(maxWidth: size.width); - final RRect roundedRect = - RRect.fromRectAndRadius(_labelRect, const Radius.circular(4)); + final double rectWidth = textPainter.width + 2 * padding; + const double rectHeight = 24; + + RRect rect = RRect.zero; + + if (drawingType == 'horizontal') { + rect = RRect.fromLTRBR( + size.width - rectWidth - 1, + coord - (rectHeight / 2), + size.width - 4, + coord + (rectHeight / 2), + const Radius.circular(4), + ); + } else { + rect = RRect.fromLTRBR( + coord - (rectWidth / 2), + size.height - rectHeight, + coord + (rectWidth / 2), + size.height, + const Radius.circular(4), + ); + } canvas.drawRRect( - roundedRect, - Paint()..color = horizontalBarrierStyle.color, + rect, + Paint() + ..color = color + ..style = PaintingStyle.fill, ); - textPainter.paint(canvas, _textOffset); + if (drawingType == 'horizontal') { + textPainter.paint( + canvas, + Offset(size.width - rectWidth + padding - 2, + coord - textPainter.height / 2)); + } else { + textPainter.paint( + canvas, + Offset(coord - textPainter.width / 2, + size.height - textPainter.height - padding + 1)); + } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart index ee864515a..cf811f551 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/ray/ray_line_drawing.dart @@ -1,6 +1,3 @@ -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; @@ -8,13 +5,17 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/line/line_drawing.dart'; import 'package:deriv_chart/src/models/tick.dart'; -import 'package:deriv_chart/src/theme/chart_theme.dart'; -import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/line/line_drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/ray/ray_drawing_tool_config.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; +import 'package:deriv_chart/src/theme/chart_theme.dart'; +import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +export 'package:deriv_chart/src/models/tick.dart'; part 'ray_line_drawing.g.dart'; @@ -123,6 +124,7 @@ class RayLineDrawing extends Drawing { drawingParts: drawingData.drawingParts, isDrawingFinished: drawingData.isDrawingFinished, isSelected: drawingData.isSelected, + isHovered: drawingData.isHovered, ), series, updatePositionCallback, @@ -141,11 +143,11 @@ class RayLineDrawing extends Drawing { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { config as RayDrawingToolConfig; @@ -158,8 +160,8 @@ class RayLineDrawing extends Drawing { quoteToY, LineDrawingToolConfig(lineStyle: lineStyle, pattern: pattern), draggableStartPoint, - setIsStartPointDragged, + setIsOverStartPoint, draggableEndPoint: draggableEndPoint, - setIsEndPointDragged: setIsEndPointDragged); + setIsOverEndPoint: setIsOverEndPoint); } } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart index 340f5f82b..9a4b53052 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/rectangle/rectangle_drawing.dart @@ -157,7 +157,7 @@ class RectangleDrawing extends Drawing { canvas.drawCircle( Offset(endXCoord, endYCoord), _markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } else if (startEdgePoint.epoch != 0 && startYCoord != 0) { @@ -165,7 +165,7 @@ class RectangleDrawing extends Drawing { canvas.drawCircle( Offset(startXCoord, startYCoord), _markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle()); } @@ -177,7 +177,7 @@ class RectangleDrawing extends Drawing { canvas ..drawRect( _rect, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle( fillStyle.color.withOpacity(0.3), lineStyle.thickness) : paint.fillPaintStyle( @@ -199,15 +199,12 @@ class RectangleDrawing extends Drawing { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { - setIsStartPointDragged(isDragged: false); - setIsEndPointDragged!(isDragged: false); - // Calculate the difference between the start marker and the tap point. final double startDx = position.dx - startXCoord; final double startDy = position.dy - startYCoord; @@ -225,12 +222,16 @@ class RectangleDrawing extends Drawing { /// Check if end point clicked if (endPointDistance <= _markerRadius) { - setIsEndPointDragged(isDragged: true); + setIsOverEndPoint!(isOverPoint: true); + } else { + setIsOverEndPoint!(isOverPoint: false); } /// Check if start point clicked if (startPointDistance <= _markerRadius) { - setIsStartPointDragged(isDragged: true); + setIsOverStartPoint(isOverPoint: true); + } else { + setIsOverStartPoint(isOverPoint: false); } return draggableStartPoint.isDragged || diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart index 1ab624ff7..97e2a54f5 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart @@ -5,7 +5,6 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/trend/trend_drawing_too import 'package:deriv_chart/src/deriv_chart/chart/crosshair/find.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -220,6 +219,12 @@ class TrendDrawing extends Drawing { final DrawingPatterns pattern = config.pattern; if (_calculator != null) { + if (maximumEpoch != 0 && minimumEpoch != 0) { + // center of rectangle + _rectCenter = quoteToY(_calculator!.min) + + ((quoteToY(_calculator!.max) - quoteToY(_calculator!.min)) / 2); + } + _startPoint = updatePositionCallback( EdgePoint( epoch: edgePoints.first.epoch, @@ -259,7 +264,7 @@ class TrendDrawing extends Drawing { } if (drawingPart == DrawingParts.marker) { - if (endEdgePoint.epoch == 0) { + if (edgePoints.length == 1) { _startPoint = updatePositionCallback( EdgePoint( epoch: edgePoints.first.epoch, quote: edgePoints.last.quote), @@ -271,7 +276,7 @@ class TrendDrawing extends Drawing { canvas.drawCircle( Offset(startXCoord, startYCoord), _markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle(), ); @@ -280,14 +285,14 @@ class TrendDrawing extends Drawing { ..drawCircle( Offset(startXCoord, _rectCenter), _markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle(), ) ..drawCircle( Offset(endXCoord, _rectCenter), _markerRadius, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyCirclePaintStyle(lineStyle.color) : paint.transparentCirclePaintStyle(), ); @@ -317,7 +322,7 @@ class TrendDrawing extends Drawing { canvas ..drawRect( _mainRect, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle( fillStyle.color.withOpacity(0.2), lineStyle.thickness) : paint.fillPaintStyle(fillStyle.color, lineStyle.thickness), @@ -328,7 +333,7 @@ class TrendDrawing extends Drawing { ) ..drawRect( _middleRect, - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle( fillStyle.color.withOpacity(0.2), lineStyle.thickness) : paint.fillPaintStyle(fillStyle.color, lineStyle.thickness), @@ -361,19 +366,20 @@ class TrendDrawing extends Drawing { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { - setIsStartPointDragged(isDragged: false); - setIsEndPointDragged!(isDragged: false); - // Calculate the difference between the start Point and the tap point. final double startDx = position.dx - startXCoord; final double startDy = position.dy - _rectCenter; + config as TrendDrawingToolConfig; + + final LineStyle lineStyle = config.lineStyle; + // Calculate the difference between the end Point and the tap point. final double endDx = position.dx - endXCoord; final double endDy = position.dy - _rectCenter; @@ -391,11 +397,15 @@ class TrendDrawing extends Drawing { } if (startPointDistance <= _markerRadius) { - setIsStartPointDragged(isDragged: true); + setIsOverStartPoint(isOverPoint: true); + } else { + setIsOverStartPoint(isOverPoint: false); } if (endPointDistance <= _markerRadius) { - setIsEndPointDragged(isDragged: true); + setIsOverEndPoint!(isOverPoint: true); + } else { + setIsOverEndPoint!(isOverPoint: false); } // For clicking the center line @@ -416,7 +426,9 @@ class TrendDrawing extends Drawing { _isClickedOnRectangleBoundary(_middleRect, position) || startPointDistance <= _markerRadius || endPointDistance <= _markerRadius || - lineHeight <= _touchTolerance; + (lineHeight <= lineStyle.thickness + 6 && + position.dx > startXCoord && + position.dx < endXCoord); } return false; } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart index 7744246dd..6540598d5 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing_creator.dart @@ -1,10 +1,11 @@ // ignore_for_file: use_setters_to_change_properties -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/crosshair/find.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_creator.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart'; +import 'package:deriv_chart/src/models/tick.dart'; import 'package:flutter/material.dart'; import '../data_model/drawing_parts.dart'; diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart index 57909bccb..c2d7a82a7 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/vertical/vertical_drawing.dart @@ -2,7 +2,6 @@ import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dar import 'package:deriv_chart/src/add_ons/drawing_tools_ui/vertical/vertical_drawing_tool_config.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/draggable_edge_point.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_paint_style.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; @@ -104,19 +103,22 @@ class VerticalDrawing extends Drawing { canvas.drawLine( Offset(xCoord, startY), Offset(xCoord, endingY), - drawingData.isSelected + drawingData.shouldHighlight ? paint.glowyLinePaintStyle(lineStyle.color, lineStyle.thickness) : paint.linePaintStyle(lineStyle.color, lineStyle.thickness), ); - paintDrawingLabel( - canvas, - size, - xCoord, - 'vertical', - theme, - chartConfig!, - epochFromX: epochFromX, - ); + if (config.enableLabel) { + paintDrawingLabel( + canvas, + size, + xCoord, + 'vertical', + theme, + chartConfig!, + epochFromX: epochFromX, + color: lineStyle.color, + ); + } } } } @@ -130,11 +132,11 @@ class VerticalDrawing extends Drawing { double Function(double y) quoteToY, DrawingToolConfig config, DraggableEdgePoint draggableStartPoint, - void Function({required bool isDragged}) setIsStartPointDragged, { + void Function({required bool isOverPoint}) setIsOverStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - void Function({required bool isDragged})? setIsMiddlePointDragged, - void Function({required bool isDragged})? setIsEndPointDragged, + void Function({required bool isOverPoint})? setIsOverMiddlePoint, + void Function({required bool isOverPoint})? setIsOverEndPoint, }) { config as VerticalDrawingToolConfig; diff --git a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart index f2e3fd013..43d542603 100644 --- a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart +++ b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart @@ -1,4 +1,5 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/gestures/gesture_manager.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; @@ -8,7 +9,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'animated_active_marker.dart'; import 'marker.dart'; -import 'marker_series.dart'; /// Layer with markers. class MarkerArea extends StatefulWidget { diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index 5f9c3c69f..a19305ee5 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -9,15 +9,12 @@ import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_data_pai import 'package:deriv_chart/src/deriv_chart/chart/custom_painters/chart_painter.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_area.dart'; import 'package:deriv_chart/src/deriv_chart/chart/loading_animation.dart'; -import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; -import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../../misc/callbacks.dart'; -import '../../theme/chart_theme.dart'; import 'basic_chart.dart'; +import 'multiple_animated_builder.dart'; import 'data_visualization/annotations/chart_annotation.dart'; import 'data_visualization/chart_data.dart'; import 'data_visualization/chart_series/data_series.dart'; @@ -26,7 +23,10 @@ import 'data_visualization/markers/marker_series.dart'; import 'data_visualization/models/animation_info.dart'; import 'data_visualization/models/chart_object.dart'; import 'helpers/functions/helper_functions.dart'; -import 'multiple_animated_builder.dart'; +import '../../misc/callbacks.dart'; +import '../../theme/chart_theme.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; +import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; /// The main chart to display in the chart widget. class MainChart extends BasicChart { @@ -348,8 +348,9 @@ class _ChartImplementationState extends BasicChartState { ), ), _buildDrawingToolChart(), - if (!widget.drawingTools.isDrawingMoving) - kIsWeb ? _buildCrosshairAreaWeb() : _buildCrosshairArea(), + if (kIsWeb) _buildCrosshairAreaWeb(), + if (!kIsWeb && !widget.drawingTools.isDrawingMoving) + _buildCrosshairArea(), if (widget.showScrollToLastTickButton && _isScrollToLastTickAvailable) Positioned( diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index ec0d3556e..c086a609f 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -50,6 +50,7 @@ class DerivChart extends StatefulWidget { this.chartAxisConfig = const ChartAxisConfig(), this.indicatorsRepo, this.drawingToolsRepo, + this.drawingTools, this.maxCurrentTickOffset, this.msPerPx, this.minIntervalWidth, @@ -155,6 +156,9 @@ class DerivChart extends StatefulWidget { /// Chart's drawings final Repository? drawingToolsRepo; + /// Drawing tools + final DrawingTools? drawingTools; + @override _DerivChartState createState() => _DerivChartState(); } @@ -171,13 +175,14 @@ class _DerivChartState extends State { super.initState(); _initRepos(); - loadSavedIndicatorsAndDrawingTools(); } @override void didUpdateWidget(covariant DerivChart oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.activeSymbol != oldWidget.activeSymbol) { + + if (widget.drawingToolsRepo == null && + widget.activeSymbol != oldWidget.activeSymbol) { loadSavedIndicatorsAndDrawingTools(); } } @@ -195,6 +200,9 @@ class _DerivChartState extends State { onEditCallback: showDrawingToolsDialog, currentSymbol: widget.activeSymbol, ); + if (widget.drawingToolsRepo == null) { + loadSavedIndicatorsAndDrawingTools(); + } } Future loadSavedIndicatorsAndDrawingTools() async { @@ -202,7 +210,9 @@ class _DerivChartState extends State { final List> _stateRepos = >[_indicatorsRepo, _drawingToolsRepo]; - _stateRepos.asMap().forEach((int index, dynamic element) { + _stateRepos + .asMap() + .forEach((int index, AddOnsRepository element) { try { element.loadFromPrefs(prefs, widget.activeSymbol); } on Exception { @@ -298,7 +308,7 @@ class _DerivChartState extends State { .items .where((IndicatorConfig config) => !config.isOverlay) ], - drawingTools: _drawingTools, + drawingTools: widget.drawingTools ?? _drawingTools, markerSeries: widget.markerSeries, theme: widget.theme, onCrosshairAppeared: widget.onCrosshairAppeared, diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index 5414f4668..71416f312 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -1,14 +1,15 @@ +import 'package:collection/collection.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_tool_widget.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; +import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:deriv_chart/src/models/chart_config.dart'; /// A wigdet for encapsulating drawing tools related business logic class DrawingToolChart extends StatefulWidget { @@ -82,15 +83,15 @@ class _DrawingToolChartState extends State { fit: StackFit.expand, children: [ if (drawings.isNotEmpty) - ...drawings.map((DrawingData? drawingData) => DrawingPainter( + ...drawings.mapIndexed((int index, DrawingData? drawingData) => + DrawingPainter( key: ValueKey(drawingData!.id), drawingData: drawingData, - drawingConfig: configs - .where((DrawingToolConfig config) => - config.configId == drawingData.id) - .firstOrNull, quoteToCanvasY: widget.chartQuoteToCanvasY, + onMouseEnter: () => widget.drawingTools.onMouseEnter(index), + onMouseExit: () => widget.drawingTools.onMouseExit(index), quoteFromCanvasY: widget.chartQuoteFromCanvasY, + isDrawingMoving: widget.drawingTools.isDrawingMoving, onMoveDrawing: widget.drawingTools.onMoveDrawing, setIsDrawingSelected: _setIsDrawingSelected, selectedDrawingTool: widget.drawingTools.selectedDrawingTool, diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart index 806f20c55..f5bd84e7b 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tools.dart @@ -1,14 +1,25 @@ import 'package:collection/collection.dart'; -import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; -import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; - +import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; +import 'package:deriv_chart/src/add_ons/repository.dart'; import '../chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +/// Callback to notify mouse enter over the addon. +typedef OnMouseEnterCallback = void Function(int index); + +/// Callback to notify mouse exit over the addon. +typedef OnMouseExitCallback = void Function(int index); + /// This calss is used to keep all the methods and data related to drawing tools /// Which need to be shared between the DerivChart and the DrawingToolsDialog class DrawingTools { + /// Initializes + DrawingTools({ + this.onMouseEnterCallback, + this.onMouseExitCallback, + }); + /// A flag to show when to stop drawing only for drawings which don't have /// fixed number of points like continuous drawing bool? shouldStopDrawing; @@ -22,6 +33,12 @@ class DrawingTools { /// For tracking the drawing movement bool isDrawingMoving = false; + /// Callback to notify mouse enter over the addon. + OnMouseEnterCallback? onMouseEnterCallback; + + /// Callback to notify mouse exit over the addon. + OnMouseExitCallback? onMouseExitCallback; + /// Remove unfinished drawings before openning the dialog. /// For the scenario where the user adds part of a drawing /// and then opens the dialog. @@ -70,6 +87,7 @@ class DrawingTools { id: drawingId, drawingParts: drawingParts, isDrawingFinished: isDrawingFinished, + isSelected: true, ), ); } @@ -109,6 +127,16 @@ class DrawingTools { isDrawingMoving = isDrawingMoved; } + /// Called when mouse enters over a drawing tool. + void onMouseEnter(int index) { + onMouseEnterCallback?.call(index); + } + + /// Called when mouse leaves over a drawing tool. + void onMouseExit(int index) { + onMouseExitCallback?.call(index); + } + /// A method to get the list of drawing data from the repository List getDrawingData() => drawingToolsRepo != null ? drawingToolsRepo!.items diff --git a/lib/src/misc/callbacks.dart b/lib/src/misc/callbacks.dart index b0ea2dcdb..86f7f94b0 100644 --- a/lib/src/misc/callbacks.dart +++ b/lib/src/misc/callbacks.dart @@ -17,13 +17,15 @@ typedef VisibleQuoteAreaChangedCallback = Function( /// Called when the crosshair is moved /// -/// [ev] is an instance of PointerHoverEvent +/// [globalPosition] of the pointer. +/// [localPosition] of the pointer. /// [epochToX] is a function to convert epoch value to canvas X. /// [quoteToY] is a function to convert value(quote) value to canvas Y. /// [epochFromX] is a function to convert canvas X to epoch value. /// [quoteFromY] is a function to convert canvas Y to value(quote). typedef OnCrosshairHover = void Function( - PointerHoverEvent ev, + Offset globalPosition, + Offset localPosition, EpochToX epochToX, QuoteToY quoteToY, EpochFromX epochFromX, @@ -32,14 +34,16 @@ typedef OnCrosshairHover = void Function( /// Called when the crosshair is moved /// -/// [ev] is an instance of PointerHoverEvent +/// [globalPosition] of the pointer. +/// [localPosition] of the pointer. /// [epochToX] is a function to convert epoch value to canvas X. /// [quoteToY] is a function to convert value(quote) value to canvas Y. /// [epochFromX] is a function to convert canvas X to epoch value. /// [quoteFromY] is a function to convert canvas Y to value(quote). /// [config] is the config of the Indicator if it the hover is in BottomChart. typedef OnCrosshairHoverCallback = void Function( - PointerHoverEvent ev, + Offset globalPosition, + Offset localPosition, EpochToX epochToX, QuoteToY quoteToY, EpochFromX epochFromX, diff --git a/lib/src/misc/debounce.dart b/lib/src/misc/debounce.dart index 8cc80a2b3..17eb3406b 100644 --- a/lib/src/misc/debounce.dart +++ b/lib/src/misc/debounce.dart @@ -17,7 +17,7 @@ class Debounce { /// The function to be executed after the debounce window. VoidCallback? action; - /// Internal timer to manage the debounce window. + /// Timer to manage the debounce window and to cancel. Timer? _timer; /// Runs the provided [action] function after the debounce window. diff --git a/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart b/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart index 1c2ffa21a..5f60a3f20 100644 --- a/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart +++ b/test/deriv_chart/chart/data_visualization/chart_series/indicator_series/abstract_single_indicator_series_test.dart @@ -1,8 +1,6 @@ import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/abstract_single_indicator_series.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/models/indicator_options.dart'; -import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart'; -import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_technical_analysis/deriv_technical_analysis.dart'; import 'package:flutter_test/flutter_test.dart'; From c2ec0dfb7eff5f7e2e1691ae77413cbb0c3bd166 Mon Sep 17 00:00:00 2001 From: Bahar Date: Thu, 12 Oct 2023 11:19:04 +0800 Subject: [PATCH 21/29] add a todo comment (#254) --- .../drawing_tools/continuous/continuous_line_drawing.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart index 3cab8e564..864ae327d 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/continuous/continuous_line_drawing.dart @@ -138,6 +138,7 @@ class ContinuousLineDrawing extends Drawing { /// Draw other lines of continuous which need more than 1 tap to draw if (config.edgePoints.length > 2) { + // TODO(NA): Refactor this code to avoid using indexOf config.edgePoints .where((EdgePoint element) => config.edgePoints.indexOf(element) > 1 && From ea8c2787c23534e4bfd11e1947e545310138c756 Mon Sep 17 00:00:00 2001 From: balakrishna-deriv <56330681+balakrishna-deriv@users.noreply.github.com> Date: Tue, 17 Oct 2023 17:19:31 +0800 Subject: [PATCH 22/29] Add combinePaths function & Performance improvement (#245) * feat: expose chart scaling * feat: web adapter init * chore: add models * chore: fix lint issues * chore: fix issue with map * chore: fix map issue 2 * chore: update light theme colors * feat: integrate multiplier barrier and shade * feat: draggable barrier - relative and absolute * feat: add web markers support * feat: contract replay painter * chore: add interops and callbacks * chore: add util functions * chore: add flag to hide crosshair * chore: active marker drawing and update end icon * Revert "feat: draggable barrier - relative and absolute" This reverts commit 0ecba93c26852abf42953aa6b7f01969a2ee68de. * Revert "feat: integrate multiplier barrier and shade" This reverts commit 49fa8a87f4925f4ed697ee20d249ad027e9fee5d. * chore: fix instance issue * fix: marker drawings * refactor: start and end markers * chore: add digit contract toggle * refactor: tick points * chore: fix quote grid initial render * refactor: chart options * add indicatorsRepo * fix: order of repaint check * chore: add id to indicators * fix: ambiguous import issue * chore: indicator integration * refactor: add dart interop * refactor: interop * chore: remove web_adapter * chore: show y-axis on indicators * chore: remove unused import * chore: add id to series * feat: add bottom chart options * chore: add crosshair callbacks * chore: update dataFitMode * chore: show icons if the prop is null * refactor: remove unused code * refactor: marker and crosshair * fix: series type comparison * fix: review comments * refactor: remove painters related to web charts * refactor: remove web marker paintings * refactor: remove web related props * chore: remove bool compare * chore: add repository interface * refactor: modify to interface * add maxCurrentTickOffset config * chore: remove id * refactor: indicator repository * chore: add bar style to gator * chore: remove id from indicator config * chore: generate g.dart files * chore: macd add configs * chore: add roc line style * chore: add linestyle to StochasticOscillator * chore: add linestyle to william r indicator * chore: add color config to aroon indicator * chore: add ADX configs * chore: add ichimoku line styles * chore: add bollinger line styles * chore: add linestyle to ma env * chore: add line style to alligator * chore: remove id form add on * chore: update bollinger bands colors * refactor: remove marker area changes * refactor: update to _getIndicatorSeries * chore: add createAddOn callback * chore: add title to the indicator * chore: sort out review comments * feat: add expand/collapse and move icons * fix: null exception on delete indicator * chore: remove unnecessary non-nullable type * fix: pinch-to-zoom * fix: add missing title to dpo * chore: hide move icons when expanded * fix: builds the widget after animation to render the y-axis correctly * fix: pinch to zoom on mobile * refactor: make functions in repository consistent * chore: add linestyle to dpo indicator * chore: update crosshair doc * refactor: update to repo interface * Bug fixes 1 (#12) * chore: exposes scrollBy function * chore: customize msPerPx and leftMargin * chore: add second intervals * chore: configuration to change rainbow colors * Ahmad/78749/Drawing tool- Horizontal * Ahmad/78749/Drawing tool- Horizontal * adding constant file * removing the horizontal tool functionality * chore: expore series and indicator configs * chore: make line series public inorder to access it from outside the chart * chore: expose indicator series and fix williams oscillator series * chore: expose indicator series * fix: bottom title for dark theme * chore: expose make series and configs * fix: wrap expandedIndex in setState * fix: channel fill for bollinger and donchian * fix: check shouldUpdate for series to draw it when the config is changed * chore: add shouldRepaint override for MA Series * chore: fix bottom chart title color * chore: modify candle colors for light theme * fix: theme text * chore: fix theme text * fix: html mode barrier painting * fix for endIndex * removing stashed data * day seperator * revert zigzag fix * chore: remove candle style here * changing color to theme color * formatting * chore: add showLastIndicator config * chore: add pipsize for bottom indicators * chore: add last indicator alligator * chore: add pipsize to indicators #2 * chore: add pipsize and lastindicatorstyle to idnicators #3 * chore: add pipsize and lastindicatorstyle to indicators #4 * chore: add last indicator style for overlay indicators * chore: add fcb configs * refactor: timestamp variable * chore: add adx shading * chore: fix y-axis labels * Ahmad/Adx Indicator fix (#11) * smi fix * adx and gator * chore: add container to get height and width from size * chore: revert startWithDataFitMode * chore: add showDataFitButton and animation props * chore: add padding and margin to props * revert: vertical padding * chore: expose verticalPaddingFraction * fix: isLive issue * chore: revert changes * chore: customize minIntervalWidth * chore: remove showLoadingAnimationForHistoricalData customization. * chore: move assets to different package * refactor: controller fn * chore: get current msPerPx * Revert "chore: move assets to different package" This reverts commit be7fea8e24a3abf4427d51d35691794eceb55c36. * chore: add maxIntervalWidth * chore: add loading animation color * chore: add condition to show scroll to recent button * chore: call onScale update * chore: crosshair related changes --------- Co-authored-by: Ahmad Taimoor Co-authored-by: ahmadtaimoor-deriv <129935294+ahmadtaimoor-deriv@users.noreply.github.com> * chore: remove unused import * chore: add line styles for rainbow indicator * fix: merge issues * chore: add assert to RainbowIndicatorConfig * feat: add combinePaths function * chore: update document * Update lib/src/deriv_chart/chart/data_visualization/helpers/find_intersection.dart Co-authored-by: ramin-deriv <55975218+ramin-deriv@users.noreply.github.com> * chore: add animation dependency to marker area * chore: add minElapsedTimeToFollow prop * chore: add props to customize animation duration and to show blink animation * chore: update distant constant to fix flickering issue * fix: indicator exception on selecting MovingAverageType * fix: duplicate import * refactor: make duration props non nullable * fix: expanded index when bottom indicator is removed --------- Co-authored-by: balakrishna-binary <56330681+balakrishna-binary@users.noreply.github.com> Co-authored-by: Ahmad Taimoor Co-authored-by: ahmadtaimoor-deriv <129935294+ahmadtaimoor-deriv@users.noreply.github.com> Co-authored-by: ramin-deriv <55975218+ramin-deriv@users.noreply.github.com> --- .../drawing_tools_ui/distance_constants.dart | 2 +- lib/src/deriv_chart/chart/basic_chart.dart | 30 ++++- lib/src/deriv_chart/chart/chart.dart | 75 ++++++++--- .../bollinger_bands_series.dart | 5 +- .../indicators_series/ma_env_series.dart | 3 +- .../line_series/channel_fill_painter.dart | 24 +++- .../line_series/oscillator_line_painter.dart | 96 +++++++++----- .../helpers/combine_paths.dart | 122 ++++++++++++++++++ .../helpers/find_intersection.dart | 39 ++++++ lib/src/deriv_chart/chart/main_chart.dart | 43 +++--- lib/src/deriv_chart/chart/x_axis/x_axis.dart | 8 ++ .../chart/x_axis/x_axis_model.dart | 18 ++- lib/src/deriv_chart/deriv_chart.dart | 25 ++++ 13 files changed, 410 insertions(+), 80 deletions(-) create mode 100644 lib/src/deriv_chart/chart/data_visualization/helpers/combine_paths.dart create mode 100644 lib/src/deriv_chart/chart/data_visualization/helpers/find_intersection.dart diff --git a/lib/src/add_ons/drawing_tools_ui/distance_constants.dart b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart index 88125821e..92e53b5a0 100644 --- a/lib/src/add_ons/drawing_tools_ui/distance_constants.dart +++ b/lib/src/add_ons/drawing_tools_ui/distance_constants.dart @@ -3,5 +3,5 @@ class DrawingToolDistance { /// horizontal distance - static const double horizontalDistance = 999999; + static const double horizontalDistance = 99999; } diff --git a/lib/src/deriv_chart/chart/basic_chart.dart b/lib/src/deriv_chart/chart/basic_chart.dart index 60c81ad11..14e417f3a 100644 --- a/lib/src/deriv_chart/chart/basic_chart.dart +++ b/lib/src/deriv_chart/chart/basic_chart.dart @@ -17,6 +17,8 @@ import 'helpers/functions/helper_functions.dart'; import 'multiple_animated_builder.dart'; import 'y_axis/quote_grid.dart'; +const Duration _defaultDuration = Duration(milliseconds: 300); + /// The basic chart that other charts extend from. class BasicChart extends StatefulWidget { ///Initializes a basic chart. @@ -27,6 +29,8 @@ class BasicChart extends StatefulWidget { ChartAxisConfig? chartAxisConfig, Key? key, this.onQuoteAreaChanged, + this.currentTickAnimationDuration = _defaultDuration, + this.quoteBoundsAnimationDuration = _defaultDuration, }) : chartAxisConfig = chartAxisConfig ?? const ChartAxisConfig(), super(key: key); @@ -45,6 +49,12 @@ class BasicChart extends StatefulWidget { /// Callback provided by library user. final VisibleQuoteAreaChangedCallback? onQuoteAreaChanged; + /// Duration of the current tick animated transition. + final Duration currentTickAnimationDuration; + + /// Duration of quote bounds animated transition. + final Duration quoteBoundsAnimationDuration; + @override BasicChartState createState() => BasicChartState(); } @@ -75,10 +85,6 @@ class BasicChartState extends State /// Padding should be at least half of barrier label height. static const double minPadding = 10; - /// Duration of quote bounds animated transition. - final Duration quoteBoundsAnimationDuration = - const Duration(milliseconds: 300); - /// Top quote bound target for animated transition. double topBoundQuoteTarget = 60; @@ -157,6 +163,16 @@ class BasicChartState extends State widget.mainSeries.didUpdate(oldChart.mainSeries); } _playNewTickAnimation(); + + if (widget.currentTickAnimationDuration.inMilliseconds != + oldChart.currentTickAnimationDuration.inMilliseconds) { + _setupCurrentTickAnimation(); + } + + if (widget.quoteBoundsAnimationDuration.inMilliseconds != + oldChart.quoteBoundsAnimationDuration.inMilliseconds) { + _setupBoundsAnimation(); + } } @override @@ -213,7 +229,7 @@ class BasicChartState extends State void _setupCurrentTickAnimation() { _currentTickAnimationController = AnimationController( vsync: this, - duration: const Duration(milliseconds: 300), + duration: widget.currentTickAnimationDuration, ); currentTickAnimation = CurvedAnimation( parent: _currentTickAnimationController, @@ -225,12 +241,12 @@ class BasicChartState extends State topBoundQuoteAnimationController = AnimationController.unbounded( value: topBoundQuoteTarget, vsync: this, - duration: quoteBoundsAnimationDuration, + duration: widget.quoteBoundsAnimationDuration, ); bottomBoundQuoteAnimationController = AnimationController.unbounded( value: bottomBoundQuoteTarget, vsync: this, - duration: quoteBoundsAnimationDuration, + duration: widget.quoteBoundsAnimationDuration, ); /// Builds the widget once the animation is finished diff --git a/lib/src/deriv_chart/chart/chart.dart b/lib/src/deriv_chart/chart/chart.dart index 14fd8cb24..60eb55bfe 100644 --- a/lib/src/deriv_chart/chart/chart.dart +++ b/lib/src/deriv_chart/chart/chart.dart @@ -25,6 +25,8 @@ import 'data_visualization/markers/marker_series.dart'; import 'data_visualization/models/chart_object.dart'; import 'main_chart.dart'; +const Duration _defaultDuration = Duration(milliseconds: 300); + /// Interactive chart widget. class Chart extends StatefulWidget { /// Creates chart that expands to available space. @@ -54,6 +56,10 @@ class Chart extends StatefulWidget { this.msPerPx, this.minIntervalWidth, this.maxIntervalWidth, + this.minElapsedTimeToFollow = 0, + this.currentTickAnimationDuration, + this.quoteBoundsAnimationDuration, + this.showCurrentTickBlinkAnimation, this.verticalPaddingFraction, this.bottomChartTitleMargin, this.showDataFitButton, @@ -142,6 +148,20 @@ class Chart extends StatefulWidget { /// that is used for calculating the maximum msPerPx. final double? maxIntervalWidth; + /// Specifies the minimum time in milliseconds before which it can update the + /// rightBoundEpoch when the chart is in follow mode. This is used to control + /// the number of frames painted each second. + final int minElapsedTimeToFollow; + + /// Duration of the current tick animated transition. + final Duration? currentTickAnimationDuration; + + /// Duration of quote bounds animated transition. + final Duration? quoteBoundsAnimationDuration; + + /// Whether to show current tick blink animation or not. + final bool? showCurrentTickBlinkAnimation; + /// Fraction of the chart's height taken by top or bottom padding. /// Quote scaling (drag on quote area) is controlled by this variable. final double? verticalPaddingFraction; @@ -213,6 +233,25 @@ class _ChartState extends State with WidgetsBindingObserver { : ChartDefaultLightTheme()); } + void _onCrosshairHover( + Offset globalPosition, + Offset localPosition, + EpochToX epochToX, + QuoteToY quoteToY, + EpochFromX epochFromX, + QuoteFromY quoteFromY, + ) { + widget.onCrosshairHover?.call( + globalPosition, + localPosition, + epochToX, + quoteToY, + epochFromX, + quoteFromY, + null, + ); + } + @override Widget build(BuildContext context) { final ChartConfig chartConfig = ChartConfig( @@ -266,6 +305,7 @@ class _ChartState extends State with WidgetsBindingObserver { msPerPx: widget.msPerPx, minIntervalWidth: widget.minIntervalWidth, maxIntervalWidth: widget.maxIntervalWidth, + minElapsedTimeToFollow: widget.minElapsedTimeToFollow, child: Column( children: [ Expanded( @@ -292,24 +332,14 @@ class _ChartState extends State with WidgetsBindingObserver { verticalPaddingFraction: widget.verticalPaddingFraction, showCrosshair: widget.showCrosshair, onCrosshairDisappeared: widget.onCrosshairDisappeared, - onCrosshairHover: ( - Offset globalPosition, - Offset localPosition, - EpochToX epochToX, - QuoteToY quoteToY, - EpochFromX epochFromX, - QuoteFromY quoteFromY, - ) => - widget.onCrosshairHover?.call( - globalPosition, - localPosition, - epochToX, - quoteToY, - epochFromX, - quoteFromY, - null, - ), + onCrosshairHover: _onCrosshairHover, loadingAnimationColor: widget.loadingAnimationColor, + currentTickAnimationDuration: + widget.currentTickAnimationDuration ?? _defaultDuration, + quoteBoundsAnimationDuration: + widget.quoteBoundsAnimationDuration ?? _defaultDuration, + showCurrentTickBlinkAnimation: + widget.showCurrentTickBlinkAnimation ?? true, ), ), if (bottomSeries?.isNotEmpty ?? false) @@ -455,5 +485,16 @@ class _ChartState extends State with WidgetsBindingObserver { _controller.onScrollToLastTick?.call(animate: false); } } + + // Check if the the expanded bottom indicator is moved/removed. + if (expandedIndex != null && + oldWidget.bottomConfigs?.length != widget.bottomConfigs?.length && + expandedIndex! < (oldWidget.bottomConfigs?.length ?? 0)) { + final int? newIndex = widget.bottomConfigs + ?.indexOf(oldWidget.bottomConfigs![expandedIndex!]); + if (newIndex != expandedIndex) { + expandedIndex = newIndex == -1 ? null : newIndex; + } + } } } diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart index edc9497d0..d0ac3c3ea 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/bollinger_bands_series.dart @@ -50,25 +50,22 @@ class BollingerBandSeries extends Series { /// Upper series late SingleIndicatorSeries upperSeries; - /// Inner Series final List innerSeries = []; - /// Bollinger bands options final BollingerBandsOptions bbOptions; /// Field Indicator final Indicator _fieldIndicator; - @override SeriesPainter? createPainter() { final StandardDeviationIndicator standardDeviation = StandardDeviationIndicator(_fieldIndicator, bbOptions.period); final CachedIndicator bbmSMA = - MASeries.getMAIndicator(_fieldIndicator, bbOptions); + MASeries.getMAIndicator(_fieldIndicator, bbOptions)..calculateValues(); middleSeries = SingleIndicatorSeries( painterCreator: (Series series) => diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart index 11e5e863b..23373ab52 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/indicators_series/ma_env_series.dart @@ -61,7 +61,8 @@ class MAEnvSeries extends Series { @override SeriesPainter? createPainter() { final CachedIndicator smaIndicator = - MASeries.getMAIndicator(_fieldIndicator, maEnvOptions!); + MASeries.getMAIndicator(_fieldIndicator, maEnvOptions!) + ..calculateValues(); lowerSeries = SingleIndicatorSeries( painterCreator: ( diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart index 283af44f2..daa137656 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/channel_fill_painter.dart @@ -2,9 +2,11 @@ import 'dart:ui' as ui; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/line_series/line_painter.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/helpers/combine_paths.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../data_painter.dart'; @@ -93,10 +95,24 @@ class ChannelFillPainter extends DataPainter> { addAreaPath(canvas, size, firstLineAreaPath, firstDataPathInfo.startPosition.dx, firstDataPathInfo.endPosition.dx); - final Path firstUpperChannelFill = Path.combine( - PathOperation.intersect, channelFillPath, firstLineAreaPath); - final Path secondUpperChannelFill = Path.combine( - PathOperation.difference, channelFillPath, firstLineAreaPath); + Path firstUpperChannelFill, secondUpperChannelFill; + + if (kIsWeb) { + final (Path, Path) paths = combinePaths( + firstSeries, + firstSeries.entries ?? [], + secondSeries.entries ?? [], + epochToX, + quoteToY); + + firstUpperChannelFill = paths.$1; + secondUpperChannelFill = paths.$2; + } else { + firstUpperChannelFill = Path.combine( + PathOperation.intersect, channelFillPath, firstLineAreaPath); + secondUpperChannelFill = Path.combine( + PathOperation.difference, channelFillPath, firstLineAreaPath); + } canvas ..drawPath(firstDataPathInfo.path, firstLinePaint) diff --git a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart index 23067059a..fbd6b085f 100644 --- a/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart +++ b/lib/src/deriv_chart/chart/data_visualization/chart_series/line_series/oscillator_line_painter.dart @@ -1,9 +1,11 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/helpers/combine_paths.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/functions/helper_functions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_text.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/painting_styles/barrier_style.dart'; import 'package:deriv_chart/src/theme/painting_styles/line_style.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../../chart_data.dart'; @@ -78,40 +80,74 @@ class OscillatorLinePainter extends LinePainter { canvas.drawPath(linePath.path, _linePaint!); if (_topHorizontalLine != null) { - final Path bottomAreaPath = Path.from(linePath.path) - ..lineTo(linePath.endPosition.dx, size.height) - ..lineTo(linePath.startPosition.dx, size.height); - final Path topRect = Path() - ..addRect( - Rect.fromLTRB( - linePath.startPosition.dx, - 0, - linePath.endPosition.dx, - quoteToY(_topHorizontalLine!), - ), - ); - - final Path topIntersections = - Path.combine(PathOperation.intersect, bottomAreaPath, topRect); + Path topIntersections; + + if (kIsWeb) { + final List horizontalLineEntries = series.entries! + .map((Tick entry) => + Tick(epoch: entry.epoch, quote: _topHorizontalLine!)) + .toList(); + topIntersections = combinePaths( + series, + series.entries ?? [], + horizontalLineEntries, + epochToX, + quoteToY, + ).$1; + } else { + final Path bottomAreaPath = Path.from(linePath.path) + ..lineTo(linePath.endPosition.dx, size.height) + ..lineTo(linePath.startPosition.dx, size.height); + final Path topRect = Path() + ..addRect( + Rect.fromLTRB( + linePath.startPosition.dx, + 0, + linePath.endPosition.dx, + quoteToY(_topHorizontalLine!), + ), + ); + + topIntersections = + Path.combine(PathOperation.intersect, bottomAreaPath, topRect); + } + canvas.drawPath(topIntersections, _topZonesPaint); } if (_bottomHorizontalLine != null) { - final Path topAreaPath = Path.from(linePath.path) - ..lineTo(linePath.endPosition.dx, 0) - ..lineTo(linePath.startPosition.dx, 0); - final Path bottomRect = Path() - ..addRect( - Rect.fromLTRB( - linePath.startPosition.dx, - quoteToY(_bottomHorizontalLine!), - linePath.endPosition.dx, - size.height, - ), - ); - - final Path bottomIntersection = - Path.combine(PathOperation.intersect, topAreaPath, bottomRect); + Path bottomIntersection; + + if (kIsWeb) { + final List horizontalLineEntries = series.entries! + .map((Tick entry) => + Tick(epoch: entry.epoch, quote: _bottomHorizontalLine!)) + .toList(); + + bottomIntersection = combinePaths( + series, + series.entries ?? [], + horizontalLineEntries, + epochToX, + quoteToY, + ).$2; + } else { + final Path topAreaPath = Path.from(linePath.path) + ..lineTo(linePath.endPosition.dx, 0) + ..lineTo(linePath.startPosition.dx, 0); + final Path bottomRect = Path() + ..addRect( + Rect.fromLTRB( + linePath.startPosition.dx, + quoteToY(_bottomHorizontalLine!), + linePath.endPosition.dx, + size.height, + ), + ); + bottomIntersection = + Path.combine(PathOperation.intersect, topAreaPath, bottomRect); + } + canvas.drawPath(bottomIntersection, _bottomZonesPaint); } diff --git a/lib/src/deriv_chart/chart/data_visualization/helpers/combine_paths.dart b/lib/src/deriv_chart/chart/data_visualization/helpers/combine_paths.dart new file mode 100644 index 000000000..32702322b --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/helpers/combine_paths.dart @@ -0,0 +1,122 @@ +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/data_series.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/helpers/find_intersection.dart'; +import 'package:deriv_chart/src/models/tick.dart'; +import 'package:flutter/material.dart'; + +/// Combines and intersects two paths to generate upper and lower paths. +/// +/// Given two paths, this function intersects them to find the common portion +/// and splits the paths into an upper path and a lower path. +(Path, Path) combinePaths( + DataSeries firstSeries, + List firstSeriesEntries, + List secondSeriesEntries, + EpochToX epochToX, + QuoteToY quoteToY, +) { + final Path upperPath = Path(); + final Path lowerPath = Path(); + + Offset convertTickToPoint(Tick tick, int i) => + Offset(epochToX(firstSeries.getEpochOf(tick, i)), quoteToY(tick.quote)); + + void addPoint(List pointsList, Tick tick, int i) { + pointsList.add(convertTickToPoint(tick, i)); + } + + void addPath(Path path, {bool isTopBand = true}) { + if (isTopBand) { + upperPath.addPath(path, Offset.zero); + } else { + lowerPath.addPath(path, Offset.zero); + } + } + + List topPoints = []; + List bottomPoints = []; + + bool isTopBand = false, isBottomBand = false; + + final int startIndex = firstSeries.visibleEntries.startIndex; + final int endIndex = firstSeries.visibleEntries.endIndex; + + for (int i = startIndex; i <= endIndex - 1; i++) { + final Tick firstSeriesTick = firstSeriesEntries[i]; + final Tick secondSeriesTick = secondSeriesEntries[i]; + + final bool newRegion = + (isTopBand && firstSeriesTick.quote < secondSeriesTick.quote) || + (isBottomBand && firstSeriesTick.quote > secondSeriesTick.quote); + + if (newRegion) { + // Find intersection point and creates a path + // with the current set of points. + final Tick? firstSeriesPreviousTick = firstSeriesEntries[i - 1]; + final Tick? secondSeriesPreviousTick = secondSeriesEntries[i - 1]; + Offset? intersecion; + + if (firstSeriesPreviousTick != null && secondSeriesPreviousTick != null) { + intersecion = findIntersection( + convertTickToPoint(firstSeriesTick, i), + convertTickToPoint(firstSeriesPreviousTick, i - 1), + convertTickToPoint(secondSeriesTick, i), + convertTickToPoint(secondSeriesPreviousTick, i - 1), + ); + + if (intersecion != null) { + topPoints.add(intersecion); + } + } + + addPath(_createPath(topPoints, bottomPoints), isTopBand: isTopBand); + + topPoints = intersecion != null ? [intersecion] : []; + bottomPoints = []; + isTopBand = isBottomBand = false; + } else if (firstSeriesTick.quote == secondSeriesTick.quote) { + addPoint(topPoints, firstSeriesTick, i); + // When both the first and second series's quotes are the same, + // it is considered an intersection point, + // and a path with the current set of points is created. + addPath(_createPath(topPoints, bottomPoints), isTopBand: isTopBand); + topPoints = []; + bottomPoints = []; + isTopBand = isBottomBand = false; + } + + if (firstSeriesTick.quote > secondSeriesTick.quote) { + addPoint(topPoints, firstSeriesTick, i); + addPoint(bottomPoints, secondSeriesTick, i); + isTopBand = true; + } else if (firstSeriesTick.quote < secondSeriesTick.quote) { + addPoint(topPoints, secondSeriesTick, i); + addPoint(bottomPoints, firstSeriesTick, i); + isBottomBand = true; + } + } + + addPath(_createPath(topPoints, bottomPoints), isTopBand: isTopBand); + + return (upperPath, lowerPath); +} + +Path _createPath(List topPoints, List bottomPoints) { + final Path path = Path(); + final List allPoints = List.from(topPoints) + ..addAll(bottomPoints.reversed) + ..add(topPoints[0]); + + for (int i = 0; i < allPoints.length; i++) { + final Offset point = allPoints[i]; + + if (i == 0) { + path.moveTo(point.dx, point.dy); + continue; + } + + path.lineTo(point.dx, point.dy); + } + + return path; +} diff --git a/lib/src/deriv_chart/chart/data_visualization/helpers/find_intersection.dart b/lib/src/deriv_chart/chart/data_visualization/helpers/find_intersection.dart new file mode 100644 index 000000000..17491bada --- /dev/null +++ b/lib/src/deriv_chart/chart/data_visualization/helpers/find_intersection.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +/// Find intersection point of two lines. +/// +/// [p1] is first point of line 1. +/// [p2] is second point of line 1. +/// [p3] is first point of line 2. +/// [p4] is second point of line 2. +/// +/// Returns `null` if the two lines don't have an intersection. +Offset? findIntersection(Offset p1, Offset p2, Offset p3, Offset p4) { + if ((p1.dx == p2.dx && p1.dy == p2.dy) || + (p3.dx == p4.dx && p3.dy == p4.dy)) { + return null; + } + + final double denominator = + (p4.dy - p3.dy) * (p2.dx - p1.dx) - (p4.dx - p3.dx) * (p2.dy - p1.dy); + + if (denominator == 0) { + return null; + } + + final double ua = + ((p4.dx - p3.dx) * (p1.dy - p3.dy) - (p4.dy - p3.dy) * (p1.dx - p3.dx)) / + denominator; + final double ub = + ((p2.dx - p1.dx) * (p1.dy - p3.dy) - (p2.dy - p1.dy) * (p1.dx - p3.dx)) / + denominator; + + if (ua < 0 || ua > 1 || ub < 0 || ub > 1) { + return null; + } + + final double x = p1.dx + ua * (p2.dx - p1.dx); + final double y = p1.dy + ua * (p2.dy - p1.dy); + + return Offset(x, y); +} diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index a19305ee5..38586d4b3 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -49,6 +49,9 @@ class MainChart extends BasicChart { this.annotations, this.verticalPaddingFraction, this.loadingAnimationColor, + this.showCurrentTickBlinkAnimation = true, + super.currentTickAnimationDuration, + super.quoteBoundsAnimationDuration, double opacity = 1, ChartAxisConfig? chartAxisConfig, VisibleQuoteAreaChangedCallback? onQuoteAreaChanged, @@ -119,6 +122,9 @@ class MainChart extends BasicChart { /// The color of the loading animation. final Color? loadingAnimationColor; + /// Whether to show current tick blink animation or not. + final bool showCurrentTickBlinkAnimation; + @override _ChartImplementationState createState() => _ChartImplementationState(); } @@ -180,7 +186,9 @@ class _ChartImplementationState extends BasicChartState { didUpdateChartData(oldChart); - if (widget.isLive != oldChart.isLive) { + if (widget.isLive != oldChart.isLive || + widget.showCurrentTickBlinkAnimation != + oldChart.showCurrentTickBlinkAnimation) { _updateBlinkingAnimationStatus(); } @@ -191,7 +199,7 @@ class _ChartImplementationState extends BasicChartState { } void _updateBlinkingAnimationStatus() { - if (widget.isLive) { + if (widget.isLive && widget.showCurrentTickBlinkAnimation) { _currentTickBlinkingController.repeat(reverse: true); } else { _currentTickBlinkingController @@ -293,12 +301,16 @@ class _ChartImplementationState extends BasicChartState { _currentTickBlinkingController = AnimationController( vsync: this, duration: const Duration(milliseconds: 500), - )..repeat(reverse: true); + ); _currentTickBlinkAnimation = CurvedAnimation( parent: _currentTickBlinkingController, curve: Curves.easeInOut, ); + + if (widget.showCurrentTickBlinkAnimation) { + _currentTickBlinkingController.repeat(reverse: true); + } } @override @@ -335,18 +347,7 @@ class _ChartImplementationState extends BasicChartState { super.build(context), _buildSeries(), _buildAnnotations(), - if (widget.markerSeries != null) - MultipleAnimatedBuilder( - animations: [ - currentTickAnimation, - topBoundQuoteAnimationController, - bottomBoundQuoteAnimationController, - ], - builder: (_, __) => MarkerArea( - markerSeries: widget.markerSeries!, - quoteToCanvasY: chartQuoteToCanvasY, - ), - ), + if (widget.markerSeries != null) _buildMarkerArea(), _buildDrawingToolChart(), if (kIsWeb) _buildCrosshairAreaWeb(), if (!kIsWeb && !widget.drawingTools.isDrawingMoving) @@ -486,6 +487,18 @@ class _ChartImplementationState extends BasicChartState { ), ); + Widget _buildMarkerArea() => MultipleAnimatedBuilder( + animations: [ + currentTickAnimation, + topBoundQuoteAnimationController, + bottomBoundQuoteAnimationController, + ], + builder: (BuildContext context, _) => MarkerArea( + markerSeries: widget.markerSeries!, + quoteToCanvasY: chartQuoteToCanvasY, + ), + ); + Widget _buildDataFitButton() { final XAxisModel xAxis = context.read(); return Material( diff --git a/lib/src/deriv_chart/chart/x_axis/x_axis.dart b/lib/src/deriv_chart/chart/x_axis/x_axis.dart index 18294d403..d5ba688f6 100644 --- a/lib/src/deriv_chart/chart/x_axis/x_axis.dart +++ b/lib/src/deriv_chart/chart/x_axis/x_axis.dart @@ -30,6 +30,7 @@ class XAxis extends StatefulWidget { this.msPerPx, this.minIntervalWidth, this.maxIntervalWidth, + this.minElapsedTimeToFollow = 0, Key? key, }) : super(key: key); @@ -71,6 +72,11 @@ class XAxis extends StatefulWidget { /// that is used for calculating the maximum msPerPx. final double? maxIntervalWidth; + /// Specifies the minimum time in milliseconds before which it can update the + /// rightBoundEpoch when the chart is in follow mode. This is used to control + /// the number of frames painted each second. + final int minElapsedTimeToFollow; + @override _XAxisState createState() => _XAxisState(); } @@ -104,6 +110,7 @@ class _XAxisState extends State with TickerProviderStateMixin { msPerPx: widget.msPerPx, minIntervalWidth: widget.minIntervalWidth, maxIntervalWidth: widget.maxIntervalWidth, + minElapsedTimeToFollow: widget.minElapsedTimeToFollow, ); _ticker = createTicker(_model.onNewFrame)..start(); @@ -130,6 +137,7 @@ class _XAxisState extends State with TickerProviderStateMixin { isLive: widget.isLive, granularity: context.read().granularity, entries: widget.entries, + minElapsedTimeToFollow: widget.minElapsedTimeToFollow, ); } diff --git a/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart b/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart index 4291ae91f..b5bcfcae2 100644 --- a/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart +++ b/lib/src/deriv_chart/chart/x_axis/x_axis_model.dart @@ -61,6 +61,7 @@ class XAxisModel extends ChangeNotifier { double? msPerPx, double? minIntervalWidth, double? maxIntervalWidth, + int minElapsedTimeToFollow = 0, this.onScale, this.onScroll, }) { @@ -82,6 +83,7 @@ class XAxisModel extends ChangeNotifier { _dataFitMode = startWithDataFitMode; _minIntervalWidth = minIntervalWidth ?? 1; _maxIntervalWidth = maxIntervalWidth ?? 80; + _minElapsedTimeToFollow = minElapsedTimeToFollow; _updateEntries(entries); @@ -106,6 +108,8 @@ class XAxisModel extends ChangeNotifier { late double _maxIntervalWidth; + late int _minElapsedTimeToFollow; + /// Default to this interval width on granularity change. final double defaultIntervalWidth; @@ -217,10 +221,12 @@ class XAxisModel extends ChangeNotifier { _nowEpoch = (_entries?.isNotEmpty ?? false) ? _entries!.last.epoch : _nowEpoch + elapsedMs; - _lastEpoch = newNowTime; // TODO(NA): Consider refactoring the switch with OOP pattern. https://refactoring.com/catalog/replaceConditionalWithPolymorphism.html switch (_currentViewingMode) { case ViewingMode.followCurrentTick: + if (elapsedMs < _minElapsedTimeToFollow) { + return; + } _scrollTo(_rightBoundEpoch + elapsedMs); break; case ViewingMode.fitData: @@ -238,6 +244,8 @@ class XAxisModel extends ChangeNotifier { case ViewingMode.stationary: break; } + + _lastEpoch = newNowTime; } /// Updates scrolling bounds and time gaps based on the main chart's entries. @@ -317,6 +325,12 @@ class XAxisModel extends ChangeNotifier { _isLive = isLive; } + void _updateMinElapsedTimeToFollow(int? minElapsedTimeToFollow) { + if (minElapsedTimeToFollow != null) { + _minElapsedTimeToFollow = minElapsedTimeToFollow; + } + } + /// Fits available data to screen. void _fitData() { if (width != null && (_entries?.isNotEmpty ?? false)) { @@ -511,10 +525,12 @@ class XAxisModel extends ChangeNotifier { List? entries, int? minEpoch, int? maxEpoch, + int? minElapsedTimeToFollow, }) { _updateIsLive(isLive); _updateGranularity(granularity); _updateEntries(entries); + _updateMinElapsedTimeToFollow(minElapsedTimeToFollow); _minEpoch = minEpoch ?? _minEpoch; _maxEpoch = maxEpoch ?? _maxEpoch; diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index c086a609f..c484eefef 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -55,6 +55,10 @@ class DerivChart extends StatefulWidget { this.msPerPx, this.minIntervalWidth, this.maxIntervalWidth, + this.minElapsedTimeToFollow = 0, + this.currentTickAnimationDuration, + this.quoteBoundsAnimationDuration, + this.showCurrentTickBlinkAnimation, this.verticalPaddingFraction, this.bottomChartTitleMargin, this.showDataFitButton, @@ -134,6 +138,20 @@ class DerivChart extends StatefulWidget { /// that is used for calculating the maximum msPerPx. final double? maxIntervalWidth; + /// Specifies the minimum time in milliseconds before which it can update the + /// rightBoundEpoch when the chart is in follow mode. This is used to control + /// the number of frames painted each second. + final int minElapsedTimeToFollow; + + /// Duration of the current tick animated transition. + final Duration? currentTickAnimationDuration; + + /// Duration of quote bounds animated transition. + final Duration? quoteBoundsAnimationDuration; + + /// Whether to show current tick blink animation or not. + final bool? showCurrentTickBlinkAnimation; + /// Fraction of the chart's height taken by top or bottom padding. /// Quote scaling (drag on quote area) is controlled by this variable. final double? verticalPaddingFraction; @@ -326,6 +344,13 @@ class _DerivChartState extends State { msPerPx: widget.msPerPx, minIntervalWidth: widget.minIntervalWidth, maxIntervalWidth: widget.maxIntervalWidth, + minElapsedTimeToFollow: widget.minElapsedTimeToFollow, + currentTickAnimationDuration: + widget.currentTickAnimationDuration, + quoteBoundsAnimationDuration: + widget.quoteBoundsAnimationDuration, + showCurrentTickBlinkAnimation: + widget.showCurrentTickBlinkAnimation, verticalPaddingFraction: widget.verticalPaddingFraction, bottomChartTitleMargin: widget.bottomChartTitleMargin, showDataFitButton: widget.showDataFitButton, From e1faa91edb06b44e3e0421c5bb1e99c42cbe557b Mon Sep 17 00:00:00 2001 From: Bahar Date: Tue, 31 Oct 2023 11:47:49 +0800 Subject: [PATCH 23/29] bahar/Add drawing tool documentation (#257) * add_drawing_tool_documentation * add more description base on review comments * small fix * add more description --- docs/drawing_tools.md | 42 +++++++++++++++ docs/drawing_tools.png | Bin 0 -> 100055 bytes docs/how_chart_lib_works.md | 105 +++++++++++++++++++----------------- 3 files changed, 97 insertions(+), 50 deletions(-) create mode 100644 docs/drawing_tools.md create mode 100644 docs/drawing_tools.png diff --git a/docs/drawing_tools.md b/docs/drawing_tools.md new file mode 100644 index 000000000..10a5f9d84 --- /dev/null +++ b/docs/drawing_tools.md @@ -0,0 +1,42 @@ +# Drawing Tools + +The process initiates by opening the drawing tools dialog and selecting a preferred drawing tool. Subsequently, the user can add specific taps on the Deriv chart to start drawing with default configurations. + +The GestureDetector on the Deriv chart, utilized by the `drawingCreator` captures user input. Within the `onTap` method, every captured input will be added to the list of edgePoints. Simultaneously, the `drawingParts` list is created to store the drawing parts. Both lists are then passed to the `onAddDrawing` callback, which adds the complete drawing to the drawing repository and saves it in shared preferences based on the active Symbol, so the drawing data can be retrieved based on the chart's symbol. + +During the addition of drawing parts to the `drawingParts` list, each instance of the drawing is initialized. This initialization triggers the relevant `onPaint` method, allowing each drawing part to be painted to the chart. Since we maintain a list of `drawingParts`, each of which is an instance of a drawing, every drawing part has its own `onPaint` and `hitTest` methods inherited from CustomPaint. Consequently, any modifications in the drawing's position, such as dragging, result in the `repaint` and `hitTest` methods being invoked for each drawing part. For a more detailed explanation, please refer to the sections titled DrawingPainter, EdgePoints, and DraggableEdgePoints. + +Any modifications or adjustments to the drawing can be made by the user through the drawing tools dialog, it will end up in triggering an update in the drawing configuration within drawing_tools_dialog widget. + +To enable the drawings to be draggable, a distinct gesture is assigned to each drawing added to the chart. This gesture, embedded within the DrawingPainter, identifies any user taps on the drawing and designates the drawing as selected or deselected. The user can then drag the selected drawing across the chart. + +To update the position of the dragged drawing, the drawing must be repainted on the chart. This operation is performed by the CustomPaint component within the DrawingPainter. + +## Drawing Tool Chart + +DrawingToolChart widget is embedded within MainChart, enabling users to sketch particular shapes on DerivChart. This feature comprises two main parts: DrawingToolWidget and DrawingPainter. The DrawingPainter is specifically tasked with painting and repainting the drawings and is invoked for every drawing added to the chart. +In other words, for each drawing a DrawingPainter widget is added on the DrawingToolChart stack, and DrawingToolWidget is for when any drawing tool is selected. + +## DrawingToolWidget + +It assigns the task of drawing creation to the respective drawing tool creator. Each creator employs the chart's gestures to detect user inputs and initially adds the drawing to the list by invoking the onAddDrawing callback. Every drawing tool creator extends from the DrawingCreator class. + +## DrawingCreator + +It is a base class for all drawing tool creators. It is responsible for adding the drawing to the list and invoking the onAddDrawing callback. It also provides the drawing tool creator with the chart's gestures to detect user inputs. + +## DrawingPainter + +A stateful widget which is responsible for painting and repainting the drawings based on theirs configs using `CustomPaint`. Since the CustomPaint is wrapped within GestureDetector, each drawing created possesses its dedicated gesture, designed specifically for enabling the drawings to be draggable. + +In this widget we make drawings selectable by means of gesture and `hitTest` of `CustomPainter`. `hitTest` method checks if the user has clicked on a drawing or not, if yes it will return true. Inside `CustomPaint` we are calling `onPaint` and `hitTest` for each drawingPart. + +![plot](drawing_tools.png) + +## EdgePoints + +EdgePoint is a term we coined to keep the data of points required to create a drawing. For instance, a line necessitates two points, while a channel requires three. Whenever a user taps on Deriv-chart to create a drawing, a new instance of this class is generated within each drawing creator. This instance contains data obtained from Deriv_chart's gesture detector. These EdgePoints are then passed as a list to the onAddDrawing callback for drawing creation. + +## DraggableEdgePints + +This class extends from EdgePoint. It incorporates functions to compute the edgePoints' positions after they are dragged (utilizing data obtained from the DrawingPainter gestureDetector). These instances will be created as the state of the drawing_painter widget. Subsequently, they will be passed to CustomPaint, where onPaint and hitTest methods utilize them for drawing selection and repainting the drawings on the chart. diff --git a/docs/drawing_tools.png b/docs/drawing_tools.png new file mode 100644 index 0000000000000000000000000000000000000000..d9037dd9b95ba0f97e118ba2a76cbf32fab5f766 GIT binary patch literal 100055 zcmeEv2Rzm5|2VEnsgSf3%1TD|Ok{7_$L_?zv5vi0OOcUHl*-;@Ws4#t*&>mQIQGo` zKcC~cx0`;yd;i~Yf49Eh*X`o-e4hDU&wD-3$LF$wZIvZOa5CSV9L2%$d}1dKv$JqALYgCRXgNf;{-R}PH9^_h(Q=5< zva`djtXK?@aAO;|g)NIE!VVOH_ZBEaq!AKfxK)Rpm7Rl`jg^^=LzR`6mg5o!C-}j} z!NSGOtF=`hZj7+Rb|{Z>L7JPxY1t*XSXe<!VBmi?R(&=ueI5qz=OrsEbA$%ssw@)BMuLljhlPV16rYuqQk9pZWfuj1n_HU|SjrB-0|wp+yID8d|~+if^v#{uN^|KIl4W!n78Hvdd_4u-5!95QTL zwi>3))@q7Q2v#w6W-KMx+Bt6n7hwosjeSJf*qNY=QI>FX@!v{CY*6-=h8V5~lq>RE z9XS-r3V@Og6q+LJ?3}TL1Gl$Bfg%$-3v=w-Z8ZJ1F6>fmQT8?lSR{XUx`6y44zPpU z7y}`MLHl>>VK|JP6?z9Bzkn__`N%RydY^jeo5N zP3q6!7}UX%$p1bUNC7V+8zdZ}K_DTw!1Wb&f5RPwCPx#b z9YVzlj)A!&5Eg$DPeAn9A-2T{G{*nFn(smu+nj?FOVe0c#KwhvcKnUA*`N*}5Sx6( z)jZg;zYj9N_5)4wD`e~d|KFOquuSuZ%!S=7etPEO{SUz>cH8{v@wu(O{PYZp)pmY* zgklBOSNH@FY2V{u{bBb#?0)bS8bnd%D4QLd-ydijzgJDRT3f+)=!pM9 z-GdJca;)mX$%S3XAE_SPe>>HK4J*UH!od#k|E+Zo?EL>w_h7>+SwFpOW5WvZpC6-G zHS4Fx=uQ>|2AT-`2auEB+Gszrpad4$4{fAj#ljy8N~}xer)R63a#9qy%Md>RTXAjc z5kGk_)abn*6E}Hyxya)4kJAVE97u%)3#X}L|Jhs*js@B{Ryml%GH3ZV!QGAPizKLpM z^z^TL`zna^-KaJPHEs>EXWn}%k%fiT?znV~f2!wBm z)t?XJJEKWrAd|vD)C6v`Q^Wgxu;LF)pnnvtJ3<}b;pBe+57^9$U%hx(_XFE@M#bMc z{PA68#ac-}s#NCzNsix<>Q_0rxmY>>wNf2xdVTHY_%1{-s?$F}TYsN?sqZceyZo=r z>3<7*|2c&Id)b?v6U!A?_GaJqHvV|*jaBUbV)k}I+I^+7fhS01gET+{`1?1F4SLvC z*#32j-k+nOe~F^^ozUxdd57(PFv-5w@xC(Be$92q zCuoN!?k}+Jc4~KjA@hDG9Qa-4#Sr<=x1a9}{=qIeA#Hb%!9S_4|2c&Iwfi~8kIT2) ziI{)!hW@Yd!dzuVa3Hw9r?0ajcv*RQ|F!!1)~5UW6A#~IaA@cKhw||M=DYKD81lcg z@`oO~KTRua7|Y=2+YPn>@RfNdW&?LbS{kc@<7xkTjqXp2g1>fW_%;6h<5_xaJDqSl z_VT}8rTf!({+HNeTip34ME+JT>`%zXSjWXqHogoDv+t#L{WI#_pGNM#mx;eQ1_wNU zTZI1u^6*Y4+kYMpV_jB%=x1fa*swn#5AV#Bkwnet$mPrtiaL>z8l0tno2%e@8uGO$6w?Y?v9`X^-PA6Tzu|F3_G znH}ri`D4BMXH6~L>EHU+Hs&@5w_Ax^1y(Y*G4$7235c~>k(Nlit-GD9AWs-@Y=dj< zppEXU=*UmV@Vwg>*8Pzo&4u0f|4;6O`hHk$r!x#Iy0MXo-(eMFSOKhpLKAT1BEs@t z8_E8=xH`X4&-k|W)IUP+cEtN{gx;NQE@>lxJ-DSa`1#xJ3|)^13TzSP;66lfb>6p3 z3vN?`*!-(I@_wJ=k2%uu`vU>kBDguy7`lVfVC&E`hX~}RMuNK%FJWI;APo(nyZyd< z+1u}h#n!a`y?$;EZmjgf>gL}KR&iss-mmX_+qT93M#$gkB>OJ%4dHg+qEl9oHfU?N zlcE1}P|t_;C;e}j-*%b)%`v~zGqyt^w?Sa`Xn=S}aEu51+LGuySpWAd`2Tm3;O`>P zv2HRfVt-_LzSC`nk)x2vW|ao_*^1cP*?~KM*;$P+3Yo153Q8{k&pTv2IQwXh0&jjk z^^AM_x?n86f8fmW?M>w?_lgMI7Mz}B#URHA8DW8c2g?ynZ+f6-ZHE?$@<{E~_Vm$4$Vs;Z^DiKqr@i{HOF%ZxGlcgA=z2O__F zmYJOs5EevRzn*0V=YTK;znx{qsz^WCS?2%f6U)Y8m(1l{)Z{HJG)%3HE-AYhiP|%7 zrvrUu;{OE>-+%NZ%NCVF69hI1c)(KW9~?mb{=&EV`r1wO-FiR1m*~68+P>-Vuf_ZS z0sb&5*G}mOIR$?I?{9m6{QLOZ0_9hl1=i#9Kj)&_IwCF1;o$NHVCkbQ?NqS0@IzMt z7?>c<4dvj@D0|>15VN%dS3Yb%iI|{lkS?G;xb%JNfEsvhV~4$cpB+k7|N4ju)^G4z zBNeDK&=MNP24M@n_HcRox&Y|3+t)?%@a>BVwo2vTm=AFilBhPY=Q0m53HNtn?hLleS6jXzw`_=$F}PUtH%6#)BGQ)F+5lc@c+ac@m+eu zY7Ae|8!bDy4}u$RVFi$3dDRyBg|$*35(J)zEibdJ6&x~Xev>oY*7ne+E<}F2SA^LB zbD=K>^u3a8A2f8?#CFYXH#PX+%eNzLj{5<_O}i& zJLszvB4BXvff-{tOdT=K-%6CR9}0nrP@t`mIp%{PCctfm0FE{AwF^7ozu6*dh|sN2 z!1Ipz00fr^Etd!oQKHbb9$a9z69vy;!`praMNoAcJ5iJ+SPSH_2Yi44JmUyb;lR5; zV23}&0NDFA11(P+P@2weuoKc!L<(H^ELmcO9n8Un6|_GdbLBUzKG0TSt&e^QR>8KPr5&*Rrtj{@$C)zU>nKis0C`%f9xkuxT!RG9`{d@ z+}}x$`17 z78d4D5ap+(!H*A`3~26UZaI7Xrc|m_Qf0h^|D@5avg3kmEIz(p;Du zTb`pI*fbR_;_4~Jxru{ELf=nfE#JzHM?mF;g9j(=(y5sBbB$o(z8g)qUd^OtR&&|- zWm6=rIlXP@Bp#3AgSRWC9^=y)9ORy}zER_G$b_RAW+|(}pNVnz5D<5qx@?$u2M3QJ zQnP>L+{-}myCY#|Wb?ao2tFHSs=hK1D0iF5cRkuPb@qZi{D9dTFD2?Ldbm}^&SurG zpy~Gzre|r*I?Xl_(lqKMsO$77b_|nVC^sGTcO7|SNuAwO`c_WS5j2<_w1bABdW7$h zp=)`y>SU~H%XMaOpW{EnY#_vle9&$fLW6`hH<#y(mvZ;XM4%oWl6mJfBaWG45EWc*=w zx@4~`y}}I~YEN?!H{N_!ug77)O>m4_7{*icK#v4WUlN-Bnn@;IMy|DJ%6j;<;$n|0 z9fNU0>ZnF|#XB}?{Q09)>1dQfAZ9MRJW`YvU(+9mPM}WjK`|s}dzftaaUeY3M?Uw+ zHIN|qd|nvV*%w0rbzMuCt>61Wwm&KSKv$vV)FBRmQ@aSz{8wAaAk0;o0hp9&;K6%8 zEFc_K_7Qh1EF|(^D59!(q7wjMk=E!sKPb0wjtUew!r)84f+;@v1CfJ|rf2Moy*pbPYWxoO`M5xIBlEGQ?5 z&cu{IdpdcOOBm)T5*8Jwhj-JyEPiwkn5{6?WiJcXMZNvs>8fkD!76A{=G=Zapb8^eg3J8k!EC0gW@n**bxDc8QNt~NAiWYO5fOBKM*+_cvY4U&!& z^UAgi7Gd2FwUfq^K;2k==0?kBT|@|>QIzS!&*f1&BlJ2U=x4NnxvEnh;&lSxr8z2`& zV^M#2s{PICLphkI{-rmP{hNR!(syG(-@D82Ao^W>XvA(cM%j=P`gIRn9TD$V4zSxd zUu95v_Etc2spnB!*lbeML)pYUq=?-~UhAiF+NEOQ0hElpgc_MUEkA_9RTAs2tC4s+volB}02dcyn3eB>|dr@_NM)Zc>1;^ha_Kc(bp29j|hU_FhO( zE91YW6YVe=J}V#Hkitw(Yd<^qbZMGol-n&nhTm0P#(n0N-&nK!K&?OHoO*go!Bd^$ zVfr9y$>0lowy)aJJb4yOj#IrMLA@nN3ko533Bvvcdii^8x9-;YoxTyTw2*pjUC?Ds zY$f0n{ed(!vWjS)1f`M8*#(Vu#K-&j!Dl_(qAom&b-OPZ?NC~LuEJ@8-+5*rA(T-I z=50TbzP==qKPGdRKOgaWoKKksr59D~X;#G(E9O82* zvc+-SiD6y*(&(Bz$GeRYeVdM@U{wY+5!1Y7g~^le>6_!k!=8sE)raerAVexY1gD9R z@y(#m2eYWv7lun%+*qDP8z~p5N+Wg3?)$|GDC(8D&_*A59Tu0QApNdJ9#LPpafrvk zJY`OIPgGk)R_J*>xhKYr3^`X{+&d^=5q|#e;o#y40qGm5YVnBP?qDjt3)SJc@LpCI z{({GVW4*-@GLR^Gx9jz>jxfzoxp#xLKA#5Hh8hM0T{G88EbclvIhkm5=0;>t9d%e| zvGve79dd*zlu*F4(~|fUtH+Zg9co?yt940QUFWr*-Ai_#nW(U&iE2p>>zJih^Ep%!hEJz zJO6D}ak1}*Y%Q`3-Gb+l?n4|G@pXXUEU~OPPQa^oMXkBXw|v%3t*yE*?v>g8%_jkp zxRr=&`&5oTa|?FD6G>(ipl@y5)$3Yi=E+7v<_XOO;!Bh*yrWtXpRu7)k^H{^yh zN0d^I?0>)eQ;F;C#Sq=qMcJI<1B35`YF{kXtBp;9 zY=@LT+R>}V*lq3WRm<&3wAuHIk+wT}!$Tp;*<_9;{Wjf;CK4c%u87aPU%#-Hko5}9 zqt>MHu&0eE&2m+HwW5@mAkFJ|R(jg{hlv%U;M!WEyTzyHJ5LE9=B_(;hd!)uVyRV# z4QjG6>@0Y3^Kb{xWEX>ciG63)eC!?~GmX)9hwz)R?wJ#k8mk|E(c9E}b~Z{jER3VV zBDi(r&7P9yoF=fT3x?0uoIl@Vnx&v+2%!z(@3Rk0Ki&eCKwYtR*vXbrqocYQkME5+ z9`4a=cr^(Z&DI3tg&ynP%(g6OuXiG5eeC5zy0A1omF7MCajH4i%ChQ=rn!_60UF4O z@D;Ulb-5!=h*f=0+0|<|5LYWM&dNW~I%JhQ8h&d|Z_m7@ZC=vLn4TPY35D3>S7DwT z1Gi}Ew1p>y+|CO5Q8KDEgtnaHu&7f;_Mjdy9p{m#Kf=JIv7RaWDYMUvCpa_m!bhP% z%l#)h((+80Q$jpx3|yB&f2j?St=sLNpc}WlC0E|ef?V!x_QNc#14#8;Z_X^;*0*HO zAKHs9TeL2t3B@SHV>XIJVTFysMrR1bVk+ye#%?5L#@pVa9`Jk6@w6Gf-Z+vzvN~vx zqLi7QXV%7=eOULl|GgYrJ$+5Z!+o+x{EpMUyx2rvov6^c7}Ru%Pi{4;dw1O8vppA# zb#Hm(-=gc6P_g%OdE*kx_Q|mAwdS>n2qu`YtB9YAOG9*%CeewGcFKeKJ^7t)Wc$e! z%bAp6W}8i6LcaS(Bze|oj<=S2m>W*98RXN~CJXKJb91AH_aZBrNqG9aD)lA`-Cv%` zJZf7g2jg*cKXwYwn=qM2hKt~O@yCShb{wz`z+aYnpu-DdFX+~XcI(YAxu?`}rPf}g zlaG-aco}eNc=+K(S2~TgHJWxwN+eHdsR$UR&VFvrU|_V-g=nx?0h zn1)|iM7rUi^Im=4jmx;tO0CydEw?6eFS;XH#Ys{cMwE4Of?Sy~la5Ka1g3 z@!zF>f=_@Zp*0I|*oz)czVSeUWctxB^pm>u={-nVj-&YRnI2^t@~*SE@r1Oz-vo6R z_4YwKdgq;YBpv>jlN5olE(0QNp7!ZD&R%plvu@;W1sD-2>7Jsq{cWzvKsy)^`Dle7 zL(|{JBSGKZm0WbF!#}t)Nx!Q2;u{6@%amB@oAyZ=cEfkQ2=Nr$4eYMn5v^trh7r-n zh$~R1YsX(cLY}B16heSLqMf||D6ZhHn3bC3q#TviGwD4gvzWOgzvElr@X5Ap^NrKT zXQF~eA`GpW6{ShnF26Yy1E%+3fy>lEVO{P_I(h!U=cBlU1iTCW&3FkpYs6sE=TCzg zw46iZF)LAGFrFbB!^;GbqLnEqzu~uqOJ~8NGtX;uOz7&z8I80x(Nq;HF$Q>$WP5q3 z^c`cqe4>FUDBE)RkPYHz>BL&J@u~4e%ePFrYkrzcLPzO{b^3=>NNr3ul2ii_*&LgBIuDsL8rG(RXDK^2239w$iF0mO<_z6s>APTU?p}Ax?0%1d z#Oo?KJPff!Bgxp0;LFiLvkVD)-chZopZ_A~aO_c;s1zCh$byH-ztB0_ED$+bK3I2D zX-*+DL^U+hCq(XCnVmcNTtH;dyGxZA?h;7X8}<^$-wQ~d z?K&@#dp{nzsT<$Np}8b+YH^=K6V3J&c5vQXqWg*Ogi{3tZr(Lkyh{CUe1HE50-xzT1mK3F0ed0qm>Wx4-gl!IPWQ7wT^2V4 zI$(^A_w@Z3q^@=lGoQX90KMzZyeCHhX~fsIhU12n%HdwSbI59p+(68^g#|L)guj4E z1J)+my#G-S-yAbs&(ZL`XTWeru)|%y8+MxdQ3OO&3$qCr%j#P87QIm{a#aNp@G3wO zA%KySR06CV5;Kq}*mZhxB#H>h2|k~lsz4X5u9zJn@J-->zb+`&EL<@Es~g|?ok4GbhijzZNn zIz;u!PhRsffROUJ>pF)u)aV74dYzQmIr06D07%3&PtFHo3_SfU+FZ5}#<@lewR#l{ zIW&k_W4FLJtbNyy0Jjw*A31kjp~5Wa+E#~ivH)H>n#KnpG!aDV0Sj#95SuVQ@g0+E zWG`)((#q{UE4(~DxEbiE&F4N*D_k#Qeo}jB71Ia7bkw}d0V~9VzFm;{dl}_NvC-1m zC%TgMmV&rLO=@XrdTDWKTA!5jwJ3&0yoAN}P6KEKCg+Qn5WuvAcg^>cPV=4}h}9As zP+zqW^6b|xF5XW8;XWKER$521u)`OTNAQ`7*oyX3VO3y6AJ638BZe53HI5Vm4kRFJzd8Zvi%_u z-XBkcB?pi?&5aeeUIaA)v}fgxUCZ8!Ul#maZ6Q0#`Y?vhUJ&pu1K~!&X6CC-l3iLE z?f6mKLqu*1mrwB&Hoy*~?eLD>3jd5=Ws1Hq36Mw-EUEfax4@s7c{o6_pBYbJ#V}=M z@Pj{Y&>2tSg=XD~d8#NCDyM(!)g@o|`i7KFxayEEE_f1sd9doL`y_I&N+T2hi=-m5k*Y}G zE4jwA>#2V%_oso3p2ikhxi8s958t!3HRaOC?+=yUrJz%k=s%u4m7py|d2uX^$ub5> z-&1J1JZ!ZWO~*`C&0bNNc+e+6+{vf*{tKEtop3r!&2H89%-PP6+fseeH5}YFCzG2Z!N7x@I zE;v=;wqfFt^YLg;m!XW@FSz(8&)jQvoqeGg?5UbRIpQ45XINhlYqXh;igm> zehhetWZ&TTGEb|_z1m0+V6k`1%`<-F)!k6;_zow9_0z5?pWPkTGD(Ch?XPvMdoMSk zy5BC4@>S>InVw&LyFfmqS22~}L~}7xPW#+$BZ+JB6J-)~4O;2a-Xiv;uX!)l9QL?Y z5x(d?OhPLYcU_QZIs*3PI3Fo4zTIIdL7OLz&*xL3JH9YEGMy?(U_6DtJF{oHtFOA^ z!-l%N21$oKP7>iNfw^hywA33Li`-h-=cM}MD(#&dN*bL+S(Kiop-cYe4$VUqUb)>Fs_HFPh|z`Q_Gg z>6E>^?MW|^8!piIJv#B08IRx@-95|s7aWcI$mAqpj*X4`B-64l+SX2TTzpX82wX%W zFOS6`@w>7t?<g2Pr^>3wC^q9=yx8JuOpf*CSV~Y4B=Vac{q= zadhB;3aK$BO&aOcHk;l~4TVz|sCF@`+M&OEF!#%*cwEY9sR0~vx{RwXdRwlu(^q(K zHL6$6&yCctJr*6Dxv__Rv!BH0lu)_E?5CU7HZyGuqpX(KPxh@w#5XPHxE*8Gx@mLk zM2CPwcyK=Ynf?9>7Ok^sNs3`X=#IM;6&3GG>}9>V*R=-drO&J%saU;qWkNRM-ie8B zGhFwnM0xgHeaff;vq0@yNm$@zg%ggxkp3J-2ej?-*9UP+_C5n$hcQwojJ1W)8c4{N zz0pmuD_eP9<vzZv!#rJI8Q96P;DQ}4)>DFdX=enU$-$fg{CQIoM+BDAbX4w{_rp$%h)sa}k zm+^ZCMvqI|9A-MpNUykWxqfA{YGpZu-q18~YUsTW>8$3=A>~3@o{7|osi;==d`ZU{ zJ{wqYlI(f;mBk1q=h;`IH})7V%p-%@4J4E_T0YT9QT6RVaVE;f^W3@gtY|J3I`iHm z&kh?cPB14CQG|Xp>!@3CwjF%lTcVt+>pC$a)bxN_ky0?2o`|yk>5Kta$nL(Sd$e(q zb@_xNw4Oz?Ef(GTm(5zUo?iJ(Tx31;bdc%tWwhd{RhFdebn(pe+u}Y3#7?4M%bcRU z1S@oOk2;0tKx##so@UjP2qT&BT}ef%3=a0WB0kr1J+E3Rq?SPTT%kkt(XxGd5#&v2 z93dg-Ds7(ih28V>73al4A0|F)8NxInzf;uq{PrCMfpX|}(RwaZCawxJl`dm=#IYO$ z*>?55`j5H-0C+x6KX*wx+f6;>Y>L%5$B9h6wOVvzk8gK4=>T_Rz=H&8@quy|^@C>K?1P^;#7@d=oz&%q^+t(PmkZpjH2Yt z^)JswEnfd}q9bB}%T&teem!`U8RG)y&`$nDs4;390mGDxB&%f9sq^^YxkNtZ-59I-iOqnb%!83Coqwy93*2bQ#s0jA0szcQrW- z?#pes8)wtCF9ydNHSH^Skl>nMulz`7mvj_Et{-wK2{+`0+>EkI$U>xa!PHEakS9H$4SCBUfykWOl`KQ z++C~xz_aI^pTq3Qbf)^sX>}9jS_3pz8|m#Pj~(kk>6IONrb|PW9%1qJ@=AMJ+^#+o zHa-2Ba;KG0nw7If>9Il@Zzu%M^%^%eZ>&zGsrx#Xa07>{k4j?bhkVmlzPU&9Brkjb z{?*0wO-*Iv#&b7L@Pvv>Dw@nGX-uPy8fn~*GKD7vQZ{^1m_T&suT~xIXs1|Md6#Rl zDKotk-e@zj;$JBw($<)yCmmeQk)BpIZX9^3bulw7qT66oH|5Z5Toa#?;ydSPQh4u5 zh3jje;Cj87#IdA>=}-<9XK0sko^Vlw;nl*3ktfJW{Di z&+!kSlNP617oViz#PF)g9B)g$rgPDC@d3PSVLGV$qNDxGa*+_D?#85Zs)2p8(GD*) zcTG{`4x6|(*5)CZ_c)q+8pFDHLLi#tEmj{AJ594o46CCEF{T|e4ksm z_##qNpx|zCRoV0_YrbORCV}b|-@1_arnOsI{+ar-VO*_Ab#?BZ4cDt{4O@f7pSpB& z9?zcN<7_pOIUKiGmd1!4+dQvZSRR_vCrr37MilYRb;-5<^ZJquXA+_*I8G+QW?OTTMDsS`&nq_g%vuGquZg zihxQ4kj~F7YZ2)3EpZt<78N<0v3V~FM^Um4?g6^LmE!I}Pu{T4g zqQkGPCzth`Dn8px(_nw1MmSOb%w>pCS7Kw$-XK}=#@-e4`TdlhXC_K(O?mQD=?mqi z+EsdTGbzO18MPgoC>p=*FiyaiUNOXn9^1=4Ce5B|k&h6!h*HwN+19FaC`l@19sang zxI4PsZ2*5l|AgweRE;@&2n&H;QPgr#9b;xL`3fjY>LKp3R$L8=S#ZXXFmsh(Jg&s zaau_+Z12kKT-jMlw=a)h$VQ%hEvn*ab)nc(ae!9lMGX2XsEo zOMQ_CA)g(>fuKvC>P&U5+sN5^YeotRpR3kCS%J$f77MdZLAK2fF|Ro zht?z=y`Q8QLAW|lSnRgFyU`^nR|C@RT!8bZfxf3nPHtr#+WaNX>z4(;+H{TA;59L~-UshJf-pQpz$-z;1lJY3Ki%m5X^JrZB1c

NnHc0DMY zOA#_Sj>?KnBLC2NztK}Bz%K~v;!rpQWv7P)EQ5!JpA zqi;H+6?LpNdQy8I>6;8@I{ebD)MvwQ_tJENZ(`DA*98mX`hebGPJ2^FuC#nnm?I;- zKc9GID%`z49>H!=C!xuonS1#41I^6dQpW1U%zL}i3hR9QNrzsg%vtV5$EuIg2x@uI zwl@qH4h93o`KkH-6S%eeN*U8BSLcXEm({p59ai)wtglbHiL1giJg?}?@=*hoY3$9% z-34SM04jpRUp`dOxW5VPT@<_Tg-hB&ZFu}`a__G6V;z(iJ)L3^hvH~v5X1LS31S;D;o1jp+Bbyx;ZpaT zT)S6Q?BaK&k3T;TMbxO9{%rA?_`*v`Lhpk?R()uKPG#YiFjuz+{cXp|woI>xR9#6A?Hru}pgCrE`@h9Y z>!!$cYr%i_%<5XH(}s?(Y6jgxiO2ada-~=yS301T9qHppLg=hc4MtzOkv|fYW}MRZ z%uaeXoFUE~owVYuXur{zrXemECgEPBTzrx*p=^m4QtKBJL^)RH!+ShDl;gGR`CP}7 z-Fi#1Xr#iKy1LG<*(w7SHLkJfa>BXU>Mk~RhfhY%3u9r685zfIhl2YwGVkS7ST;Xg zE;s5{Et<~?bfC7Vo)mn^5uCJtxj1LhZ{=oPNJ9+n`p``Eo+ssN(v1lk{$t&?ef0LD zOb_T3!nHdy>&JjmjHvPJjtRYMqweWu!UCjZK^P;A_{#p^jS$5btsKFsn+5WB%p$WV zKkK9DDD`j|y%!9U&P!#=oXThXJSx)l@JYdQw|9nKsw`28Cp6Bpd>UDgFN?Cc->Dz) zu$T%FJs-iat}XMPmn~jlwV6Gok4L$@%lMF>E3*$7^?f@NW$ALJ80|5Klhg-N4~K=@ z*UMaY%BgBF?{dCrU;TL$_!p~A6Dz|!7w1!sI2;W;aeDm-bMwOk9}1k5Qo7QM4yUSW zhWnE<5>54non}-w!?kI?^RYXELd|SFWAN-{Rx6sMgX0m;5!I`S|4Y3|LSB`MM~a8q4E<6X6bP545s58 zT8R_gai`+Cm9N1T-c@+$)@dZB)5t%dvm*Cf8K5Q0RC}hOe?7ijK3^xUMmf!05~*K& zGrsYm%4|3bb6TQYxaZ*>W8SXF|G?~yR zIudw-;Z~YzSO6(-U{~iX^6dg%Lcq(8Q$h^l=!M|B0>&}}U#$E13%#;ETsP17kW7fa z%U<_tL-R^lZj=cttbD1Ta3!Kez1eRw+AW{b>yhWCNmFQt($pnHkDV(n=FieIzwt2w z%3*k`*st4jC1CsmZCtrUg8QfBmBmKt632#VE_B}W<%fLK7QSTR$34f}%f~+_y~{P6 zEWa`EarC(Pbcu%+J+*fJJzUuc=>oUdhL^2Lv8|xm_u3CNBqS6DRf;+KS4@l z*^KVF(AMjoy)3%<1>DS2DGWoXSF1@l<%Fd z8ESZ5Reb-(zT&Zj;YS-+Oxofi@)-2Qj-0%h=sq_F?8tc8m$RFD(Gg7#!#Zve)~|_5 z(iTKSthvr7+pRsd%g(0AZqjjo*Bm5RLc19;H29%0x|?$ESdZ52OdV5# zOBNr5UR`jl%O!1ixiofQbfQe`NXEKuiEpOfL`5dheTgYc;|yOI;jzMbdQN5b&!JY zMl@$IJguW1Aj=Ra#x$P~B#zauEXYR0_Z~xKoXS5XsXMBnG1u96|8!g@s%@jhrAl^z zlVOvzgXg^N8O9{J;JS*bwg45|lyH9WD=^Bg2C6kGLYu6?!?j|KA#n*-F9$YJz_f<_f7wf6X0)r8`+(@@sDL19HnDn<9 z3N47Md1Xv1WBN&{$4bP82nGjAchl4f>YRICWWc`C*a&7tiCPG)Bv1PPlD}DIrN$nC zq&6;)3u+z7!pBDa?J_~sKa}Z2>m_A6*|YZie&Ok(D)=DkFBBLehZF)HVUtrFs=$kV z8?xDeZ((RW#{hCCSPYCPKD;+26Lyc%{Y~8%QblCHzp1w5AW___JxUitfu7wk|3NT8 zFTygIS6t?JUrO1L>~=i@65~hwEa=d1U%--9@~q#O>a&wx{)& z$q8xs(yB<~x`B6y(eeV4&+xNn)e6{-@+_z?Rj(WkJJ%4O`2f+}OgeC!As8r=bo!q6 z(L(U`>C6W$Ne=Rw){EC25IT(s&Ksqu187h4FXG-@sMPhDU^+AIch5|&nd_Iy5Tch* zec`+La;2Z|BOCf8ax1@Jqbpmlr-R*W%3~rY@={j%9{ar@Djyzmj#=wYH$|Ktu&F~= z9Ve|2y%ZI`P#jKuKe}E~aBt#-*YT$>ND`$7T=NKC zCe!dZnBV!#@t(Alle`;Qq2^s0-CRuP1uHThTigJH%&ASCYcvfH9?l2BJ99V%p=(FA z+{5q3U+uJ^tS~6avaje@vQFC@MY*)3n2@i5HMsVtZh7%yYlwD|rkVGjzSWdjL z|CpAE{KJc)ci_4Ya^2fyMlU$~0)Lkr;cHV@pYHQnnFB7)uG#?kyTKT zoHmE++HpfqQy ztk_j7UoykZ1b4qW5@542C_Q=sk=k*4C5}$9PWh%i=^6zae)L`#VDFLir5U|5aYh*j z)1~V0C>#8qXXGapmM+IEJc7HoW{N7WmMC>Q?=8Iwaz7OG=I^c*x!oLGO-(zgE2PJs z=Jy1M>V&O~{$cR3FJ1rNjp z>y;)-)D^EPDe4zF4d>Y%^;j*AozK#c zbS*D&L45f_3VCZ@NZ%8Fc){&#K8NwWBR6iFXhiaCW~-{+(RCVp_QH>xr!6}ZFL$LA zmn)ErwARc$xxd1!S1YR6wva}eY>f}N@S>PGR_0r3f@l>fCYn)-`bcB*1Qi8E3-3~`a;b#8#irZak@QvF z(qpe_fkR>8JdTjxLihnfJ?&(2{g6Zj@@I`B7P4U$tlXQsj9f^lue4`VJbFygnOy!{ z)!h|Y+QnAoH$VT*wrIdnTiHGI@`Vc*23Im%2gv9X#e5I18PT-&XOs}pD7^lRTGxye z;QTCHReWjM?k*1A$3p}SJdwLCV7HX>=>?=yRpA?Siutjt-M8irQ7fGEiC|TVtVvnC ztzY7#RD!E2sG#I-`3#-#@TG@gr^3W2+t^-C$4vbQqveZ)?f#7lYYV=u%pqsNLbb zY44bEqT4#utG&87&X+8p?odbdwHgM%QITG0@ZRie#W@D{qR5^@;`yFruVytV-py7k zONa60<{js%@h^K;!LX2pYrE&bJ($}dC{8ft}t1Uas`$WPB3-!&NRfXIq}>f~-!5jB&Ix z$*%)~f;5w9U}i#Ig`=17ZY~g9=aSC7X&-XV4;8 zHLvVRb%j=to5Q0keT*0cuS6^N!*$)?y-m5XCvPO{_6XVZf%uSuiF<)939iPZV?9c@ zGhxh*s1N%W52T_5>cSkVIV=U=oK8nSmZL0yQa&@|pVNu`>M~{(g=z4(yczG51ezx) z%LlK^>%$Qz$})1ivLE)x1r7!`9j^;rZ6#i{OX#&apmQ4pEBUEfZ>>X~f;8mYo=lzam|gqod2V=1t;I`=3%a^5$M1b= z{Si;8k3}oqlq;og!PP<=vlGu9h)-2Ny1~c5vAWpYeNHvScab@Mj9f~;)G5mR@)^QhznE3B*ZWDdrmcu3SA$)^Wq~2A}8b z&G{4+SuG+;2FU$R!dwxgE_z$`VQ>Oo&%%-?*}$`n52czt%B_&rY`Q+6M4ez2mfB)n zMN>@(62$(Oc^@JuK4A25`mv4SlI28$M_a?v>*Gk3^z7GP46^)8nssb|8_K@KuH7TC zYE^lvncaOU7VNLz*EHuntBOIeW48Z|Ze1i}1mxW%M8}4oJq?W3qg|5)WJ2D&nPee! zQZ1awc$>K+-Oqsfc(&Q#B}jv+C_Rpg_YsiS?WV?a#v~mX$_zC=ICwIZ=5TjIboe2x z@~8gDSn;7$<9;M!e&s+*+OS58$9w!bjbgvW+-dnX|9Gre2y zm>}~i@Zw0k=X$a7BR6M;&d+8O#^gs{hY0alhU(dlRB)wJe#lo@Y4bQW++Oam&=xhk zJhwrg-Za*yp)k>R{sFyw5M&F#QfHCvDH-7F_ znd}2ui#)Z5j1Ln$V*#1&y{~1AL9|(wxyBa<4`g3@qTVr)@;inCM`c41DLte{v>0!Q z)CJRLF&*O;V~JUjbb0eIX~Lqf+%&=N;RW#ldilg%Gi&-HfpT4jGZ$@gSIKL=@>(q1llrNk8FF7@etS5aJykh3zs zV$?aJ$@k40yM!kkjhpx@^a)_9Ky)@&-Lmcip#c%=V<1^W1W2Lvj(oG0$L0(^BmxD= z#tDJQRxQZ8Xd&%$h%0seK8GT^Td=I9{!+-s!kEc5y)t&UH6I5xL8JH2w30n~Y&$$3 za%daPk0zXoh4#<)j~P2(^^)`8#pZfgtxmC*irY>^cInLwV1Kw!fdu0hI*HM1+$HpJmx}<<+WJ)6@KIz-mOwCTZxknjujMMZLhj$ROQl@ z!Fvc7-|r|S?e>jD4V!wpR6evO>!%cQqLNll*th5(XWF-%fA=Wl%$2%sSO<(*YSkC6 zg_d=3t4lQ2^{S^SHrL*96xmOt(#Sj%5g5i8VYm;2&g?SVwD_c~80kYoUjQt1iHmm* ziPt!qpJ(7e1Oqkn7JtE=i(c6j%~g3QS>Rb@ZaBfquyurjI6K-)6S(_y+bi&1pW+lg z$7RMsH1#3GKG)b(F;_Z@E#zY>U@bko-B-jxOR}^hi)^GF)s`=Vz@Au&17BY^>)#v; z8~=8ny2ev|H`~(t9P93ez2>7a<8q5F%&jkFgW7Ejj4P*(JOhas0hTetDp%VQ=3!7G ziaN2Bkjp?+;;ns&4Z)l_WZn(ObcuPbhwi_ApVLsAq!K;<7L33(YsqBVYv1COI62vMYkr#R-i;)@N4nxpc)1i`AgJz2yzk zutI(JC?))T<0oR$7C{WzXgH07RRZ5Zv~@Q@%!i!8j&>E-BN4~!i!yL~TWO&@mZ!oq ze0NX0cVDy9n({@&6}hp~M{4!oE?Zq&{1T(@mMI$z+;l1tzR#nSgeSC{RN?}y?0fy% z_ijdZWl?wtyaD#-C;YbZg~_3M0*y}pv`b?1C$sf+^W|ZI1n};>{K^7~Q6nc8iOdD( zvgbMGf=YYjCu1$M5{$0XUb6pH7LTgku6B}XmwK>o2BCp?J z+Ax?JGSJA(6zZ5<_+@Y}nu!oNlCq0Lug6J0J-l%iq@q|?Tk|JHO7+jbNg$fk6U==q zJavOhj+4n7$`HD#N!9*prnX!vmK)*UWK+9;PWq&Y%r%7TQs45@N~64Io{1{Q6)&*D zaNkRsm#(PXzFA9Z{ECv^HpjazomH>qm`;Dk%TG3@t5qdILVo2wkKT*{Wh9L-x$+^8 zeQM;w#io#!F3vo`dHdMI(BKhN6)LFc9kJ35=R}WqkaBn;>6U>`K|6uHGabt86Nr$Ek z!Lv(OWNTl1*w{EP>n!GbXkkXT!p_npnC^@)l}pTvi|6$(xSj+lL?q6|6MDqBx^HO! zDO`Cbyuubjcfob$iAktb(39m}&r0Lqk7UlPIEQL{N9Wdt(&+0x)tt&(jrNumJwXTW zjo)PmBZ#~tEV!Il<#)7HG4k;-KV0A_60Quwb0ZLVIxcqtq+AhqR1d#U+R7;t3&nv8 zJ~2cd#U%&F;w&zUC1SFvt{?&XB{bCbliY#fR=K=bY2gPG0_3QsEiUMA z&*3PBGsNJGpo`NGFVR-@B>)smJf%w+4AStFodMGeApjw>DwDS_NJs>yyc-1!P;P+E|QgP;H^r;?dO(i z0Bx=(k-KoU0i-N6)#)&Y(yp8a#kYK4UM6{~?UkKb$h*E(omj^!`;9LqkC6b)bPzK; zd_SN&kfRl+2qLw>tjA_qc`@a`o|+VP0#NuE_UXb__4gp~es=OCJ9JBdIC`T5}*`-+bajG&0u}>6t2qN^i?{W3g1@! zdBS6D(QtR&+*jg^Z`^VkRc{!vZegYi5T50y0d_8Y9kR>t-fI1o6;~-wvhH81K=6oy zVqjwGwApoUXN{f_r>!B0t>gi;?jMz;zT%2Y_O>r_@DhnM^BHT#*G|t}y(ctqNP)-% z`I{j)l%+G0QW~= z;T^vK6GyGTk-oYT>?N4)Rv?h^BuU~kQCCKs(M(j`e;7k(iJ*G)k#OWFV|JWkc&g%L z?=fPA**!{>y$iBgsHyoQ>eo!H4=tCv%*+QR84ohk*65&#JLY&XWQv0u2Y5}Qt~HAp z9~`rA8LQSVXC9zyoaPmM%-`G=KS0}LdbjAE^`S!dK(p53jVas;ThA=n!yNF`DDm50 zI14}NM3G|9B?$3sGIcJiFc8mTwNGkhWukH}_eSE|{j#s$n?-ShdvkU@dc=`z+&wvw z{C?ctEj+x3R6hZR1N@hScP+$65z^Jep6TJyzwUfvd!fu==0Qp48%yT5tHSmIO(8{F z%&7uU-B`?K`pZ}OgX=GLAH0#-dv?0^$-^c6i=LbnUDJ0h-ghy{ZxQu0;Hrfsx_I2A zy~*_Pp_9+rkgWv^ZtZ%DWevNhnKKG9*+Zbr>vV8NqR^=KSpFrj!Mpm)h3CUU_TL(F zI?jn->`(P=79jrdRwm5i(EtaXjl9**K`Z(T!MQp;<1@0% z%BZl2ZR>0)8Zcspwt$4CS7pbWIt63KUUorvFU}glr1GtWIP#>G&|~&dBZHFJT#eKa zCEe5os*?j_fS54I*_#rdMn`s!YZp5DfNUqQBk%zl9FnQ=@&{-A$Wth1+(EeTLnH-; z&~$(;QZoFq`5+KoJ+8ReP(J7qrUbzFGL_A!8X(TQ8QsG6+Ywi*OMr0kj@&*2fUyJ} zXvO5H0aq&EPjLRl;I%i9RtMrb6AY?~Szg;=l6?EGgLk`_4vK?rr=Vk~m|QF{yDgoq zCh`E73Asd;Z0${Z-N;dV4DId$Gbe_r!| zw(iBinh?PHXg2R*(6kIX9E=HQ19?FPB#?Ug2?K69(9y9nmQ$o1_V7ef44EATYh<}% zeHJuz&4L<&le-Xj7$H2sxN6KJK}W1Ey8LwM=qfpS48O4eL9JUka2cX&J#a)9I=TxH zlRAL1c8;mvJ66yUe7@LD4%T@qN?{AyV4o@Rdij_D28>`zPT3>rALt}I14H6{O7qUJ zXjjONInIXnHh9+=^R<|5Hck!eN^gj(FzAnSXmTyDUcM`6HNej2Sf^-oz=CuvN1YF> z1j?y|K*9&+KGJqSs`yf+FMNHYx^`Bx{Y@~fg^ecHkZY04dV%5jr3Y@E$=BzR+KiVG zW5NC{w(}xNwffy$+J{_TdL(u6=?gipj29WotOk)AHa6q`daxV^ZxbBWHs+%0zYg5* z#rHo8z zT!8MDbR!04KaEX?XNOlW+_p2-(#-tQ8_a*IMrw+iYp7}nZL_d)YV^a?;`DcGNpd>b zdW)2+8t1}ZF@AF43F%^sE21(B5QR=m{SpU|gAah2yPKFG0!jh6Zw?%z>~1SE79ai} zdv6^S=lAprqJf0q8azNC1b4Rt4IUEQHMkD00fKviOA<86-~@LG5G**seQsgeq(nX8P^Hxrp2A;S-U*Z?WKJH0Lh&yB`_gmw*++lQ~nI+lE~-_oR= zHFoIMU(qN281{S_p|7^we;snrR^1=)8ZG*k2~=cVTmS63pB3?{IJ>IyI@fHM5l>_? zq|DA!SvAJ)^_1Zx&oj0sWG5e+G@|0U8S$)*^k&}HdRn=;4Q&n){SR!P;g6D=jgL*< zs&#deQ3nAwnPTF?A_lj=?B~36U-E9zmWNb+q@Q8v8Y<1m(N4JvKvlmS878u4gfJ1{ z9m{q`D>BIss2j*NM-@1SR3&i1io4Ex#(%(|NXR!VT+w{{H4ZSy^rT!-sq+< zR~i0~IYBfwBXIhA@>Pt}>&uzKY7G%XyP)IW-+IBpTfnn_IjD)i`P8ZP{*0AbLir3A z?au#L1MceoiK0B*OjJHySS6pQ=Dh1AE7d@(N)2hE^~jdqKx4Vf=8d z6BoAtmaTu@#ljZ6{@5g*T)V&d`ToB1ohfYqQt25noRte+6Il%k0vVtoFWV9COr{`k zQ6Ts7g{;AApGihi}8wpUMH(EF5afD*@Lgk1=eIz)}RAff^$~%s-C%zz`rtK8bCl1rYOO5#f$& z8Gu`Yfx{U;!0T9%!P{)WwY#&^|I|?{2PxY``YoGjD8KQopDRy-jl%4xie_&xKj6Cx0 z25}Uy2h!ps@ z9td^5l16smAz96NW|H~@ecw!cs*#M=?R(6w=16_VkAHel{Gvpf^2|vCB?dKPu;Y83 zWu1}99b!|fdex3QU2Gliv#i7J!LJ9P-@iuXL`oB)<4>iNUx?u(`bIX+8EF2=o{+!6 zc|EXBW5DL=?@L3>(7T1iVNU}qVMAynoQOLy@_1pt{n0^Zw=UF(v*%%Iz`Y04weGq~ zwg&Ee2O)!exFcF5ewG@{C!e%;3->3f&54)kS`*%zbyL+7BA{J-wj0ps1+Z?gM|E! z^~gX7u%2?NNmADXTfX5NmyDai$FOEfck_0N<*4GJ_L5+eSi-Bf=s zZvoN@MF;1~fUnrUc()~sH-pf>w)EyG+W#x~HjfoM@b1K-gFhG$QOZ0VJxBxis`>)` zcOQ#4ivVg*`G+1LRl++_ltC5#_9S@@KE@!7_)POJL-hA@c-eUd;r|Zm|KAFY2#FOcj5#7mru3-SxErhkJo2XJ~t@Mv0>X)U0VgJCG(Gek3t@|J~_B zb*#S`?OlA}lDa07Aj0B?x&uPZz*fT;%))l($rImGWHNmLWmtF>bz!lu*UkB7|Cq9P)#pWpp$^ZP*Wo}-if$9R$+Rda{=#tAt^_LF0<-?7RVVTU#xT+5%&V_-)BFSQRgPsGmS}n$< z^_^gR8hR^W(9w$2@q-8 zoj%}dLdR8+5V2f+7sg3cltlep@RRBciqfv8H*DuexemsWsES^d(mMQvFhDHW!P2#% zcNn3gpcfRf-5>IP^+YZ1B1{z=0;h`bob@}u8oV-YW2-C$rXL*M1)S3ZoOAt3clcXT zT;y@COJ{EFv5X8Z+ozMVT|HiZd&?+yE6+Q7;{Z(04h>0&kdC36S8-f-8Ig7TXB7%I z3;7BA6F-*i%U;@-AEZ&eDCgNYEUiNmeptaZTuhay{e@%cRvm=v*< z-vK6nvr8o9MnK%!)*$Q2B}Euq8F#8vO%1Re#E2JQ)0y%Dt+eh=5dazczYTh54*MUK zpxcw(+}s=-j539hGsfEz-7}iu%{8TgucIwk^h(Aj_=E6b0+AaF%w-T)JDKbvg@N*v z`3jkST^11;wh#M0sq#I7X3v)=%3vkj8=OksZvCDNDgRlOPMJh6Wt<{ZpRqjfQSP-+ zpQl26d6S|S+XYYB?FaHlYI2hwVqHDpJEL5&r z=b`alQSUK^x_tsZ8N;tQHP{R|qfzZuarJCMH&R!4+>xt!XxBaMjpLeo*+KD{DB|s`^%?wf0ic5*Pk- z^YBWHU0CNIEPQr(&D~>L%ynMl#_21sfc9`)#d;X@V}ne8*NSyjj`}%UMM>s%kE=F` zopyE(GeBi{ZRXejs?akg7L8&T_=tT0?Gg96%=&O;qN!q%?j&Y9Bg;%jog+d*x@uXxaviJoop$H({`|O=EGcQ+9>^|(EQl+9t~xaq z@H?-qgf#$I$H@_SK_^{-ZPs>z{Hl0~WE(p)LA3Ep<&L=|WD3nu-~Q)|ZT+%3x@zwD7Y$GOeCbsf(nB(Tu*n$zbG-6f z0SmJg)y22&Ictr!QQl-u18*R@-B8saPs#bH;|WYghKs$|nc-4|$^dfD(&3NWhnbA@5y z?#k8fa0inev!&SE!~&*8H!S9O5wx58m6)rx0JZS`mWeX#+Wc^veymK&sb_SvD%Wv* zKmMr%xfVz1lpo~`2{%;3QsDI~24&Dqif7c$h2>94afP*}zNfFwi>Z6~+)&u}**A5J zl^%t!a8)^zCQS!w<#qqYgP*|GrH5u(TN2~{r}d&atVdGBc>hR2n=dmIXEf@;`2y^x#}x9o~TJok<f*`ab>)_Zu&~`nr+l-+hv7#qGhSss`+U~m7^OshTiDwD> zuqtPYBvh9gR}TdhfL2lV>~H4uN!eT{27Cbcsk>jt@>TTZQ%Xuor|U71{cwCW8mWUT z<=d(q(O7Kg*Yc(vZ5L3`CW$Ys0JUmi7E4ITWuYa~G=GhnY`sS> z@Y(dRI>Id~B5S2G$|y4sX?<%6Z}THQ4^OD=26TSyCzu$coq4D{(Dg&Kn2$e^3w4Wc z)336x&S7af&LA99Y<%dh;VRs~sKfEB8qdfX_JBSLJaWbO+&!5`7^6|v6Vz)HEJ*oo ztUeB>8!4Emo>b6<#Cw)^Sr`OFKH$I4gvzP)6EIuTGzA+_M7(^7b?Z zQ`vdF@DQ&AG*O+2eq*Lod*5a|)p#g-N<%xpTW7`h>b1ZRTMm8UF;zZ44WU6K2N;so%R3UJUakN;U_=u+62ubfVDvm?V z&i@z5Vu$a7P_YJDLn|=J*PgcdyU)qt;evQv3JuZ%2;f3Aq7VOG?f^{ej7W6FJAW3S znw88<33owO348(nSH{WS7|!s;UA{@b`C`z#Z~y)n(nmvF$bZ;TSyI{Qcqii^1?3CR zTOTgjpx8Y5)LI+TbaZ?gd-JRUEeJw5BqH2CHTPl+1@o{nRXE|USVp`#2naGhQSjr( zhB^KZ*6fe`R3u+T$WA}t(%;K8GIdCZFy|;sqG(R6()=Dx%x{dJjm(_l=$STneCBz#05k zt{Hx`cWo3>^i3#lOW|*kf+-j=fiP%++K?!KFYBR6q9eyYl_lw*CdBR4j}#^()(3mJ zvaIhM`QZow0+Joj+qy>|R?`_{y?#Nz@$HC_>`CNwsTR*QWzD8MSj~d8YI68wHn^WZ zjkm~Qfz?*58{Xh@J-?$RAINe>BU19M*L_n;A+rs5eU_>0hqZl;td;OQQc&Kv@Aad* z8<)Xh@fT0wep_RsFX*V0_%v9fKBaT=$6HEAk)eOuitjRrYx;?a^}Cjce~Vj+@#qN6 zEL*k6`bqam$NBMHaR02fQV(O-v_ z@6gx%r6Ks5m2S5g<$U&bGsE?&kQu>T+Q01k4}5CRZz}`s13?GT`&-mrI^1pnT56hk zwRrS*;qj7XNa2#*5_0i$w2$Z_Ok+Qh;BY*a#lcY#kNp(S6;JS_@i1w4lYNVstFD}D zSi~=5$s=ZF=fT`|(dBrdP6H8QUL!$&da3q|OgQ6Q$Kx2O;uuj8zx|<=M|G1`HY>4@ zElFsok-#6;W9rf!oEKIkEwr)qr=K>tNcqt91FaZa?z1QP%nuDX7P%4kh22-u!3_0B z2Y+WT#G|{Zv}<`6@0*fxTD@J@6)^a!ceU^u7rV^}VfMoGPp%LigJpG|WuONZS?YUQ z_#`xjCP;Xjyu6vUl-@{M?;|HCzu2U_6I3DfKDj?blfAj50lo6L-L%D^bJ6)`fFdal z|MPwVC}U#OV?aW1!Xm4>936V%BnK7Rb^N56Rc3WO+d!eSE_B|{RV+;hpQ937ev`Gn zgSsb!&XR)C{1K;;3M4xwVAYXa7nCdCJ24C<=B5odBCD4R8IT<~f_B;F+N_IiTQeO| z+*Dk{4VSFfXmF^I;+~B$AXeieDBpjT& z7vTW$47FJjK@pJvsf0x`J008biN7PF-K`LU8bUp>zb7&Wwl}IFmjBoNEo{)5nUCy3 zXC@l~4_R{?7Ioi@L9Fg`kCZsdmNfP6#ow$;`N*}%&GHDp5YC92XNuM|pZ<(hG0wn< zt4A1n^81rF?&e3tNYi_ANXISTcSGKiI24ugM z81Z+v_k&O6o1U!vp8IUm$jMm#bD{dX8)`%d^19c4v!e`S-L*_eXNcl25Eu~l#An_j ztRwUyi8J)=Q|zsA5fhTj&?8(KODiK_*U)Uf(K~f;EFl*sMyNtQ!xP6sh(K`Y<p*KE_N8Wh__#5xUO#I*sogJA4wf8NNY*?bw?##;n0cmj-~aIC{c)Or-KFOU_y|x`c1m#ugi!=`sZM9CDa?6V1RlgK zWcFSNg&R75J`_P^;7#W%1n)VL8W`(ywDBUk&i+P@*4_&Nv2BEAk9ZUEIU{fW^K3*AF)=kX%6-Q7)6*rW0E7LOh^ zs54*mHUp&3&%S0=|DMo9r-k0q;+0>FbH#AUA9E)YjMIqIPf z!C>ADBnFEIngi(E9efnyrbz|aT%QbCcJa=ss_C7>MOPy^Un*P3Id*3`9*FJMX1<$# zHl{9!$|*$~Y(bi&nQvlzlmz*Wt0FrzI2gy7pSe!wA`MNmWjF3~Wd9L@lyb`K9QK+O z56Yb^%Ic`-cw27WldcrlABk)6084Oh9MK_-FRuW9SY~al26JYNP&G$0KOwKgLT?V4 zWL~?=fw?~VdC*oeJ5wGg88 zFkXdQ=DF?Zv%1ujtS*Y=3D`7Q4-UL#BSs=PCfMlEJwywB=0<*AbeJlJxP|n4^4Jda zpH8ZYohNVers*v;n7%5uyrM}X-?Xo zRwVU8Ar8`Cv&Hnssc1nn)0u^&n%amu$Y;Jzxqn$%DSe6G!a;w2G4pVOd)#}#(P8>Z z1erL!shWC8x9uj5kXW+Dq-HKHgKo@LM84pV2FDvIj&*FES-^v95>HK^FN2-oV3U>M zd7Lbo6s$W`I0GrWfxs&=da#?bcBYlE(A?De9)fSe)b-xeY?#ZlNc7S@$_Hwbfs1j` zv<=pX;vvsF<#2n^Nr4E7w`a^tlk)@+fp-$nO(toS-) z(}6X?Y4VS19|(F>%B49jtYqGM5z;Mn-;MYw<4Zn5w!s%<5r;c341VW#`GgIA?D)h)%L|A zM@X zR83ZmZrITovF$bf19Rol{jKmK`SF<1D)n>_mo9Ww*qWB9`dKGH;5)g_;u zb3Zu{Z>m4T-uziSV)4t*F1TNag9+;XJ)tD@jUKkN}M2!3*iE8fTY@ zsBf38AB|&!5m7~9iJGvhmZuyMU{M(Trp=a$D6>zeADLQVWlu*&s?B?bL;5YZ@G*!7 zy-#b_J~f+NGN&I-!`QxW?nJnTJKdw3Zn7Attd97J`Vc0Vw^!;woFGJyPSy|x(QgxV z&NL^*f+0me$vYCf_lCbp+c;x;dEebiU_f+r^8V5Cfc%W}m;hR*i&#e^q2XRdLVImP z{{3Tply^$v#xr%A7M}Mh@M(+F=LH_CZD|>2Oy#+~zGA6>-z{PDh)KbT<5! z<2>l?62izp%P>(e&Y+3MHI(jp>i>sWdU`E(je7#kZje(fJ`Q5%!6hieu+2EO?#>LC z#Rpz_=j@&Xx@ywJj99|7o005;TC=No>wuROyu=)l-<&agBAA~4OxFMlq3dKnUUO)q zjVFbi?ageHFeoZ$EPdOZc)if-KjqVja_>!hXQ{F+gWm zLC%R-fTJ-IZ&M;G``x4=LWif#E(;88HAx58z^$|&mO8+frPEI_jp@whz4|D4bum>) zg4w$KfC;SM9frXmpku1JD?|P z?;5X(y`fY1Q?mI6g>I}N0>VTQgp{~Op#SnH)W6%yDZH^ZGlV;3zLzE&^$CAaH-a{#PpxR;qi&V{^ntes6 z)h$CvH4|u?wN+o`qDs5ALRSzK0l$s_{}GVSzOuU(+oMjelU@ zXUVINo2=g_-OF8U&Uvr~qPY~Ao{lZ-WYDAPx}SL_R`LwG3i=-!{*ho3k~Te)tfE1E6L~O6FV{8z>N7efudOy znl$m8aEMY@WTeUjr)N(E0n;=ukRadQsT7I`UIyP^=6%><>Sf{gl zGPYiDF7-fA`Xs?1%dlz9+kK+v&}&(jogJyKwSr22U`;?uxGBEP+m%R=meMshA?NL9 z1oKJk?4a4AY{hAeY=}!dv+mJvk21;~R)@_@rTFxxtX<(cN0BFo^G`dQ1I_AVwmFYO zvc^m@MLWCqO9yn>wZ2N#cTPe=TTE|nWaA*kZ^`#DsM>2U-NtG{^SU50{5s^uUiRm{ z?h(*K-$oPfarfw;FJt9<8B45U}9bj@+f{Y>^U{ z;+Eo-;(-wnf!*u3`OIu%c|RBHboV?@`uHP%YJ;(sBk~=B5(m{60ez*~b6T)RB)(t6 zg-F%C((xG&Qt01iC@!`JZ0~7bp!8dBi8*gJ%X#fXsdPgS{%q%-`+>z#w9bxcC$e1yBJq=oU6zRU3l`iz7f68PVoMR5UOiHxb8eYU zd+KKp{7i|3a`}ZvI$=HJtDF2DtAU4)=TupBpPrnLGHmvSdWit{%&%VxDVUp>YzM7Q z0Gq?9?>vxEbmuU#%6GrPTu-;Yj%w8+?V<4A77nVLLa;L)3}Z5)4r6C~-*>XLoGM+2 zU;l=9>iTA)TVu6W2}P2+6Wpa@n(?p9k3Iw>E<3JlNj=NJ49@KNAy z00On=>lPOxuxCPSay3yJKQ@nbt#M)}av*gSTG@TAlgUS#N{zBk(5x!m_g?c1?GViV z(fh*UA_L++5~3e+gqL^3F_*R;B>cNu*q-&|&EJR~NmATcM4bAKqA5hZr0IM3>)-XwIh|MLxeNm zP7A2)l}Fmm5q;${muyhF62I@@Hk4C+NO_~kKG*Y(Biq4noYjSd3+qt&VVeb4-cnRp zLR`}sEx+>t3Q;s=a4|7!bI=>>y~}6{MBL5oXNiCz+bRbFnJFj5ve3&;d5wrWA095aMIFHN{PTui8X^IOuCEq4d5IGfX&ZH&B~v`07))g8%6V)lNm$mG-IWRdl|GZ*-rE4{%Qp2re5i&u5p z%@=HmCacYe{2t5fl+3C_70w11N9MW&n_DIs^)cQnZ#+Ap*kp8Rfks!`6{ zpD7%+V5|QkSP-(`_a!*5iRB(92L^Uxut_J0s7A5AMCpO@7ge_#5z_gnZy)zv0^grG zw7L^`Ls3N2_A@Q(`by%S>ef~mC}*|T8dSTK!XdR#aU8fZBbKE)UV+f?SUAN=%yV{a@-cobI`XYWdTqI}lZ4Bb)KGTqB zKs#7m493tr+Z5e8-za zbIa7E^7JJqgkj!lil3=fQTf+=T)q$2&?66eA45vb2-0HofYd7m98u& z*JZ=#nyb*dh+_{Sw=~q2JizGfR_->VkV;w(1KI6d+C z+E|m+nj9SQE-~;u$>Rn{AL&E=)`37Sq$ae&%%_3fo4ke{i^0-RHU9FZUE3R6Mzuri z;aOegQqkQck?O;py3+NuVvY(k=VAKn3qUeY9d>>+Ifrq>+w+5hKedIpo3`&`*atm` za$ES9&4$_k+R%r<6pjbRmYp`2jr*6WhI*!pfzx_a=PI}XY3RLg$)^kgnjVojkBJN$ z_GgGj-3!JbRrOhIcdQ@XdqVqCi=;!J-6|(cm&>XDLo{~^=cCfblk(hTG2`U0W_$f} z7Q81fgSJo~7#~(K?YaNha6d|U$rlQd9>}-9=^m+bUXphCs*g%V$shS%ic)g3DkRMX zkLYyT$r@$Hl09o6HB5OnBb>vaIFypQ!!7LPfDtT@%E6%4WpJG{>E?#)hZC&1B)6v* zDZseFsEX2D!yhBUSXsRq5l_vfI?yn;Me};5)JUXO)S&O^ba?>?xhal1vo=YCX2E2I zR)r?jWnWd-2PQZ1_FBsGE^5$PuR3~jPCHvlvyYQ`}KKMnz9*cB<^!x`;K zt;R=`j!IvjITm|^XD+Sg_FRTqB@VLsMtN%%Gr~{IKSRu>UIKb(O&^%b&1=keiHy2; zzLlbRzJMkT&AMH}u0JwFbapRuSm&hw)oAzURl*o2q>Vm-W|30M?Y1_&PSalffaA6G zSg&Ng7Jbtn3fMsUKB?B+&9xxDPga918r~Bn8`1JE?sR=F*nB?E+(Om%R9}lKk@V!0 z`qLOxFTc(Z{7$mcezwLDrdVv{v9H!c9#AKjx!t$=-%#j_^(DolpyVX3D5Iu@IrpuOWH^Rd<(m(2h;$TNMq? zeH0pXsPmUbET#Tw{pwknMPWS3)3X?kBIAbk@_~ggQXa>ZgC{mRRsEq$tH}2j){{u* zVaIcG)MH5snW1d3{*JB{gLjM^K4c>@@l$p&N?hw#x=*%l95JN*&UxaeebKZN8N+&O z4wDP2j@PA`Fn^|LQcZnDKG~iQQ(9t3=Jg!$Y+3g6>d^xGHm5%G>t^zeocSBa(U%Ed zIITAaR+(5utVY?-Pp^LU^hzL?_>Hrz-RvBZ(VSe-(P0<3Qi|b9^?dB4>Wnclof=!E zwgf$i?Jiuh>oj4{AKR|>zCBR)+nT8zt;7%@`yJ8ptK*vP z;vdjBwVuAbt@x1{}g* zX>4NYn$#Z(-@<@X9z(_N@FHhYr0k}1-y~Vg2_>;FJ%He=8tsddEwD9(bYEA7!^RC0 zm9pN;iPWZX6Xd}vJiYkcaXzS?EEH-RkIKmR?%FjSDkbd1Uw)?9yXqHI(-I}QRdON2 z{>W<5h|~6(YG4{`jB!Lu~Q1# znXCF>KKt?{MHiA+dj+{k4=YkbNBgohQG$4yBWowx=RUPvW#SUz+7-{o_Kn9H*ZTMh z>+&mYNFNo~!Bw;n@gsu$^)mCQu^I=N=4T$YG(CloVHxO#M=s+ zT7dH;Vkg})w>eJyM&H)B@vy%QdX*}lDC~h>yIxXcU@QXd!>{PSP8*;+j=GZk9sB(; zE>3_Z`1L%)H?Q?}UwEVQOF<9WGH&*cH706ZS>p2)0%V@O=AKiDIie}0Tvu-PTe5J` ztJ(CV1lG4T0Bkz^ni4)Gn27nIkSPRgCz&T>fe(cv)6@(ZBBQd4!o-7cL-|XK-pU1u zEFe-AwR`#pyw`%@Z5o-!;<$ER7$;+;zWc_le)xJ+APo#rzWu!m{M+1JqZ_+^lD7#Ee!O?!!Jn(V$%uxhcxo6b0sr34eW$^fU@E^;i

@pT2??13C~>Zhrw+XrKxtMHFgCrz`Ju>*YT&t>pIU{vA3 z!;cGOEDSQ0{E`9_4e1GeUxD-f!3tx;H(_UG{v#P5bn<|6GTw~Yy~{=cFTwgTL5CmT z=!L+|RprMcZnzFu;AYswE86>bh=O}XA2Z*5*_1${%rXB^OiA~f*Gk%WM*8O~4XNf9 zKqQ5YE+&f+Isu7@qf>l)nAPdm1DmoSONk%%(wes*WT*BbE^Fl(+uGdNu2WWQ-e848 zifu=@a6{?9TE>~$M)4S;ls$aZQ24rN>yb4fTPmmF$8TR^aWqRDdx8y=(={tAE!$Eb zulti?>VP|ptVtmXC^X?fq$E>x0x{_Vpf7%2T{UfKy&CbVaDOj8_PzGq3qx33c5>Z9 zNV(gb>m@PXMvIYMl{($nwmHYK21>ddh!9g;M+@FbQV;AoE=6Yw&6PPVyn|tw^%@Qt zALJ?%=ZuX;HlaNItwm%r?c{j6zvN~nn_~RsmBCdmiNLr#R3^HR=M^rx1t4g_hT^!7 z7o`l7LTGXF*qYEzK7FkpAI7#^q1Z_a&9;nxGUK(rF&a^`qb#z#nY1|&S+ZbV?TZKW z7C)I4u7WsVl>6`AeEjR1PGkTrnqeupiSXwFVlUJdO&a{y&G!eqLnA5p1_Ivyh<4#F z3pJ7w15hj%3~O&1Lu@eENlAhy<6Yt$#}vGkxL33aw9J@}y0i%G#XsqBW1!VVozH=C zk%L)60trXZ;IT4JKnX2%DWxmDYKSB?4VX|S=MT@@zu)NK#=-&eYq&;|fRzuF(6ap- z^iMV9+YX=m>@F!R|)8Hh6i`s^mH~yKY0L&9I ziDU(`|Nh{cWAeX*g^E9fztjA$dUVGz8XQ;n0R40uvFMU?fUr>Rx_f`;cVGhKRMF(j zfw&bV5Xc$()9;vf96+1`^H*hbj~L-PfO{Y^sc!=D?jrF}HZa5)-pDn0wgA9cO7!q= ze>vUk2-xY@9lcClMH9nQJt-&%yF3J}kualQ#mrd@J z`Ee(H<>*}o{5_eZQRw^u;*#j2w_l!Q(qHmT?x4J|diZzHeFkbjT)+3w&HbwuASk;q6`l~5qBd@?1^cGzv5pAoMFV{4C0P+q*J`h%mPOG>Yk)}% zK*!(Y%RItHLoUGnOVxr*^I;GB!uX40SwPn-bjc5zRbMtiCCsm-Lh!4^AWELTzZOH& zk&yuhPteH`i;b@r_SL>;3HyECwGB&{_E@|+*A1|K#e*8(eBB+t)8MkQVGxG@zmh3|EuqsgE@F>C*HEF&?yJ^+jXw*<2=@yJ(;vB#eiE|;^H^0 zSnA;oF$csaRiCAC9-(~c5M-Fe6DZIBs#pJZql={0S?2uay`qJ$5M*QXha%~i!_HG4 zPN3>4xUo&>gy+pwOn$iTdEZn~kQAFkA$Dd&|55t$aD3mt$ zLsI=ER?&oOLM9RTmFq**jt#!G3k?o8(gCr=3yS#n%WTiz=|zP%vj7`3hZont_Iugd z{_^W74UQ7r#&nYg>_%Kk&8K{m{+A7WxFpDZ{=U?4ga@|-V3LruN4*0(zYNaqD8s;o7SH{)y63l!TZ5gRPJ-Z>1NVQbd)$?YjBK0J~5t0;D~D^(98p`0zyUo=NwP5 z(X}%3f)!~&3OHf)TexepT?Qzr1snJQa32%HHRKF0EWnfX`=VFm*oStk@`OG$aMqMHKF{ zBF0Ct#Wj_b?vFtJ@me6wBMYmedy>=zV2HqQp|vPEC>li#DFI>@;K*fAcO$Xo!H?qY z5ocUlf=#)qc-woUhTb5HeVqnKITd_r zgzle7T?P{q?i;n)@a=7~UeIvE;n2#R2In<=f9OY5q z^lWSZ;C%q3rTK(Nx0zylpa`4LbnbJUa>OfFou*)tg29#~h7(mIqO65;JU| zLJoIN3R=agL4@YI8~#4=u?*ISui6wmQxy`6bXBMXt761#UtBZONt&!Mhb($l-DdD> zAsuBY!zT5`MyHeov-P9P9r64QfrGHp%7?l~kIO{%{0)3cS@o;7xJ-MpsMgD7Q)8~A(s4HFab+El5D7Oe zlrqJ$X@dJq+q*Z`ZhX$n7m%3K1dsthB@W_UWWi4u76XgFXGOX$9R~SFtrd@E<}#v% zrNZZbePq&X@_t0`y8FI#4l0?aTT?&g1;Rkx8`vx8xGul*JvJgwgk8Jo$k&#`KflK8 zH^?Q}d*9#i1xx4mZ-}@<^v%xF6HUQu5dxnty3m%WYL!OcZBpOa63QA%X7YUht}Ix8 zfUm!q%Vni(1_$j1#B=8AawkX55hikaHd=^kBE8UDq*pMqX9mZx62^RE0RFY~d$z&7 z(8f}2ygs8&v0-YwQ0g?5huvf5o^GA%=y$QOVoiIEQemq~%aHhiH;pTr%WQUGvc#d{ zvgmZ7npEfKNqmxss}CvO6%+M#jvR~4e?NsSP261b`y8R;J&}WBgN6$e9A~ckG`bC* zs2#t=!lY;B+85n^2E?t`QV1=~XU>p}7O|wBY{pDu6Sf=(lsg{$ek^=2qGm{!4Is@k z?=gjRK~DmLK^=A9i1c7e$vWKZ*sI1%I8Ks0bf|dcc*RfY*Lcr&su7c|=?_NtPQ$pQ z**#YMcUK(|WXKK&#TwL9`Wr)|3e|ILx8(BlO3j}QP8UCO#$-#3?^Ltt33?0Iv)=GK zlD|pgl{qCH-5-fyGwRL>t3DbKbN~FtdZ_>aIEYhYMI5S8jm~qOqt+T6xf;rcM|vDthZx(ZmHE#<`Hl*-kZq+S+pGICemian z@%wB&6;0>H1uf<|L}~tJ0{ri<>ehJ@Vsu9T4DX1D70|F-_FQ{R_woY?kG?hGOy^B8 zx8#M*pZ%8uv#SwY|@NQK}MTjj%#x-k(3+m_gNoKb5w;^QSu`3IGewK z?c+zunjhU<4v2Lw%=x}3_r2cV!(WMvx%56^R?uzWoy-~mKYV!U_s8-IWs_Aemvl9T z9IGe4=-P&nDEd+G)NVa~jHufEI$nEem>Yfajc^>;n--C9@=)H=?bv!8sJgsg z=H9qsEt~m_5Duo(&*w0{GL${cd&}Wa@`ol}$U8Zui+{iIP}+j_gNJe!@#cb`vWP9c zIuoY(PDxc?jynV5Hb8_LPW%FLPgm|+v>=81L9zA>P0`p0?`J)>)Q%n9C5klNznF^?el*5f@jqH z1-oP+f-SQ6Essm`pQwz(6O|8N)hQ@`MnhfzoE?+$nm7-s z;mTau!4V+f58sL!8diu|@;Py*e)N9$sLY}>0kW}}00!=Sk0}k!f0=}Z`G6Q@*f~DG z7Mttd37VP!Y-b`^an3uc5mR3>q47GFQNu2BXQDsHDe;@`=vBRLU+Vk&*M;r1vnIGD zQ~{p=Odl;ItTwp3%aSETmQ^oAxO-iKsv3ruvepsIWuf@`d?b!m8cNJ!KuNeU!i9fU zp{T>&;3q~tag4E}m@tMuN9<(UQBC;W;b3opto>q)=X+m!FQ@gMIJuZR4rbl4_;;rm zlU3!ZxaG>J60aU}@WUKin8q_RHjJ5$IQ;>O=FJJVTePUE?$h2A54^EDh01CleQ+#%~g1CX+5UI(0;s zzJDH3Fgt>Zu+;8<(l^q*Wtm5R%CoH!%=4msqe$m>OuOsDA+uAbxfiRmb@B8(rw;ls zCH6ojopET({1d9QEUM{pc>2aH8-ZRIv1=Dk-aL8(_GwesF7neevJiTIbJ#L#K72}oK zbkgKV=fTRAhvrqkX8qx|8@*;OH;GWA?M6BJl??S$;|!hjNOd)+&Qy@PWZ*lmu~-Ss z(zixOIhsE#xkfDKaHo~bUYi-87QLPIi%p5W=d*|VzS740mDMzMvQwFm<63NYl*Izk zQc+);!iSZmuL&GB>#$Xru$BR>S0Y)vAokfZtM0^A=Ai2)4nZ~(hIAA+E@8NX-4lSr zGD?^3uvV;p;IGN+kA18S-JK|z<;NNr@@}R!?M@@M=ke}Vs10BZhoarNmR(P9G)jGU zjx6yB^9n?r7tb&c^=uBAhecOoNX%Wy`HHWI!tkCvKS4q*J)a_Uuh)hoJZQ~O1QmsT z4u-IwQS|m#ZO9>gA=uhYBGWlqW$qV`Z+b;A-&=ls_$=z&Q>vo0M)Rray1>@-QJ`FK z#gG{koA9Ad?L&fdb<>O+*zvQ0l$oH2t*G^y>|&k+nbxj)g@+1>nrvrl zCYr*&>9~KG73Mrdbe`yWw;L03H+Coi$_>}qD~0G=J4!>H4X0cVj6(_NeO+>QK^^$Oc{51 zbV4$XuSW)H2jfuXp-JCf5iaTPn$rIQ$?ibhoumk&8VP6}3|g0#$jAXwP4^fUwy+7B zVdWOaDC1YtXQXVBMf+8u78h6#&hx$X*s)h@7{Z@-@5vuh|WUP?hDJ9ovU9-!m-TG$Q?Fo zKWn)`i8*T`SHo`Yy?K$+AFGpDFzB#<<;C53Laht6w&R&kQ1fAW8tszzVWt)mGFLRM&9X`lC%5U%dWqK zK1PbYZ7bBy8}%!H4SQPtWIN`u&iY4q%*gx=x8F*6#}%XO;33U`Cmaz z+!7gHy{P9b-PK;`h7g3H2}Fwk5u>>t<82t%_Q@9j>^-d#Mk(rTE+M~?t*-Nrj#pD z?oCRb|HjPQxwMf$z(W5Yk!7mHpdW7TRJt zln$;P3Q$|#$hOFke-?1JcMkZR+4jA3OODthB;xmvJNT{D+|m!^&PHo1e4f7Rris&i zr@D9Rspn>?vn;IT2MgZc3Z*G#p{vu4dCzT@lS|Z{Q@WL)oQ%LJFxldrO%iOP_Zu2ML#)O>7Ym#XQbNv} z`8t*O2XkUhQTCNsZ3 z*ob|_lOBx5cy)a#c4fv15q*ehL=k@N&78^PkDZjKbr~k!j*E<1o)Oov88|+@HISMw z_h8a>Jay*@CT5x83DS~aj)_C60EOGa8bQ9wE#0JO`(ok9ABk6}+piRN(zlsRJ1loshfa#7R&-MJA z*Ipy?M_>q^A63YG^QQL4w#+V2frpa<0uaaZjvD+|&HuP8wou{8jqbFte`!}`Isc47~vI*KY}%=oL}6EHS4*_ux)>}w!c)n z^bq{f%P6@39~>hY=)$wNeL$=Nl91Ck2JjJS$M^xiap8oA!S!M+C^M(v{oa zQ!R{^Q!5}VMHXl~8oakt%y3kzn1Nxq#i>5`p29B0|5_{8k-gPT zvOofc0=8*ISA=zQ{xrhgp-}Jzh&(Lr zZp|0LkinKwkh%K)ld{d1s=a}FdMZW@e@!iE3NB=L?IJN4qeexKboe+laYLABn8hcn zr5t$*ySkcBk)&HSJ46rU{Cx@JcT91T$MaQj2!DXbkuOrC$UtyxQAAU{d;Lw-gWNIP;b{|WiM(*BA?m6w3^|PD z4vy8v8R`Fe>NO#Q8=|k{q~)p z{NRtE>_a^am0FDuHGbpxa#f$t40qcS{!`a^>lEgxFw|sWnB?}8UsvRnLm~y)B9fOoyPhE2YHBTQ_Sw)( zOL4fqEDD8bNc{&fNA`z90okXvyPjv*=ABtMi5;uLr^8g3ENSFwz?U}=~ zvN#K3v|xTU9&N%lGZ(kILc^%_2aEvX4TgX(C6*-`X81|@+i-zY9G`9Oq7t0-K&y?} zg}cgrzjk1@cz8DRAz1QL_M!r_iHIJus!PM=%EEos6PXtTZa)7iMS6LHQvOnw?t)oHvQqO|Gkhc3svBmc^ zMrqEu&sMMT+M-&uTx9yKwAhL)9oQBZy95c|PQxzXz-Tt&OZUJywza#xO%Cbc#sP6O zFyX$7BS0mH)I%PR(W`rpZAt30ecFRZY%LppPDobr@ge@dAO`S8s-eMxFIQ$8l*nV) zNHV@^!Bf=LgUhJz$5X&F!223i!=;i^zk~N(iPCtP69Er3XCbPl(O;nRCL8&5`Pu_> zl;}S!051XlQIBNz0jzIrb6>TS3XK$Z_w)=aFH zMi5Zkk%hYi-Klzob0Odl=hJr;%pi8qiY(eP+>S@UhUOuX0~t)o^Ihr^oba`LhYvD` z=>)mO93mmXZf4SAFUp4z;Cs3jUGZ~?j;~h*5OY^K8bpWBh1hD6-A62&I~!0HI3DFp zM3SpOK=$uT)ee~^PTK;5hk6k&MEI%~51XvQbngju4c-)xbeQYjaMkr1nJzM6HlFM;;`_T!$stBfdN*b&v6K&Ua;-Ea*r>Mp9UoD9I;9P+ zC2Xdh5QAoDU&wcroDrJ3v zyFwXun1OY{{gDbCfi?J>g|SO!=sN+r?YUECZX1*O%y`S~95u943YwPsVPrm3wV>G- z1Z;P2BC565Mi?g<{g!S=7;0*7Q{+a15m6_UdXw#R8*!%jf9dx;yuIJ7E}#2+zbT>m z)GH>uk$CXff~>{Wa`tA}>&1-3d@>c}w>~QBc3mR<>C82GN&~?~u+@vrq4cM@^RMHa z2|h-W%AVDGtV+hAip4Cg5BT^^7atb&9C6T}#r_un1&uG!2Es(T8WN7upOVZfH5nwX+G<_C%y8Yj9a6_|2)3e&dZVV@Kwx-4&%8W>fRrY0i(P+H zdVqop7efH)^TyowEpgYf=T2D{xX#@dDcet3)=#PZLG=&L)&{@HPBnXVK_>W!h3h=x zc@QW>bjL&65?iv4^?yu69bz?F|68xd59gb9Qp*u;>R zwG7A-EfpcnRpNKVzbq3hJmF}3i(>#KFFdv*R$v%b!J~|#Fzcm5)sn=;uFyUmr=Al)rOzihnRDcu^SQ z3gnO2|G}WBh4Pu9hoFEnmWEU{yYb0`Om0lgHXAG5a%{>J_!*6{HX6goxD>#XtI-l_9lk-0HZSwilg z4jpD)wrkb$nhh4d8>8>UyrBjVCGs;USQL-x(|AGQ_H`gy*KT$xsV znCfVnet>N$p8sVB?<ajLqypIFsi zBPncrVE=V!TxAMKV6j90CcD6+gP&gX00}_?i%hq&H;Xq6{@8r>4T|s|Bq*46%HB5` zvg%WT$qR=gQZ2f2vzlh{a%1f~W?y=-y4|T%Wd4MJyI|@eYed?AV8aV~tRHt$w=DmXwP&sN!Wi1SWVs=rTt|BtC3Bje)2V%>A(5ow)Yg7^v6@7OeC`$TO3rUI8 z-T++%{sRcGisaV?EhhuCD!xzx`v|m1gG%zgcRZcp?Yy!GL7*d8f0NncquBZbnQ%a@ zIvl7Y;6frylc#g~_>|Cd?85`lp{+D(>F4tHp&e7ZK^^`BxHOTlGO4jobStl4Ykj3! zRRY>vf=+bd3s4NmK%e)%A9MI4{HIFzQB+8kpD{4zQk#f&}(v{RldAliVJc8 z$Y9xfULIZCuv+cMopqT(GC@M}R~dNdRPJk`NAsh`yA$OPb7-XF#tY1}6>igdx0D3QN?6)ko{A;*le8@W?_fB@PyMT+F9xCrZ zIR5v$6uFqYvA}A#qpJhJ#>64){|ZdH@%2~YW62ajJ#_IT1tH(A&MBK5_PMJqg566e zsnbJol{47i9>UB(l2!?5&<6u|s<@I#Q|lv{3}C?=zPY%Mo65T#Hu)u@@|F9o?Z16K z--zT%&bD`{{lT{*guS`-)3S)ek;B7<#6$bv$ixRJ3Imh-f8iuFgOg+Q#JOv+2ad4| z3qwDI6z82ba{8Ee3Z)q!y@+11;}3)0a4sJ60bvN4poJU5a5xiHb~n@Om=+4BI82vV zm6(|}-!@Kgg1Dtiy5JD-+)w|f&@R;6SMn}o$GC4GkRfA- zW=sk5B+4I-CmH$6yp!sOBW_|3MwgYkTWoKDRm}cesC&}=eD4_^^CJzht3R$*iJk-i ziC&Q>DW!%%)2z@yXmG>%Ab+#rgTNQ1G^2Un{J{;&(dx#RDC=Ji+RJmwO^&Hdhd-0l zlX)Qn<5)I&M#ppO1X5$i7tLG}8$`EKmoVWAUXK~@V2TI@YBav`L}E`b7wZn)U7p?? zwvo>4=r)HSkzHrjyA9=K8UgS83Phtn73d+LH@1Y6LT0|oyZxKml}{1m)j=cvpofXy z{L^o_1q8UcGx3?;j$h#*>NR`$i^lv}iJg#(rBF?vu@taGHc6fE6mfqKaohhGb>xT5Bgn=FNuL)CBYkuUPq~x%Rh^OC`%2im!v|ki}XAvA$N%5 z=EG5Z1)ALx=)~(CC>znGjm*DA41CR%nz#G66nEd|y#Uu4+QTFg$w#N${ok|9IInKe zTLH_|)7!K6{VsUf6B}6t?idGRBc>bC_R6pe=@yfciRtM~seZ>)p;nGuDuXJOG{}C= zFpEY0Z}FUouuZ^GN!I?HP-+&MP`?>Ycc435K8K{DB;a}nwIgu89}@gNl-6}u;uq*c zx911qi@TaRf?S)ufqO%`*&e}9E!Dr5y`}l&aUtNt@jwcKWWtCnH?3jk?AyZ<-Fo$L zqPj08?PuDM4?-}XNv2CMouUTE(| z_3OPc`C^Y5EAsjYGJr2pd6z(iLM+@sFjnuK5go~Q<=NMz!J;C*)Dbm!3ib9Q&iCF; zCD5ApxSnaPthSaID$OfEX&g~+H>Ka_bk?PeQ4$n8G600M+{*q3OMul^ynxSGG~cb)XA9!UtlDbGF@ zLVJk1H>c&=>VLN6aPyTe7GLfd1kv@1=dX;~h2-BGT5lLyn~s>g;Vk5}vG1O2G*}|^ zr5g$A+O#q7X^KRV^%ZvI@)}N#2E*Tz;(^aHTUuX}UB)lvqB{XqX##Mmq4mCWl`I_E z957GIx-@`qPZa;vd6Reessn&f(mSWVm5#8o)ea;gZS+I(3GfXKxLB?SkpL(LE&yNL=VbC3p>JS8ht~n@ZO|#KIR!v}r=F{Ytz4 z2E&sXo~Fk?^W2-shdogKf(B5?(|%f+y-C?3-HnKm9Pm;WJ##4q6BQxQQ*NLn;NID9 z2bd|hO-s|hM1n$5oe$Yt{gh8twOj6-qbwKZ;rXE?jMEa4ys`1kKzK)}qkl-0Ia|i% z0NJAF<&t>}*?h(ItDSH{kw=eyno7lfNkblhV~m;H114;}iM%G+D85#pNiy9__G(;A z>2PI)g#t=x!G|04#y{K8ENbuT2z9`M0B|Yh;1r}gU&Z|dk!nkggZG1XRzd(}_&T@r zNAG&>Yo}n(cx_u`a*Iu=Kx7xPNRa%wV3U0(_--Hd>k@Oj{iT~ktrhW=jiH&kytXn2 za6rz+Wfr8%eThQ)exs8HbnS8v%EjwC66FWUL*P5o2YQ5`S1XEj0lzVshEVO*{p+}k6vxJbOo=QHSK;){vcs{JdOPq8?pif1%Qee zy{L9fhh8pJMm?O^>@U`hg`Gd3iiv~r3$2-))`?7Zf_Oe>oM%P414?IUY4yB~CS#Hl z^!QJ007-&W7miHf2sgI8v)zF^U^8%QuFYNrk^6A9*vc4Gm^JdvEmg}G- z)4_kcpRL86UbY~*KIDN3%46`UI%E)-q#||+1_?4AHXi0mfkIW#?#vCpF<|XS?;aHb0~&n=kWYsGiWF;*1g6Wh_UVrUkjhA8E{|EBV4*6=}FL8#}n4y#ob6LonL59kjl!e2^)5dCGP1=MJHs z+zZ*TMQkO^RXIPcm&<&_xRbY5>rvR7O{YlZ&jf1Y3p>fY_Z#p1bCt};2h-?PsA;NoPktV4gvZkKrW2v-n%e?&OV(07jDscWa}Q{I?wor-|GGThepwp9rRa^L~SwG zv#^&|Kw$)=P*>fIkTIakI)Z432`onrj^niU_D6A%eEYK4Hf{369c z3g+RE#ltV-CN0Aaw&@je5u6sg6?>$KAX z9xSW>M;d;JREG4PyvK4{E@e2Pz0z3NKZ z6Wt@ymyqQ>M7&+8W@hut=w#dz-T4VbB#wZ+%KDwtSjs}L-Z2oarfFvU*G!y~!vzAN zC1l+BY}>U?cVs}pGN(3Ves_eL5(84!={Y{Kt2K)r5B`FGFsg6?tbU$kFu=tYYOM{_ zJ99c+C#@#g-n___ZQoXN{k>nMGe0ZCxh_GqjkiiJeim@DH8$_uG*X^+ zgwjg}cgax6i0)WVZU_TiCWF^r&yG3{eC{K7L1tF z2k(_c6WbUZn$0(~3{8~sFuGTcVVHfjMu;loKwX?<0m}dZ_yNop+Kr9V!$Bab?5)B} z1%6bm_D+%kor`CO4|*4qFnT7NqUNGo59xL!qj1!<+rai%i~n(S$@UHiOK{YC(q$*` zSe?xnf=oFINd@o0MT zON@TQ-E@M$BN{A+a(nd1l^hY@{rnUUkKXRe|A}yNXs$4M<|i1LeVc*D=y-Tb4X>$d3ELt6W5`4sLV{M}lhHkrDaZ(3b_*YDz^)%|}_ zD*ib`*%_kr7J3<{Q;om{`xeq1p{gg&<6x#`TyWCeb*SuJ5AqP-W3X{k-)(4_A142xNy4Vp4R7X6&4roiy*O)Jlr8{GDZc3NHQ8 z-eZJJUZcl!$C=6v5}A2)vBxR;vJX0f!08H6aqY0K>f-71X9Tp(cN*H)sD{NQ5NQff z@{DdeXu4O$ZORa*o5XID zJ$)VdjE^zt?dg_OVQuJw{q6}t4R?y*b4sAF;LeUNmfWvFO3PgT=+L}Mfs0kow{&6& z(%`v9jI5f+hTQ=v^w_y>P?0oXCw|4B+yChBc#qd7!ZdUN7my$J_^STWt58t|!yo7W zOX)V)SIroFWg$Q}1EDiA=~+rkg$FA5(W&qM$?mx|ODC457w0ucihBS}43pg(zJU)2 zlXL|)(i9w^>7I0O+S&k#pe9$fN4ganU$(R0Y7u1dUn5}CXMyT*!;&`>1-XB|d+WIB@9W9oI$#|3?0lQ(wcws;H<&}4338n+wDW9!i)7qu4^M#8? zv8%_WdJ`BF%boyzSV)`E-uF{W7zy7JEnFJ~R+nsY=xp`4+G`gP!rq{nkQ2-xC~YPF z9Z0;6xj#Ub)=De7p9rYX>>OU}n>69LQwM5WbEdz|j_lgQeZjyEgA+5vIa-8lRm==f4e=gW* zCZBYuR|7}0QwhY6msLUl$@C|0Sq>`Q3x;M`T-Cjo8%cL~TkqI*8q8(RVa0hpo)^i& z^n#d`UhfM6Nv+H&>Bt4z-CJAh zp`Fa_4ZBQhVgPWC)z$A3+MuOh>=%`*^Sb{Axly2Pm(^Qf0F4TQk}j50n~;%>EE_0g zL*WGUdg+osyiVB2&>p{Lihd6K6zCRPAhd=?GfrY-hgpIx*mi#CpdJF)(nIAp7}X*Ot=<$P|Goq}wLz-}p!Hk- zTQ%U{$JyNoftAD(=k944d%jic9uulLxc(8o4zT5nBG%8TI!K*Vx?+Jcj8FXCwVy>F^&lX82 z@imG6`W9Rp*iy3d*XRPK>Yv4A`;KprxP#9W^eTlOPW}3G7=s7 zm!Aw|{9baE(bkbm|EIVEGPG=DT$WY`XbO91!GkBNO#u*JhWX;YB&vHsoank38^y)0 zUl|yGn;RCJ0@@Rh{`Ld_1}~*LE*Z;FS`WBVkT{BLLA&7x34USQy&!NqkY}OdetOZ5 z=7<1i6zPwz&6oyT##~2EfWKJ3J=Oc~O@~?nC7Ujp(3=kaOFNdaVK|eqfUH;Ur7MVk zhXpjD>_#O*AHm1~H56%(uZ}8VE;yJB)!BglhQK^>gZRW!bYw#?{B}4t8JUBIH^5 zCC$%X54y8CXLui^q{y)wU_>6`u#a^RurJ!SFvnOKH*EGvE;nJ`&UDS0?Nl42 zHwe%nT%^Lw2fz=?txoI;wsfb)ve{6<;&6O!r8f~-`A)=rDC|4^LgnVH)SpR1xLiG1 zObt;DV&;j8&F_ENZ2fz(SQqm}y5QGk-g5%yVK*y?S=RO(8cG}tie*B8pG&%`>njXS zAsIL~DT)u;BL_SFFI>lI=7_&Ar9{Ck*>`h%$Wp{4Z*tz~n`rH@)8(leAse;Ep@79H z6AkPY>d;=H(h-NFSJy=2knD$Z#iRcO&7L-+DJKi<#}{iQQGqmVg^ z`FBSiSh5IHt99F0czFsYIE5m`Jw??S7FUua#x5U%Xkq#wK3lKj8~xq(ReMrFtM7{Y zh$wLZ=s5D+i=_&11OzS8DC_jzcPF14(NpCU&yc^rOx-l8=nRJ|74z~*r1v--qkz4o z_}1AVjfr1lZ%>Xtp^$?t&(?UjUA)-V%Ams{4M$_d6o^LMqxI?&MYI3ON0bRZR$W=& zc(a@DKk--%4SYK4h1^$4#ojX+w6UbjWVHBt_fJszUTQDn)X0OKSzz;u-qGJ`xD4D! zTKvZO8Qm1VwI9@aBv$iFaTlkwOX6^Q;7^B%&P(vWnDAU^m$w(L+Oux4>oRMq+4&;L zN+dZHWfxa{mn}%?QAx*<>byaXH494eG~#eIFIyQGX8ZCMv>qC;sHtiM#LLSSs2I2} z_J!PY+-E+N{|voMfC23h6Cgcl7#ec$?e$DIcR3rVRz)g?V&QWag6(D(#ps~7^*o8eMHqaM(YZg>@o6-^C$OBrX~yPHKGAfdNmD)!F2 z=*N?3JDPnqA`@Vr~T3&AbHge_@>Nt+b;7r8i<`J&Zm>i9AaQHN>RWDK7Yb4dq2s8tR z>0Y)%|3Ox~Vuvct=a1)^r4Y(2K5T^R0x_nuu9UyAeRfeS{Gb##weWMh8hqvE7eYKd zQSkdQFkKBx=qWD|Ck&H6Mh3rZHGyZCW(}DaM1Ox$jp1IS0H;GEUS86rdT*Gy%U&hY zyq|s#_>9_>a_)X733kRR-v>ed&q{PIsaBgy~dM5Y2mJDQz4Z%D_+*HQvoo+o6h2Y{XT}|iMz3IUo zht!18_y6O>;vfdJFjHFI>45fPFyooMYesh0=$M1B)8+_yPR7S(@6aa5bhgPKL37-P zg)eMvXNKNcs+VMzlf`m%`iINEBszra7Fz3I+?F4YJl}+h8j~scK{{ONU}zvu<=A-*F4=vbANWE> zC-41{M7GBT;Xd<4ZlBUR{x(w@`kUT)oQT1UVh(_}MaG+vWl=d#YfjY%G|pe6bG*Bi4cGGV;FmMb`_ zX};m~psz_EGz9b~S6aOJ7BT9hA|?DoBhRPQ^73!m+n00HryRyA9>st8Ka$-;yctMl zMxM4d);b)kfP#3;m$l`&W(k?mr?3F7R3qE1y95KftkH(=acFitWg-8QMAC>uyZMY z`)TUFcp6=FZUqNJlI>V+zITETzQ=Gm(?x0~zPA-Wde^r@lmwhw zC-NF`7Fi>n$>!KEeQr(ay*WqS;mj|m7mPZVs`$h>6)EYx!U?_+(y*gx=ZQ%k{)B|j zsOQ8?+w+>~duH*#r;m9meBkZ`cIEi4rSg?}sq#1Y3emaz4pa8q(C|-+IqjHn6`4#z zmeT7)y!ZQUfNXLyI%e4Uk@%1@u#Vp7SvgxKYo@4ISXBxawd(ly_bZA>_JN6|PcTIr zmEp|kKu2r-U3;x9;F+!99&dBGr;)r^Y*QC3c6kuA{+T&PF8YuYGHNKFIrWTO%ujj% zM`q0aJG-dQ6->*89jEQJ0^VH(i@ia^$4^#osQo^CT(jZ`PoR-agu51Hp0?N*%ZI<} z7N|VFElNo~o#8w``4)LMf+-l7vruCUNszIettP>bA`blRjhU~W9sqkkW_+fNX21Ru zOwR|Bj!u4aKP&e4xh%nf&=zWHus@-i4lUIVZr|$PI3Gx|Oj{vUb3c?SsIq#0Gb-V8 zE`=`ae@Dm+lG~pvEmj-dM9eQBK}rJ7*)dwWU1rh)+wuv*0(%~{yuGw$^HmB(keg>; zDf3#w&_$LN;oi%&W}E2yzS*BAqRdkXz1;0F)XAA@m2eCvSlKj4^)`;TKaOjrthoBA zlbD;o{QbAZA6-#nVK8Wgv*FSau~^u9N5gja%@8WENP&0AKOOTcpRtlIJ8jH_`EU{Z zeq~RrdSy)4`I_dkLMKp}=q!nL>IK`S@o>?pG@4Clc6Wv74j&3y3V7bGy&`q0-+v+y6 zFwNYe4Bebbw0FLA{N0IyW;YFAt>c~ba>Rh{a(odrk_PilHf6#kLp)BOkZ^?^D%{Pb z8g_hh{-^iPuI z#TItMZ?3-+@>jBO7bEaB7+G6L#lkyZKH|1G8Q!qG)yVMr^$|s#=6>_RH<`CgEcN2% z*_QR>opcdIx?t zrBXtseJP6J5NuF!d#ZA0&BXU$v<(^KspN)l8b~u1jCn+Ueen{`yhz(tyE0*f)X+otSvjiFuQ&tAP`feIPD? z6t^r{J82|SH;JXYC@7R#ddw^4M%t8Vztqj1}xpD(u4 z4PM2zTL*hkD3$;3ack@`g~yk@`gLLbQlBh6i(eRM3wIN^ml$Z+4M@_aYbU)_J4jSK zVfuQZ$<-s$gSjc(pwn7Ty1;Au{cSLd*J1wsx#ddr>C*^H6cy z&W6_sy$#5_2HfUU7J#sE-$b4sO3QkqM1!8|HIb=7^J^<0@aj0}1%J6isRF|BN<9hv z<)PM=m(th3?|q3m{y{ekkMCI3OCA&Z6_dCs#H(Kh{<`|9M({aO)Z$3|Gc%cnss7#< zf}r@q8K%rK`)wCngCmk{QuOxWX9FEwUaRCJpOKT0>wKQ^lM)iLO?TCxm6RM2i@x#Y zBpWSjek$7c;Wf&gH0`ts;z3W0*#L*t0){+{h4yJ~7=c)zj)zdSg}YvhBdmzUpn3~# zrs~atwI#PpodnyPn0VKoYZ6lx(!_u+Fu|tBp;L$gkFD&>n-zi)^lf6%24|u9U>O-^ zzi4Gpx?>*{PM6qW<8QWzjrcgY=#MosoT()cVmOr_{>0-j&~n z@{`YY6v4{bB)J;7T7;`wc)2i`98KgG!w`Rk`~zSFhr`fx2`FQhS_x?x&-g%#5eWNOib10`ysD(^&7YIOneuVzssi+`$DF=PU&N(e0g=M z)FUlv2@LR?N|Jf7b@5r%FHDD1a8sN%-}p^nC7sH}`iz$*i?*E+aMJeN%2U{*Xe$+A zT8$&WJ>68r{*YfX67HH3m94Ao^5;$f&1PEt)whO4iMONFxZjkrsiw8yL-VD$X}l

7%H^+s>*s7Z(-#wJseM>W3c>H zpbMYC*$7vCA@)p>j>Ox`I01_ih9G>0hkF7{`(^RHsFX=I<_5*-`>7DGUGwXD(oMTTADISoRKPÐ}iz)!yxJO5Vi-^ripy6pyvQFNsa@%TX0+$|rPI zvdJkbja~Sm~sA6x8hd!z>M+B*uL$I z87k5&?OJ5>7m{#v&|z!kmyxJB`fe#c#t((BY>Tf2OsNb$Tv^Wf2jD%1Rq5w42y z=0Y?^#ITX!uGc>Fc{6=`?xkzgaa^x-p3jyU$PAiwHKo0_ur!WiISRKoR{f$^f8@OO zVehQYfrMAH5kWrVZhd0MaeI`qGR(Elj;D&Fm$00z5n&f!lJkwpd{F;5Siwz0S04AA zVTz~>(;EqF5W2u*Xd@8dvS$j0qofP14}WG}!*_o!#CC^lF7TYHAXs9UZCI9-Mevtd zWyG@LaKpagg4H6t4`fdM4;7ZaslIYoB2RLNr_)b8w-O?JVma`?(h-~#W zd3)S$N6()q;Lq$DhTtO-HI~W8B>DMVVsM!bl;9_(-g1GbLLysQmhVRkR{!o-6YaP} z2eh_haQg#QkdvvVSDJiL1uT{Q0~z9VVghW#DCj?at$+~T2XA$Uvc?B6!n3eVzv7HB zd#4hR=&(;zfe3bdgzji2^5$oO#*|gsNf%dv&F||-`2?2wJ`Yz5lU?@B*aDpU#2;ou z@wS%?x<|y$>s9-bFXkIxU3J6Lk^FQID!&l@<%-%yA5(b0u@zbN(2bm1(_%CAR8Pzw zwiprp#f|0-YL03-1&a13u!NL(uiMP2-2BC282_bsA$-&`2$f6T<2=cOkedY6e#L-h zDG}`6$2a$iwM$t)f0A4ncI@$!Se{lsdaLy(qhzl)u{L2UAu}h@}a0%s_F>seXwe>b{IU1 zGSC~=ds!DK=nV%>%x!4CnZb2OoKKr6FsbTQnRGdpFPGWy)ou6T#?Z?J!ZFeQ&<`$! zHVZ}xL~G?qiu2Y7xkijN5^Q;dhN^_$w{Vm{shxK9=MMc_F^6xcON+^lZJR!X@8biV(x5wjp#P42?@VBRX9M!t zG3i3#$;#a%jE#j-P-P-FU>;*31jFxMzf*A`+{kk}@2NS}lx+6+(Z2_2-`OYq<-zy@ ztZSp4E#8f`)9zu3>JvwuNxd+#T+GSy5 zq2DKcuI)O?LCUG?{`3*khqFY6-&GurHm$-xL~6$}v5PR3H{Z3>R)6EN9IB z#)VMzpHCQ_`LnGznto<-{D2!{yATnsFZ6C}tFX@Jq|>77rXbE{@6(u^`@y%Nq#u(g zb2#Px*Jmj=#79>L`n1!En#BbMFQ_8V^Y^Dzg2qPP5_qeSqp7n~II)s#L?YGNXUVj{ z?yCR5!A}<~#7{5~eE+=3dJ2sIG6ULX#C@fJTlS-c3&FjN%f~BkoH7ic^FSPHwF}dH zSVL0L;{&!)2&tz_)5S_ZmMcf?eRNgVU03i5%}UI3CSWw-un?}$o-qvb1O*k(j`O2e z9F{p1iXQ8a0GiDYJ3=P%rZMa#v4V?M>2rLF=l9UCJjcS6iymK?XkUfT z*0LGO<~cNL-rv7_5QaxW$l9qQijz8`X+PXMcnH_#Gd7KCK9|!HHPcNW1hd|lP$W;KPD~UyCzz>Ut|$p zXG1ljwKH8V-;jUkB1je~Yti}6#K;O(xxO#Z%}3k27_={#S& zyguJ`>IA?(6#9&&+VKW;#A;j~7P=nmUt2+$AkWM`Gx0>z|M~9zL16 zuM=2D#gvHKSFqy_al-q=J)*H(s-E#{oP`&b#h4P8v)x0|CwnJHl#y^} zxb7w~-rYK$_B@@MYbyEfa5WQta8sz+>~w_WjgeU7@-3>8R9YyypuJmdPSI3 zB$+x`Sz?+ICZN9UrXDuQQ_kBZGBr3KsOjERT>Rt0ZN^ zzK~5AevFr@d3I%7F@6PUZE_s%=~5}=IZ9v*OMMnkT)#a^r}`C`v)B5eQo_CP>$fiMRar8QBsV*~lc$P8t}GWfibF}` z9%c>R>unvYpD3o*$Pme0eQh8<4hdsWt2^$Br3o5#88ILV3qh7{$%Kp8Au~r|K*WCO z2n-Evh+&B41mk_?v(Q_S-mKoHDGpHi9^?WvRvW99NVEi0z-|fwMR}dLQ;mHSFo#Ko zF~I{40V?bNNUfk*zM51w54ml9I5b6zM)V2{5vWq9y8baG0ukia^awN5;1HdaH#U}= ztbOvaCsDrcpkpQnW%WZ_yC>cT1qMd>&fx(0yq=1q+wc-S#;%MFP4ly_%HE+0YfYY} zpzE#N6DBAFN%^A>zg_o@_ikGvdjobUc<;(w*f?J%3D&0ZbdjTjcOPA|L)^DmndAlcX7GI2 zEf+p476|Zfgk3==5kWZl6e*>rUwd-X*?cqQXO)kXMNb%$lb<)JAeb;8dbN^Qw z<`&1~@a4d5v~Llfl2PMVpOzgrmVq=;%LzFf7|N*N3Mvz?=T67gOwC=x;%HNlimXcQp3wHK+b@wpJ@SYT@nGd2%lnxeedW+!4J;X##h&Zx)V{kzTQt z@y(#BUGJCdH(Swpq!=Hem-X9|!3i4Vg0l`?_2T!7PQq?L!zg7z2rl;V9kuz#-Et0l z11qDW++u}=r~7jVdjEEkS`a8Jj>HE=NK6&}p_!0W5s~V!*PAk>%>7h{uVPBaD8OD} zv31Tv)8%;IOnRAqQXzwUgdTQT(L z?t}!V)69C)W%b%zdbKtPX#cVRCiU?n&p|U`#_eLWXzeB;iPh3_mA~hdL4?c5 zbv`zvgBy{8F{%Gmlry+H_%(+cZ|i}vWYh@tz+kgU_yw%?KZRaS0^zJK-rE~+i4G9U zy$HnZ{d*1%DHxD~NE}=ek*uP@>GGK9VZbJb8`a$4Kb8^qX6O-cu|e59G5?lKit-`+ zFD7_hd;n)Ke@3+a^EGixdBH!pU(j-XLL!AS@mIPrsHAtTuVO~tbVIp%6kdfpwmb*Vy)oS+SW0nt}@b(8N#oK*O29H=-6JUhVc z^ooy_y-}Up%<)Ht7p~<4swc5tPP5(DB{L1_mUUE~&zcdWdSuKV|z}Xi@7-+d{LyI0HE2PTrCtZf}n$=gHl)rX-M*x~iAS$qrfw2fa z+=jJrhj74fS8;WbE}zONT^*P$Lzg9719Hr_ma<%JUb+*z{jd%VJe|6)4caYW5H>-& zd#s=>%}u~f1eM1k5uDbLYglvGklWX)GtutctaI_GJu)8}Er_7l=UW}mY;y7FPK&yS zrWtzgCZMV-xmoWL{Wphc7_FfVAU1k)`Z9~Pwz*kULM~X=>8!FZQEVrVmrv@40)Ec% zIh9AFS;Zqs3HyQjR=&tRCY&ZwIhE2uiq%ZO-$&PZF`+(4+VCPed9&XAnc_R=gALy~ zdIU$juW)Z0HX-Tr=7#+%GVK8`qJmbZ!fC-~{1xHg2t*k#t{U~xopEoyKg*#u>9I|d zu8x4QEqI27ufQNjJjOgqcN1XBQ-P2B2@N5~%9uXg#^3@bqN_xV=UMF)Atw8FXUcU? zV!s&KdPZ3r{*?Zy7^Al|H{yLi4q0$Zkq~lbS90_$RLW9F+Up>8I@K6oJU4c*9-(aw z2Vbq~5D4f1(E|5t^U!K(zQVA9xvSL_#})lz8^|!HXd68FdNCW&r!#SiUExamPsg*4 z3*2Na{FoXREnEqPxh!#$1!SI!>9h0U_qe4H(CvYFyEohG{#p z6i;h6J9A>R$tR%y^OLaQ^=CJ;;Shz!_y&MsLkG38DrsrelKIFg0jUyhe0vz@FVI|~ zTSu^Zn1Q{Ypu4U+pUYEXO8%$~QvV_lE7Zq?E-&3U^+v%VTmlXE{y(hX#M|CaUMc-( z3$=B~X`OEll5KFKTj(SKc6|(MI(%+%#3`-RygNUn5%doj#!9rjmz5WE+bZ%Ea7u|% zi+E+;+bsx>EN0nyEY`{R!^fUHPaJCl+``gU8VSM!EE=$~kJ+;ZHfEEhw`4dXvRvK1 zV}SE%-=q&V(2d!28tv7FkI+^Q7oqY_U)Z`0@iO-3-|W{_Afq~t4`uhAz<5qf`&$Ht{o{c++Titavtt&h;P$GPENRI`xBKHRm)bc;eYJ$eji z5dHaF&79)yT?tpeNv;f8NhTg|#=6Ym+j-6OZ~N$&wikH3M;nqIJG_RUBY1p{Q6)TT zf}#4Cq2@uM$WkqgNP;-9=V%ykv(|a_ncg~+or^ttw~`JXj6%q>1qg79>fbi$TcO{= zwQ>(`l3!q7o|2W<)jqL0TpP_fueC!EJ9*yTJt1GLLskXZ01nH_R_UyQ3AGtv-S35^%^CuAj%vzZ52~}xA@LsCcS=Xt-|aF znZpxz+1Ss{jUAV?UIvRQjf})jhp#RuEPoPF*za{25Y1;kiBHA4Eo?jnZ$?#67e=%f z0fXe-%{rMMMsXS+vd2X6E6qud*!)Xo?Nf?ev1_>=w^qcdg8pNO^ z&ifx)!blGHc6Hn?&&7D%=YO~%IHF*|aSEj}7rnwnPK221y_O&>iqgMyMe%fMr8WKS zbTvNg{@dl#IPAw)b+}1qyI5v~W}hv_aU0DBMCuB)*nK9I8oW<%$vVzoWe(ma9+ON6 zj$td(P&`iLBU9X4__bew7}-BZ2A~%3(%pAU0!Fb(zH6hf?Y!H`3KtCBxucdMO?Dps z`mf%d3OGM@bZZgMY-d|yJY1JZS;_f6lZWaRs2l#;(Q>=lHc&0rpuniV6h=Bptkyr@ z4kA8x97a^@WWE0&YpiB`>`rn#9q>3tfJQjqSh_!UXcKgMB5(k(l2UJV-{teaR0kRg z=QWxcCYfqf{F`2NYVjn_C2ohKu$X2Nbs0oVX&_kWp9T@yv`N(z%jWWaDA)v|U}NxghPrhnkP|i{NMGaZScDN9pCyf3gxd(cFML?NX~0*; zaw~EtyQAD(Hd$*C5Yi{fr6IZ23sp$}el5O2E?^Hkg218k0nV=d8a?@3Q`qwb zub^?2>tVA~T`Ro?)QJ5#mCTE_rbghNqI%LPsQ*6rU&1G_+dBVIK0#JA@2uxLOFQF1 zbFb(DLjNZB(74-}NIDfdrzb*aKz~ve%zU*`%+{7*G)Hx(JM|6&hm8R$-nVT4SavHaNot;4stkv+?cO$Kqlc*3V$3{;8 zFl(3kehOP%S_`TN=!yc{h`MsP`dXtag@eAydW;A-Kv{x0J*!vyy@Z<@3!>vo!^QWT zF7gnx6P}_errX%D>LZ3qUbS-B>H}&=^#-`LuPn-Kl_^>En>~ZaHHue`x?_l@3(bwfDn4dd6zdUfYamEH&EEW8c-Q(r|vKLjl0FjAnJ0 z0Hi`W(0J0gRo=$t-RdF9LcUT*onXc>G6+#SF5jNOa4J0ruU+>#`WHhtkF4>zY~S{L z!2^BJ-)zE0DvrhLob;y1jK(UFLkHPbqEOd5R#OPy?VcYoSKG*M!g0?6A&nfVR6ZGl z)AjLZOv!BI2p@uCdD3q!S@9o5UwCZ}%EQGJ8kO)AfV4c9lr@xYYqTVHMS8RuM9i$+ zI-0{ziD{*V+EHncexU3DrQx*&sdZw=|ytWu~sDTpS8U>_W76aKyov zRolptZ$H_}ZPtXyxfK!1C=HbgH&}FeM5=(?@+vvwm)f_bwNW$1o|}I)+lOaH?c+%0 z)I4I@30t6Gsa@o$$RthZ|wK&2Ch> z$J>gZds%fwGQUaVRGPiGo~}_#f0#{fPh5vO{3*Uaiq`#zq5YEL_xf^8vtmp4)Ds$? zcNzN1VynGX#_bM;se<^w5=xXmdiGt0F*>lK=K#qxM{u~uOh@!4nCV}5ryv2_eJe89 zAX6T^vc25^!4@6>hWN4@>8&3DOi4Vq1V-y);y4(3LjukE55c>@_g6^pikh7SBE&)Z zviBE%4OLzOu9BAb;d8KSPi9-AL6A%OH%Fq7LfG}E)p%Q-YNSiYDk=j&ec*9vz`-MA z8p&M3VAc^crJs@M4yK)z0#}Ed1F5J+H^)C)>}Cif4%_{7YKAfw?m6WvqlGG@sv*S6 z(uZeh6eWQd@8x=evEUQK1klj$YN?tM6w^Q`w~b~7;vjiMx}2X~w2*YhvqG(~w(jUW z{qaMaP}Y&`PT;yHHk}Fkr%C9)q}hU;IQ(G5DZZ;!&TIoF1$whVa|*C}vk;G_?8aB6 zCwXkcaf^<4*}R~IGKCbEjulDg5Jc8tOEWXGRVGc;&a=H&*>&WRij_x4U-4j7su^1I0MZ)|?nIpt@TAgd|4*a}2_iuv5Z*uaKN^)C$4f-QL60Qso?~K!EBt zW>C+78L~5@KUq~K^wx$gu&r*-F?-Kj`t{*uzMPwho5xe(qf>kCZY(U_& zHZ4p%ExTP#_ZcdbdR2As-Mqadjyb5tO8p(PV+2ax7@1dDar3BelV(C}N}csg{@`ba ze)35Q)-=H|^};*t9D;}O%msoUdLZagX@Z`dC5%Hype2<$@DHG zJs%CS5NFBj7}Ip=miI_TG*AExd~c$2N#%L6W;w3@3L_XhRMX&vF>!HrDB;w8{K@ogIvj^C6~QVt9XJu z9d>jhRw&fu`r_I$U!hPVSMR~-`?EIS$B!o!`;gB{7DTsa;llzwm7RoKhS6UPzcVm10S`scWsxUdO(uW#VV>w>BTZLz1+wc^dU=j%Cvi9((X z^NsF))ihpUU+R5P(eWFhyJ^DcWQIKE=UZD2(3AE$u>)5ooj~asm)wBlUr8^-8_v;G zLxOc1y$dtwJnqQqu~}b<#Oc%2`m4C?zFNH&7R6f`OPjO5X+=IxVOOc53gwaY(E%-i z1w9G#x(vaOfJHrAB9jjd7oz-{7zb9)P-!2b&r|xQ@e+yYA_@28#TW%}6dE|Wm=hJp zA?ZorEhUA?D}(-Ubp{n~HZZk)Zn%<(UhP)p)Ef~tnP9xAV?e;}MoSp$hJ&r8X21eu zO!tr)mB^jiCj8c&I7M5=lE?UbqWR(sznWDi-^DMgtNNR>1OY61nkbIda-a?!xJ{K}@!HaH3>*A=qQu^n=w^$lBQV`}h_U9U=w>R$7%UOh%1>sZ?t0 z9<=y{kQx!W%Z_n~!Rr#4f!WyQ3sI#Q#U*y5^xJR~Gj#xIaDq$oZ9_u_g#YPhgaIm_!v{203Y zZb5)ZS}a+9C#`K2a^BvFCquB&Z}PW^$;qYYRzNXsm=SJzyvcKnnOHPpFIE^j8ks;U z9rkXr{Q>zde{KnO;3ZA{qb`fPE>;2MnvlXfA<4Phj;I>jZ<;kL%?e2hh};rjb|)Vf zgaN23&t;2vDjzv~cQ)g_T0vOCCAD^?h&Y#z!q$}N?l*`AoV2y@D1`WFnC7 z1%(8>_Vlyzrqo!i)poIAU+o32GYFTsIQqKV^?DnYUnmfLZvYS;kxXj&=IwPzp>PAo z;dnysM59U0-)FG{Bhh{F>okR)>l0Yat!zzg_P}gpgWFX1_leK4ZJWsnf&Lt-3{@dUEKm<|%6D_9HA=F2Ip&;~=dO+yOZ$hdE{+ip%tCmkktb&j#6cjosO_+A5s3c-OV6ElGFhBvPeve7c)zE_FPo zmNeUfyHh+@ke)q*AOz83tNgc0fXFeGR^~!t`i)qkx@NEO63^u_jxr3%e@9q8Zv?d3J4h^BPmh@19T!-dIq@R@+)cwRho)#J8 zoFc|sldh>YoHiLpLR@Xs6fpNE**pmJn=c9DVB;F}KLP^+GJL$SobzWdwGb?@-td-H zYOm0}CCX1!dsRlxVbc(bB(&PKq+4N1aCm(UXP(kmUx0SrSoe?+9q3vA{jAfb!rXnyhBeusVeQA_1}mOombo$1bjR{+&Ma2 zts=JNla`ayi4Au@zhk5xbKUbq+MUl<@cT{7ji@zulzU9_l>Px-_q!qo?C8Hi;e8h- zgTT0ilKcd0OWhRDqF1Tb(Z1YhZR&-r&34kk&b(5#{m1WkjyLG~gur%XtjLnQlA(ri zyv~;t7hR59sO59_cqcCfOMH*-=};ZoY(zzZ%sN_D!ZIFC8QZ2m`%9 ztp#EzEaaS@m!s%j(8SsCO02LqWxh~RD210OS7ApV-$9Pu{ z8d?C_^ypNShm%!XD%B*32_&9U2qGCb$H)7C+N@7d>frs9ZtBhrM+jQlI z&1)Z_l0hHo9fxTv$o7aVQGNH>V(wuxa;pDoVKO5$!JwfqbBQ|lhOhEh`R|`UoG)^f`k? zsF**0gzv&BN~a@|5)`TMOnrVYMmn3RV$$9N`~p?-?>j~FJk3}1n3MyOY9$E3-v8EL zEhRjIWb_PrvbEZkP9gvPCV!xmjZ@O25_S-*N%%9J;hi$l?i2>d7a1olNk_>_uV8^k zTfz{xHEjT{W7*7!gnHi}%9DXG@j0ufdQjo)>Q`BS3<%MLV&UNs{Bh|i`G>12YqaG) zAd)>XRo53>&0g%8;yBQ&*AG6qdSR1Gh*sOSAnnvJv-%XpYanhk=f4w9sO|oyQK{yM z>JYGeE~hv`TvDs%YzT}H8%M#Q`gk{&{9?(#SKf{G+<~|X(%eOsF{x-;4NT#DhH6QruvQX7_^2+7xcaWFmNQ zyzVq*U#O3%!IfiX@2KK_=1^CK^mfFg$NyNXA~5vCKNvS_{Oiwr@8D^8i1U_$53!`A zCpW>rOK>Co>P&S^@F@GLhg407c3e`~hSphvMf&jC^gszP-y9Ny6gT3ZOAqOAKRLo{ za)jgJ(U*2+EL~mQpu1f6f^mw_na?_~DQ~_D>sq%kY3| zm`KC*%Yz_oSf$ zxv@kzuuJ|2z=yox=tBWE3zHaS^&i%F!9>*2$)v1UsFUOwlwzTtOWzS0G-S3qr234cmNfL!ePE-^qA=i zpWyN~!Ht%z3^%(6sIh1tIOck2C&yg zAqg(5(`BzEHf?VF+H-wqc%=>O5rE<+E%r(^1Ci57I_ACX3!Xn$?eDs&|##kNye##wIkR{if<|n6GR0KgwR8A6V{(fgX`HRpNzl zNs0p#P2%q88n+zh%Xj9;G5-B=K{Z`nA?pJ3wuRI5t^Al57UU0 zk<(Z;1S2h{_^N>iwXY}56?NS%EvRS5ac*>;BmKpVe7@91o!fqScg8v($q|CWbD{0WH$#hHjr%E2bE5ww zkQx@azWFawR(0^RKN)9WWGJGrmpIF2K|>U*(uZ99_W`z<9YcR$*-*&aCbjYDoifo{NQ1Mnn+eFb;98K)&$gmxN?_8}8>v!AMe9~ew|Ma{Hx;spC$A1S|lg80~N)uMycNMz;j(pl824&7o+Z`6H5 zqPu>|J<+WQu;ry|PKbTt9u?`eXqs~`Zx|^KXohcW@Kcr52#I4$@{$N zi|P0SZDZ0vvqVS)?bDjiyPGP3gMe6U8LwrtHB6Gib!*JAA_?jiV+ zx60XNj_nR6g9(b+&y!Z7Il)j;t`mBD!kca!?qaO-;6~gE?Z_MV6A2TiK=a07D`>?9zk0C~tn$kW1z>9}_0WqYVtNMHd^WO^Ef#3U*# zYc%(~ZKnYL68(x8B%1TUeNaK@|KutI|MmYxHZ}k~_5%B8{lI+K+TIE`gCD~PM6FW; zK`Rg9*MR^82;eZwxwj>Xz#u0#I34f?%*PA>nN4}=4gTOiS>^v1nX{7TpX3Jd3O67T z%#BI2wvlfFTsq?gDtr(X*4{E(9rJNI4J=Lz(N^8PTptt}P~ zu01UO-{6{~+_P0LD2?6Ho&Xd7fGG5^ul>LD1sut-PK;<_@KRaR_tvu(g?jG!4V77 zPL)nwrLk+3MO_zn(Kj_v^V)Qv9x1Y9!N-c&Gil1_AMWmdC^?#L Date: Thu, 9 Nov 2023 11:52:54 +0800 Subject: [PATCH 24/29] ramin/fix_off_screen_calculation (#253) * fix trend drawing off-screen calculation * improve DraggableEdgePointExtension doc * Fix issues after merge commit * add repaint boundary * remove the print statement --- .../drawing_tools/data_model/extensions.dart | 20 ++++++-- .../drawing_tools/trend/trend_drawing.dart | 14 +++++- .../drawing_tool_chart.dart | 47 ++++++++++--------- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart index 131238fb7..a010474e0 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart @@ -12,7 +12,18 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too /// /// When we know how visually can represent the edge point (circle, square, etc) /// we can improve this value. -const double _edgePointOffScreenSafeDistance = 1000; +/// +/// Currently as for a safe number we consider the half of screen width for a +/// [DraggableEdgePoint] to be considered as off screen. This will ensure on all +/// granularities the [DraggableEdgePoint] is fully out of the view port. +/// +/// view port half of screen outside +/// ------^-------- ---^--- +/// | | * | -> edge point is NOT considered as off screen. +/// | | | +/// | | | * -> edge point is considered as off screen. +double _edgePointOffScreenSafeDistance(int leftEpoch, int rightEpoch) => + (rightEpoch - leftEpoch) / 2; /// An extension on DraggableEdgePoint class that adds some helper methods. extension DraggableEdgePointExtension on DraggableEdgePoint { @@ -21,6 +32,9 @@ extension DraggableEdgePointExtension on DraggableEdgePoint { /// The view port range is defined by the left and right epoch values. /// returns true if the edge point is on the view port range. bool isInViewPortRange(int leftEpoch, int rightEpoch) => - draggedEdgePoint.epoch >= (leftEpoch - _edgePointOffScreenSafeDistance) && - draggedEdgePoint.epoch <= (rightEpoch + _edgePointOffScreenSafeDistance); + draggedEdgePoint.epoch >= + (leftEpoch - + _edgePointOffScreenSafeDistance(leftEpoch, rightEpoch)) && + draggedEdgePoint.epoch <= + (rightEpoch + _edgePointOffScreenSafeDistance(leftEpoch, rightEpoch)); } diff --git a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart index 97e2a54f5..65fd84f5a 100644 --- a/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart +++ b/lib/src/deriv_chart/chart/data_visualization/drawing_tools/trend/trend_drawing.dart @@ -9,6 +9,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_too import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_parts.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/drawing_pattern.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/edge_point.dart'; +import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/extensions.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/data_model/point.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing.dart'; import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/drawing_tools/drawing_data.dart'; @@ -163,8 +164,17 @@ class TrendDrawing extends Drawing { DraggableEdgePoint draggableStartPoint, { DraggableEdgePoint? draggableMiddlePoint, DraggableEdgePoint? draggableEndPoint, - }) => - true; + }) { + { + if (draggableStartPoint.isInViewPortRange(leftEpoch, rightEpoch) || + (draggableEndPoint == null || + draggableEndPoint.isInViewPortRange(leftEpoch, rightEpoch))) { + return true; + } + + return false; + } + } /// Paint the trend drawing tools @override diff --git a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart index 71416f312..cce6859db 100644 --- a/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart +++ b/lib/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart @@ -79,13 +79,16 @@ class _DrawingToolChartState extends State { .toList(); return ClipRect( - child: Stack( - fit: StackFit.expand, - children: [ - if (drawings.isNotEmpty) - ...drawings.mapIndexed((int index, DrawingData? drawingData) => - DrawingPainter( - key: ValueKey(drawingData!.id), + child: RepaintBoundary( + child: Stack( + fit: StackFit.expand, + children: [ + if (drawings.isNotEmpty) + ...drawings.mapIndexed( + (int index, DrawingData? drawingData) => DrawingPainter( + key: ValueKey( + '''${drawingData?.id}_${context.watch().granularity}''', + ), drawingData: drawingData, quoteToCanvasY: widget.chartQuoteToCanvasY, onMouseEnter: () => widget.drawingTools.onMouseEnter(index), @@ -96,20 +99,22 @@ class _DrawingToolChartState extends State { setIsDrawingSelected: _setIsDrawingSelected, selectedDrawingTool: widget.drawingTools.selectedDrawingTool, series: widget.series, - )), - if (widget.drawingTools.selectedDrawingTool != null) - DrawingToolWidget( - onAddDrawing: widget.drawingTools.onAddDrawing, - selectedDrawingTool: widget.drawingTools.selectedDrawingTool!, - quoteFromCanvasY: widget.chartQuoteFromCanvasY, - chartConfig: context.watch(), - clearDrawingToolSelection: - widget.drawingTools.clearDrawingToolSelection, - series: widget.series, - removeUnfinishedDrawing: removeUnfinishedDrawing, - shouldStopDrawing: widget.drawingTools.shouldStopDrawing, - ), - ], + ), + ), + if (widget.drawingTools.selectedDrawingTool != null) + DrawingToolWidget( + onAddDrawing: widget.drawingTools.onAddDrawing, + selectedDrawingTool: widget.drawingTools.selectedDrawingTool!, + quoteFromCanvasY: widget.chartQuoteFromCanvasY, + chartConfig: context.watch(), + clearDrawingToolSelection: + widget.drawingTools.clearDrawingToolSelection, + series: widget.series, + removeUnfinishedDrawing: removeUnfinishedDrawing, + shouldStopDrawing: widget.drawingTools.shouldStopDrawing, + ), + ], + ), ), ); } From c42764dbbf9bd04c0b962ff6cf76a35582592590 Mon Sep 17 00:00:00 2001 From: ramin-deriv Date: Fri, 1 Dec 2023 09:59:34 +0800 Subject: [PATCH 25/29] fix: fix deriv_lint dependendency --- example/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 4e81f5a1a..89fc5ca9a 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -26,7 +26,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: flutter-version-3 + ref: dev flutter_test: sdk: flutter intl_utils: ^2.8.2 From 51679149c1514a64e0b9c7717a7941ffe66b470b Mon Sep 17 00:00:00 2001 From: ramin-deriv Date: Fri, 1 Dec 2023 10:05:48 +0800 Subject: [PATCH 26/29] fix: fix merge issues --- lib/src/add_ons/indicators_ui/adx/adx_indicator_item.dart | 1 - .../indicators_ui/alligator/alligator_indicator_item.dart | 2 +- lib/src/add_ons/indicators_ui/aroon/aroon_indicator_item.dart | 1 - .../bollinger_bands/bollinger_bands_indicator_item.dart | 2 +- .../donchian_channel/donchian_channel_indicator_item.dart | 1 - .../indicators_ui/fcb_indicator/fcb_indicator_item.dart | 2 +- lib/src/add_ons/indicators_ui/gator/gator_indicator_item.dart | 2 +- .../ichimoku_clouds/ichimoku_cloud_indicator_item.dart | 2 +- .../indicators_ui/macd_indicator/macd_indicator_item.dart | 2 +- .../rainbow_indicator/rainbow_indicator_item.dart | 2 +- lib/src/add_ons/indicators_ui/roc/roc_indicator_item.dart | 1 - .../stochastic_oscillator_indicator_item.dart | 2 +- .../indicators_ui/williams_r/williams_r_indicator_item.dart | 1 - lib/src/deriv_chart/chart/main_chart.dart | 1 - lib/src/deriv_chart/deriv_chart.dart | 4 ---- 15 files changed, 8 insertions(+), 18 deletions(-) diff --git a/lib/src/add_ons/indicators_ui/adx/adx_indicator_item.dart b/lib/src/add_ons/indicators_ui/adx/adx_indicator_item.dart index 8b6a955ba..03eb154c0 100644 --- a/lib/src/add_ons/indicators_ui/adx/adx_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/adx/adx_indicator_item.dart @@ -1,5 +1,4 @@ import 'package:deriv_chart/generated/l10n.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/widgets/field_widget.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_item.dart b/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_item.dart index f99af5179..54ddce46f 100644 --- a/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/alligator/alligator_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_item.dart b/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_item.dart index f44fda83d..ceeb154fd 100644 --- a/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/aroon/aroon_indicator_item.dart @@ -1,5 +1,4 @@ import 'package:deriv_chart/generated/l10n.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/aroon/aroon_indicator_config.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_item.dart b/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_item.dart index 89e68b0c7..472c25ee6 100644 --- a/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/bollinger_bands/bollinger_bands_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_item.dart b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_item.dart index 3428d142f..03a8528af 100644 --- a/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/donchian_channel/donchian_channel_indicator_item.dart @@ -1,5 +1,4 @@ import 'package:deriv_chart/generated/l10n.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart index 5eccd97d0..b7b46cc8a 100644 --- a/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/fcb_indicator/fcb_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/indicators_ui/gator/gator_indicator_item.dart b/lib/src/add_ons/indicators_ui/gator/gator_indicator_item.dart index 3df06e1bd..64fb72732 100644 --- a/lib/src/add_ons/indicators_ui/gator/gator_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/gator/gator_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/gator/gator_indicator_config.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_item.dart b/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_item.dart index 002d6ee75..c15577fb0 100644 --- a/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/ichimoku_clouds/ichimoku_cloud_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_item.dart b/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_item.dart index 033e80043..e8c38e05e 100644 --- a/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/macd_indicator/macd_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:flutter/material.dart'; import '../callbacks.dart'; diff --git a/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_item.dart b/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_item.dart index 565a0885c..50757852c 100644 --- a/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/rainbow_indicator/rainbow_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/ma_indicator/ma_indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/ma_indicator/ma_indicator_item.dart'; diff --git a/lib/src/add_ons/indicators_ui/roc/roc_indicator_item.dart b/lib/src/add_ons/indicators_ui/roc/roc_indicator_item.dart index 4e52e4467..e57dc4c24 100644 --- a/lib/src/add_ons/indicators_ui/roc/roc_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/roc/roc_indicator_item.dart @@ -1,5 +1,4 @@ import 'package:deriv_chart/generated/l10n.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_item.dart'; diff --git a/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_item.dart b/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_item.dart index 6132773cc..4e4a8fe30 100644 --- a/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/stochastic_oscillator_indicator/stochastic_oscillator_indicator_item.dart @@ -1,4 +1,4 @@ -import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_chart/generated/l10n.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/callbacks.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_item.dart'; diff --git a/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_item.dart b/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_item.dart index 1c4a2a00d..706c63126 100644 --- a/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_item.dart +++ b/lib/src/add_ons/indicators_ui/williams_r/williams_r_indicator_item.dart @@ -1,5 +1,4 @@ import 'package:deriv_chart/generated/l10n.dart'; -import 'package:deriv_chart/deriv_chart.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index 38586d4b3..9388e6943 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -25,7 +25,6 @@ import 'data_visualization/models/chart_object.dart'; import 'helpers/functions/helper_functions.dart'; import '../../misc/callbacks.dart'; import '../../theme/chart_theme.dart'; -import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tool_chart.dart'; import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dart'; /// The main chart to display in the chart widget. diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index c484eefef..4b5a0e49c 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -1,11 +1,8 @@ -import 'package:deriv_chart/generated/l10n.dart'; -import 'package:collection/collection.dart'; import 'package:deriv_chart/src/add_ons/add_on_config.dart'; import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tool_config.dart'; import 'package:deriv_chart/src/add_ons/drawing_tools_ui/drawing_tools_dialog.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicator_config.dart'; -import 'package:deriv_chart/src/add_ons/add_ons_repository.dart'; import 'package:deriv_chart/src/add_ons/indicators_ui/indicators_dialog.dart'; import 'package:deriv_chart/src/add_ons/repository.dart'; import 'package:deriv_chart/src/deriv_chart/chart/chart.dart'; @@ -17,7 +14,6 @@ import 'package:deriv_chart/src/deriv_chart/drawing_tool_chart/drawing_tools.dar import 'package:deriv_chart/src/misc/callbacks.dart'; import 'package:deriv_chart/src/misc/chart_controller.dart'; import 'package:deriv_chart/src/models/chart_axis_config.dart'; -import 'package:deriv_chart/src/models/indicator_input.dart'; import 'package:deriv_chart/src/misc/extensions.dart'; import 'package:deriv_chart/src/models/tick.dart'; import 'package:deriv_chart/src/theme/chart_theme.dart'; From df5f4acf9ec6459f3575219810c13d669e4cc8ce Mon Sep 17 00:00:00 2001 From: ramin-deriv Date: Sun, 10 Dec 2023 10:26:45 +0800 Subject: [PATCH 27/29] udpate technical analysis dependency --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index b51cc27dd..45e53f113 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_technical_analysis - ref: flutter-version-3 + ref: dev equatable: ^2.0.5 intl: ^0.18.0 From da51929462ffed97f08904589056a0a97cc71e63 Mon Sep 17 00:00:00 2001 From: ramin-deriv Date: Sun, 10 Dec 2023 10:44:27 +0800 Subject: [PATCH 28/29] Fix merge issues --- .../chart/data_visualization/markers/marker_area.dart | 2 +- lib/src/deriv_chart/chart/main_chart.dart | 1 + lib/src/deriv_chart/deriv_chart.dart | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart index 43d542603..60d578b27 100644 --- a/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart +++ b/lib/src/deriv_chart/chart/data_visualization/markers/marker_area.dart @@ -79,7 +79,7 @@ class _MarkerAreaState extends State { duration: animationDuration, opacity: widget.markerSeries.activeMarker != null ? 0.5 : 1, child: CustomPaint( - child: Container(), + child: const SizedBox.shrink(), painter: _MarkerPainter( series: widget.markerSeries, epochToX: xAxis.xFromEpoch, diff --git a/lib/src/deriv_chart/chart/main_chart.dart b/lib/src/deriv_chart/chart/main_chart.dart index 9388e6943..9569febe5 100644 --- a/lib/src/deriv_chart/chart/main_chart.dart +++ b/lib/src/deriv_chart/chart/main_chart.dart @@ -13,6 +13,7 @@ import 'package:deriv_chart/src/deriv_chart/chart/x_axis/x_axis_model.dart'; import 'package:deriv_chart/src/models/chart_config.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../drawing_tool_chart/drawing_tool_chart.dart'; import 'basic_chart.dart'; import 'multiple_animated_builder.dart'; import 'data_visualization/annotations/chart_annotation.dart'; diff --git a/lib/src/deriv_chart/deriv_chart.dart b/lib/src/deriv_chart/deriv_chart.dart index 4b5a0e49c..d2799909c 100644 --- a/lib/src/deriv_chart/deriv_chart.dart +++ b/lib/src/deriv_chart/deriv_chart.dart @@ -334,6 +334,7 @@ class _DerivChartState extends State { dataFitEnabled: widget.dataFitEnabled, opacity: widget.opacity, annotations: widget.annotations, + chartAxisConfig: widget.chartAxisConfig, showCrosshair: widget.showCrosshair, indicatorsRepo: widget.indicatorsRepo ?? _indicatorsRepo, maxCurrentTickOffset: widget.maxCurrentTickOffset, From 49d7f9e4a09edea2f41a7d0438cc3a8982a7ee29 Mon Sep 17 00:00:00 2001 From: ramin-deriv Date: Thu, 14 Mar 2024 14:05:37 +0800 Subject: [PATCH 29/29] update CHANGELOG file and increase version --- CHANGELOG.md | 7 ++++++- pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac071598e..bed13a59b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [0.1.0] - TODO: Add release date. + +* Add drawing tools. +* Make the chart compatible with the `flutter_web` platform. + ## [0.0.1] - TODO: Add release date. -* TODO: Describe initial release. +* TODO: Describe initial release. \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 45e53f113..668986287 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_chart description: Chart for multiplier flutter app. -version: 0.0.1 +version: 0.1.0 publish_to: none environment: