Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,19 @@ enum MarkerType {
checkpointSpot,
}

/// Determines how the [ChartMarker.text] is styled.
enum MarkerTextType {
/// Renders the text in a uniform style.
plain,

/// Renders the text as a progress counter (e.g. `"2/10"`).
///
/// The text must contain a `/` separator. The part before `/` is rendered
/// prominently to indicate the current value, while the `/` and the part
/// after it are rendered smaller to indicate the total.
counter,
}

/// A specialized marker class for displaying various types of markers on a financial chart.
///
/// `ChartMarker` extends the base `Marker` class to provide additional functionality
Expand Down Expand Up @@ -195,6 +208,7 @@ class ChartMarker extends Marker {
VoidCallback? onTap,
this.markerType,
this.text,
this.textType = MarkerTextType.plain,
this.color,
this.hasReducedOpacity = false,
this.displayOffset = Offset.zero,
Expand All @@ -221,6 +235,9 @@ class ChartMarker extends Marker {
/// If null, no text is displayed for the marker.
final String? text;

/// Controls how [text] is styled.
final MarkerTextType textType;

/// The color of the marker.
///
/// If provided, this color overrides the default direction-based coloring.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
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_group.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/markers/marker_icon_painters/marker_group_icon_painter.dart';
Expand Down Expand Up @@ -196,16 +197,35 @@ class TickMarkerIconPainter extends MarkerGroupIconPainter {
YAxisConfig.instance.yAxisClipping(canvas, size, () {
// Horizontal dashed line from contractMarker to start time
if (_contractMarkerOffset != null && _startCollapsedOffset != null) {
paintHorizontalDashedLine(
canvas,
_contractMarkerOffset.dx,
_startCollapsedOffset.dx,
_contractMarkerOffset.dy,
finalLineColor,
1,
dashWidth: 2,
dashSpace: 2,
);
final ChartMarker? contractMarker = markerGroup.markers
.firstWhereOrNull((m) => m.markerType == MarkerType.contractMarker);
final String? contractText = contractMarker?.text;

if (contractMarker != null &&
contractText != null &&
contractText.isNotEmpty) {
_paintDashedLineWithText(
canvas,
_contractMarkerOffset,
_startCollapsedOffset,
finalLineColor,
contractText,
contractMarker.textType,
theme,
opacity,
);
} else {
paintHorizontalDashedLine(
canvas,
_startCollapsedOffset.dx,
_contractMarkerOffset.dx,
_contractMarkerOffset.dy,
finalLineColor,
1,
dashWidth: 2,
dashSpace: 2,
);
}
}

final Paint solidLinePaint = Paint()
Expand Down Expand Up @@ -279,6 +299,84 @@ class TickMarkerIconPainter extends MarkerGroupIconPainter {
});
}

/// Draws the dashed connector line with a text label split into it.
///
/// The text is positioned near [lineEnd] (the start time collapsed marker)
/// with a small padding gap. The dashed line is drawn on both sides of the
/// text.
void _paintDashedLineWithText(
Canvas canvas,
Offset lineStart,
Offset lineEnd,
Color lineColor,
String text,
MarkerTextType textType,
ChartTheme theme,
double opacity,
) {
// Distance (in pixels) from the start time marker to the right edge of the text label.
const double _textPaddingFromStartLine = 16;

// Gap (in pixels) between the text label and adjacent dashed line segments.
const double _textGap = 4;

final Color textColor =
theme.markerStyle.markerTextColor.withOpacity(opacity);

final TextPainter textPainter = textType == MarkerTextType.counter
? makeDelimitedTextPainter(
text,
delimiter: '/',
primaryStyle: theme.markerStyle.markerCounterPrimaryTextStyle
.copyWith(color: textColor),
secondaryStyle: theme.markerStyle.markerCounterSecondaryTextStyle
.copyWith(color: textColor),
)
: makeTextPainter(
text,
theme.markerStyle.markerPlainTextStyle.copyWith(color: textColor),
);

final double textRight = lineEnd.dx - _textPaddingFromStartLine;
final double textLeft = textRight - textPainter.width;
Comment thread
sourcery-ai[bot] marked this conversation as resolved.

if (lineStart.dx < textLeft - _textGap) {
// Left portion of dashed line.
paintHorizontalDashedLine(
canvas,
textLeft - _textGap,
lineStart.dx,
lineStart.dy,
lineColor,
1,
dashWidth: 2,
dashSpace: 2,
);
}

// Right portion of dashed line (text to startTimeCollapsed).
if (lineStart.dx < lineEnd.dx) {
paintHorizontalDashedLine(
canvas,
textRight + _textGap,
lineEnd.dx,
lineStart.dy,
lineColor,
1,
dashWidth: 2,
dashSpace: 2,
);
}

// Draw text centered vertically on the line.
paintWithTextPainter(
canvas,
painter: textPainter,
anchor: Offset(textLeft, lineStart.dy),
anchorAlignment: Alignment.centerLeft,
);
}

/// Renders an individual marker based on its type.
///
/// This private method handles the rendering of different types of markers
Expand Down
27 changes: 27 additions & 0 deletions lib/src/deriv_chart/chart/helpers/paint_functions/paint_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,33 @@ TextPainter makeFittedTextPainter(
return makeTextPainter(text, fittedStyle);
}

/// Constructs a [TextPainter] by splitting [text] on the first occurrence of
/// [delimiter] and applying [primaryStyle] to the part before it and
/// [secondaryStyle] to the delimiter and the part after it.
///
/// Falls back to [makeTextPainter] with [primaryStyle] if [delimiter] is not
/// found or appears at the start of [text].
TextPainter makeDelimitedTextPainter(
String text, {
required String delimiter,
required TextStyle primaryStyle,
required TextStyle secondaryStyle,
}) {
final int index = text.indexOf(delimiter);
if (index > 0) {
return TextPainter(
text: TextSpan(
children: [
TextSpan(text: text.substring(0, index), style: primaryStyle),
TextSpan(text: text.substring(index), style: secondaryStyle),
],
),
textDirection: TextDirection.ltr,
)..layout();
}
return makeTextPainter(text, primaryStyle);
}

/// Paints on the canvas with the given text painter.
void paintWithTextPainter(
Canvas canvas, {
Expand Down
1 change: 1 addition & 0 deletions lib/src/theme/chart_default_dark_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,6 @@ class ChartDefaultDarkTheme extends ChartDefaultTheme {
lineDefaultColor: DarkThemeColors.markerPaletteBorderColor,
upColorProminent: DarkThemeColors.upColorProminent,
downColorProminent: DarkThemeColors.downColorProminent,
markerTextColor: DarkThemeColors.markerTextColor,
);
}
1 change: 1 addition & 0 deletions lib/src/theme/chart_default_light_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,6 @@ class ChartDefaultLightTheme extends ChartDefaultTheme {
lineDefaultColor: LightThemeColors.markerPaletteBorderColor,
upColorProminent: LightThemeColors.upColorProminent,
downColorProminent: LightThemeColors.downColorProminent,
markerTextColor: LightThemeColors.markerTextColor,
);
}
6 changes: 6 additions & 0 deletions lib/src/theme/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ class LightThemeColors {
LightThemeDesignTokens.semanticColorEmeraldSolidBorderInverseHigh;
static const Color downColorProminent =
LightThemeDesignTokens.semanticColorCherrySolidBorderInverseHigh;

static const Color markerTextColor =
ComponentDesignTokens.componentTextIconNormalProminentLight;
}

/// Default colors for dark theme.
Expand Down Expand Up @@ -310,6 +313,9 @@ class DarkThemeColors {
DarkThemeDesignTokens.semanticColorEmeraldSolidBorderInverseHigh;
static const Color downColorProminent =
DarkThemeDesignTokens.semanticColorCherrySolidBorderInverseHigh;

static const Color markerTextColor =
ComponentDesignTokens.componentTextIconNormalProminentDark;
}

/// Candle Bullish colors for light, dark
Expand Down
33 changes: 33 additions & 0 deletions lib/src/theme/painting_styles/marker_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class MarkerStyle extends ChartPaintingStyle {
this.lineProfitColor = const Color(0xFF008832),
this.lineLossColor = const Color(0xFFE6190E),
this.lineDefaultColor = const Color(0xFFCED0D6),
this.markerTextColor = const Color(0xFFFFFFFF),
this.radius = 12.0,
this.activeMarkerText = const TextStyle(
color: Colors.black,
Expand All @@ -35,6 +36,22 @@ class MarkerStyle extends ChartPaintingStyle {
fontSize: 12,
fontWeight: FontWeight.w700,
),
this.markerPlainTextStyle = const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700,
height: 1,
),
this.markerCounterPrimaryTextStyle = const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w700,
),
this.markerCounterSecondaryTextStyle = const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w400,
),
this.startTimeIcon = QuillIcons.stopwatch,
this.endTimeIcon = QuillIcons.flag_checkered,
});
Expand Down Expand Up @@ -63,6 +80,9 @@ class MarkerStyle extends ChartPaintingStyle {
/// Color of line for its default state.
final Color lineDefaultColor;

/// Color of the text displayed for a marker.
final Color markerTextColor;

/// Radius of a single marker.
final double radius;

Expand All @@ -89,4 +109,17 @@ class MarkerStyle extends ChartPaintingStyle {

/// Style of the marker label.
final TextStyle markerLabelTextStyle;

/// Text style for plain marker text displayed on the connector line.
final TextStyle markerPlainTextStyle;

/// Text style for the primary (current value) part of a counter marker text.
///
/// Applied to the portion before the `/` separator (e.g. `"2"` in `"2/10"`).
final TextStyle markerCounterPrimaryTextStyle;

/// Text style for the secondary (total value) part of a counter marker text.
///
/// Applied to the `/` separator and the portion after it (e.g. `"/10"` in `"2/10"`).
final TextStyle markerCounterSecondaryTextStyle;
}
Loading