Compare commits
2 Commits
3c99159bd1
...
c655cdd31e
| Author | SHA1 | Date | |
|---|---|---|---|
| c655cdd31e | |||
| bb86c6ac20 |
@@ -86,6 +86,8 @@ HTTP (ISIN) → ReportController → ReportOrchestrator
|
|||||||
| Namespace conflict `CertReports.Syncfusion.Pdf` | Aggiungere `using Syncfusion.Pdf;` esplicito nel file |
|
| Namespace conflict `CertReports.Syncfusion.Pdf` | Aggiungere `using Syncfusion.Pdf;` esplicito nel file |
|
||||||
| SkiaSharp `SKFont` NON è IDisposable | Non usare `using` su `SKFont` — solo `SKPaint`, `SKPath`, `SKSurface`, `SKImage`, `SKData` sono IDisposable |
|
| SkiaSharp `SKFont` NON è IDisposable | Non usare `using` su `SKFont` — solo `SKPaint`, `SKPath`, `SKSurface`, `SKImage`, `SKData` sono IDisposable |
|
||||||
| `SqlDataReader.GetDecimal` su colonna `float` SQL | Usare `Convert.ToDecimal(r.GetValue(ord))` — le SP cedlab_ possono restituire `float` (es. `PriceWorst` da subquery su `Prices.Px_close`) |
|
| `SqlDataReader.GetDecimal` su colonna `float` SQL | Usare `Convert.ToDecimal(r.GetValue(ord))` — le SP cedlab_ possono restituire `float` (es. `PriceWorst` da subquery su `Prices.Px_close`) |
|
||||||
|
| `SqlDataReader.GetString` su colonna nullable | Usare `ToStr(r, column)` (helper in `ChartDataServiceV2`) che fa `r.IsDBNull` prima — `GetString` su `DBNull` lancia `SqlNullValueException` |
|
||||||
|
| `cedlab_Chart_UL1`: Divide by zero su `Strike = 0` | La SP divide per `cu.Strike` senza guard nel calcolo `PriceWorstPerc`. Fix applicato nel DB: `/ NULLIF(cu.Strike, 0)`. Colpisce certificati con Strike non impostato in `CertificatesUnderlyings` |
|
||||||
|
|
||||||
## Grafico V2 (`/api/chart/v2/{isin}`)
|
## Grafico V2 (`/api/chart/v2/{isin}`)
|
||||||
|
|
||||||
@@ -126,6 +128,8 @@ Percorsi configurati in `appsettings.json` → `ChartSettings:SavePath` e `Chart
|
|||||||
|
|
||||||
**Note renderer**: CTF non mostrato se `NumPrezziCFT < 30` (mostra solo avviso testo); asse X usa intervalli mensili adattivi (12m/6m/3m/1m in base al range).
|
**Note renderer**: CTF non mostrato se `NumPrezziCFT < 30` (mostra solo avviso testo); asse X usa intervalli mensili adattivi (12m/6m/3m/1m in base al range).
|
||||||
|
|
||||||
|
**Label destra — collision avoidance**: i label sul margine destro (barriere, strike, autocall, end-label serie) vengono raccolti in una lista `rightLabels`, ordinati per Y, e disegnati da `DrawRightLabels` con spacing minimo 13px. Due passate: discendente (push-down) + risalente (clamp al bordo area). Questo evita la sovrapposizione quando una serie si accavalla con una linea costante.
|
||||||
|
|
||||||
## Database
|
## Database
|
||||||
|
|
||||||
Tutte le stored procedure sono su `FirstSolutionDB`. I dati tornano **già formattati come stringhe** dalle SP (es. `FORMAT(value,'P2','it-IT')`): i modelli C# usano `string` per questi campi. Solo `NominalValue`, `PrezzoEmissione`, `CpnPagati`, `CpnDaPagare`, `CpnInMemoria` sono `decimal?` perché servono come valori numerici nel rendering.
|
Tutte le stored procedure sono su `FirstSolutionDB`. I dati tornano **già formattati come stringhe** dalle SP (es. `FORMAT(value,'P2','it-IT')`): i modelli C# usano `string` per questi campi. Solo `NominalValue`, `PrezzoEmissione`, `CpnPagati`, `CpnDaPagare`, `CpnInMemoria` sono `decimal?` perché servono come valori numerici nel rendering.
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ public class CertificateInfo
|
|||||||
public string Leva { get; set; } = string.Empty;
|
public string Leva { get; set; } = string.Empty;
|
||||||
public string FattoreAirbag { get; set; } = string.Empty;
|
public string FattoreAirbag { get; set; } = string.Empty;
|
||||||
public string TriggerOneStar { get; set; } = string.Empty;
|
public string TriggerOneStar { get; set; } = string.Empty;
|
||||||
|
public string RendimentoPotenziale { get; set; } = string.Empty;
|
||||||
|
public int CategoryID { get; set; }
|
||||||
public string Note { get; set; } = string.Empty;
|
public string Note { get; set; } = string.Empty;
|
||||||
|
|
||||||
// Stato quotazione (da SP rpt_Master_CFT_ISIN)
|
// Stato quotazione (da SP rpt_Master_CFT_ISIN)
|
||||||
|
|||||||
@@ -325,21 +325,23 @@ public class AnagraficaSectionRenderer : IPdfSectionRenderer
|
|||||||
("Barriera Capitale", info.LivelloBarriera ?? "-"),
|
("Barriera Capitale", info.LivelloBarriera ?? "-"),
|
||||||
("Tipo Barriera", info.BarrierType ?? "-"),
|
("Tipo Barriera", info.BarrierType ?? "-"),
|
||||||
("Tipo Basket", info.BasketType ?? "-"),
|
("Tipo Basket", info.BasketType ?? "-"),
|
||||||
|
("Direzione", string.IsNullOrWhiteSpace(info.Direzione) ? "-" : info.Direzione),
|
||||||
("Leva", string.IsNullOrWhiteSpace(info.Leva) ? "—" : info.Leva),
|
("Leva", string.IsNullOrWhiteSpace(info.Leva) ? "—" : info.Leva),
|
||||||
("Memoria", string.IsNullOrWhiteSpace(info.Memory) ? "—" : info.Memory),
|
("Memoria", string.IsNullOrWhiteSpace(info.Memory) ? "—" : info.Memory),
|
||||||
};
|
};
|
||||||
|
|
||||||
var rightItems = new (string Label, string Value)[]
|
var rightItems = new (string Label, string Value)[]
|
||||||
{
|
{
|
||||||
("Rend. Capitale a Scadenza", info.CapitalReturnAtMaturity ?? "-"),
|
("Rend. Capitale a Scadenza", (info.CategoryID == 1 || info.CategoryID == 4) ? "" : (info.CapitalReturnAtMaturity ?? "-")),
|
||||||
("Tasso Rendimento Interno", info.IRR ?? "-"),
|
("Rend. Potenziale a Scadenza", string.IsNullOrWhiteSpace(info.RendimentoPotenziale) ? "-" : info.RendimentoPotenziale),
|
||||||
("Protezione Capitale", info.BufferKProt ?? "-"),
|
("Tasso Rendimento Interno", info.IRR ?? "-"),
|
||||||
("Protezione Cedola", info.BufferCPNProt ?? "-"),
|
("Protezione Capitale", info.BufferKProt ?? "-"),
|
||||||
("Valore Richiamo Anticipato", info.AutocallValue ?? "-"),
|
("Protezione Cedola", info.BufferCPNProt ?? "-"),
|
||||||
("Distanza Richiamo Anticipato", info.TriggerAutocallDistance ?? "-"),
|
("Valore Richiamo Anticipato", info.AutocallValue ?? "-"),
|
||||||
|
("Distanza Richiamo Anticipato", info.TriggerAutocallDistance ?? "-"),
|
||||||
("Rendimento Richiamo Anticipato", info.AutocallReturn ?? "-"),
|
("Rendimento Richiamo Anticipato", info.AutocallReturn ?? "-"),
|
||||||
("Fattore Airbag", string.IsNullOrWhiteSpace(info.FattoreAirbag) ? "—" : info.FattoreAirbag),
|
("Fattore Airbag", string.IsNullOrWhiteSpace(info.FattoreAirbag) ? "—" : info.FattoreAirbag),
|
||||||
("Livello OneStar", string.IsNullOrWhiteSpace(info.TriggerOneStar) ? "—" : info.TriggerOneStar),
|
("Livello OneStar", string.IsNullOrWhiteSpace(info.TriggerOneStar) ? "—" : info.TriggerOneStar),
|
||||||
};
|
};
|
||||||
|
|
||||||
float leftY = DrawKVList(g, leftItems, 0, ColW, y);
|
float leftY = DrawKVList(g, leftItems, 0, ColW, y);
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ public class CertificateDataService : ICertificateDataService
|
|||||||
info.Leva = r.GetStringSafe("Leva");
|
info.Leva = r.GetStringSafe("Leva");
|
||||||
info.FattoreAirbag = r.GetStringSafe("FattoreAirbag");
|
info.FattoreAirbag = r.GetStringSafe("FattoreAirbag");
|
||||||
info.TriggerOneStar = r.GetStringSafe("TriggerOneStar");
|
info.TriggerOneStar = r.GetStringSafe("TriggerOneStar");
|
||||||
|
info.RendimentoPotenziale = r.GetStringSafe("RendimentoPotenziale");
|
||||||
|
info.CategoryID = r.GetSafe<int>("CategoryID");
|
||||||
info.PrezzoEmissione = r.GetNullableDecimal("PrezzoEmissione");
|
info.PrezzoEmissione = r.GetNullableDecimal("PrezzoEmissione");
|
||||||
info.CpnInMemoria = r.GetNullableDecimal("CpnInMemoria");
|
info.CpnInMemoria = r.GetNullableDecimal("CpnInMemoria");
|
||||||
info.CpnDaPagare = r.GetNullableDecimal("CpnDaPagare");
|
info.CpnDaPagare = r.GetNullableDecimal("CpnDaPagare");
|
||||||
|
|||||||
@@ -60,12 +60,12 @@ public class ChartDataServiceV2 : IChartDataServiceV2
|
|||||||
BarrieraCoupon = ToDecimal(r, "BarrieraCoupon"),
|
BarrieraCoupon = ToDecimal(r, "BarrieraCoupon"),
|
||||||
BarrieraCapitalePerc = ToDecimal(r, "BarrieraCapitalePerc"),
|
BarrieraCapitalePerc = ToDecimal(r, "BarrieraCapitalePerc"),
|
||||||
BarrieraCapitale = ToDecimal(r, "BarrieraCapitale"),
|
BarrieraCapitale = ToDecimal(r, "BarrieraCapitale"),
|
||||||
Sottostante = r.GetString(r.GetOrdinal("Sottostante")),
|
Sottostante = ToStr(r, "Sottostante"),
|
||||||
IsWorstOf = r.GetInt32(r.GetOrdinal("IsWorstOf")),
|
IsWorstOf = r.GetInt32(r.GetOrdinal("IsWorstOf")),
|
||||||
PriceWorst = ToDecimal(r, "PriceWorst"),
|
PriceWorst = ToDecimal(r, "PriceWorst"),
|
||||||
PriceWorstPerc = ToDecimal(r, "PriceWorstPerc"),
|
PriceWorstPerc = ToDecimal(r, "PriceWorstPerc"),
|
||||||
NumPrezziCFT = r.GetInt32(r.GetOrdinal("NumPrezziCFT")),
|
NumPrezziCFT = r.GetInt32(r.GetOrdinal("NumPrezziCFT")),
|
||||||
NomeCFT = r.GetString(r.GetOrdinal("NomeCFT")),
|
NomeCFT = ToStr(r, "NomeCFT"),
|
||||||
TriggerAutocallPerc = ToDecimal(r, "TriggerAutocallPerc"),
|
TriggerAutocallPerc = ToDecimal(r, "TriggerAutocallPerc"),
|
||||||
AutocallValue = ToDecimal(r, "AutocallValue"),
|
AutocallValue = ToDecimal(r, "AutocallValue"),
|
||||||
});
|
});
|
||||||
@@ -134,4 +134,10 @@ public class ChartDataServiceV2 : IChartDataServiceV2
|
|||||||
if (r.IsDBNull(ord)) return 0m;
|
if (r.IsDBNull(ord)) return 0m;
|
||||||
return Convert.ToDecimal(r.GetValue(ord));
|
return Convert.ToDecimal(r.GetValue(ord));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string ToStr(SqlDataReader r, string column)
|
||||||
|
{
|
||||||
|
int ord = r.GetOrdinal(column);
|
||||||
|
return r.IsDBNull(ord) ? string.Empty : r.GetString(ord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,12 +79,13 @@ public static class SkiaChartRendererV2
|
|||||||
|
|
||||||
// ── Linee costanti con label ───────────────────────────────────
|
// ── Linee costanti con label ───────────────────────────────────
|
||||||
var constLegend = new List<(string name, SKColor color, bool dashed, float thickness)>();
|
var constLegend = new List<(string name, SKColor color, bool dashed, float thickness)>();
|
||||||
|
var rightLabels = new List<(float Y, string Text, SKColor Color)>();
|
||||||
|
|
||||||
string bcLabel = data.GlobalMeta.BarrieraCouponPerc == data.GlobalMeta.BarrieraCapitalePerc
|
string bcLabel = data.GlobalMeta.BarrieraCouponPerc == data.GlobalMeta.BarrieraCapitalePerc
|
||||||
? $"Barriera {data.GlobalMeta.BarrieraCapitalePerc:0}% ({data.GlobalMeta.BarrieraCapitale:0.00})"
|
? $"Barriera {data.GlobalMeta.BarrieraCapitalePerc:0}% ({data.GlobalMeta.BarrieraCapitale:0.00})"
|
||||||
: $"Barriera Capitale {data.GlobalMeta.BarrieraCapitalePerc:0}% ({data.GlobalMeta.BarrieraCapitale:0.00})";
|
: $"Barriera Capitale {data.GlobalMeta.BarrieraCapitalePerc:0}% ({data.GlobalMeta.BarrieraCapitale:0.00})";
|
||||||
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
||||||
(float)data.GlobalMeta.BarrieraCapitalePerc, CapitaleColor, 1.5f, false, bcLabel);
|
(float)data.GlobalMeta.BarrieraCapitalePerc, CapitaleColor, 1.5f, false, bcLabel, rightLabels);
|
||||||
constLegend.Add((bcLabel, CapitaleColor, false, 1.5f));
|
constLegend.Add((bcLabel, CapitaleColor, false, 1.5f));
|
||||||
|
|
||||||
if (data.GlobalMeta.BarrieraCouponPerc != data.GlobalMeta.BarrieraCapitalePerc
|
if (data.GlobalMeta.BarrieraCouponPerc != data.GlobalMeta.BarrieraCapitalePerc
|
||||||
@@ -92,13 +93,13 @@ public static class SkiaChartRendererV2
|
|||||||
{
|
{
|
||||||
string bkLabel = $"Barriera Coupon {data.GlobalMeta.BarrieraCouponPerc:0}% ({data.GlobalMeta.BarrieraCoupon:0.00})";
|
string bkLabel = $"Barriera Coupon {data.GlobalMeta.BarrieraCouponPerc:0}% ({data.GlobalMeta.BarrieraCoupon:0.00})";
|
||||||
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
||||||
(float)data.GlobalMeta.BarrieraCouponPerc, CouponColor, 1.5f, false, bkLabel);
|
(float)data.GlobalMeta.BarrieraCouponPerc, CouponColor, 1.5f, false, bkLabel, rightLabels);
|
||||||
constLegend.Add((bkLabel, CouponColor, false, 1.5f));
|
constLegend.Add((bkLabel, CouponColor, false, 1.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
string strikeLabel = $"Strike 100% ({data.GlobalMeta.Strike:0.00})";
|
string strikeLabel = $"Strike 100% ({data.GlobalMeta.Strike:0.00})";
|
||||||
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
||||||
100f, StrikeColor, 1.5f, false, strikeLabel);
|
100f, StrikeColor, 1.5f, false, strikeLabel, rightLabels);
|
||||||
constLegend.Add((strikeLabel, StrikeColor, false, 1.5f));
|
constLegend.Add((strikeLabel, StrikeColor, false, 1.5f));
|
||||||
|
|
||||||
bool showAutocall = data.GlobalMeta.TriggerAutocallPerc != 0
|
bool showAutocall = data.GlobalMeta.TriggerAutocallPerc != 0
|
||||||
@@ -109,7 +110,7 @@ public static class SkiaChartRendererV2
|
|||||||
{
|
{
|
||||||
string taLabel = $"Trigger Autocall {data.GlobalMeta.TriggerAutocallPerc:0}% ({data.GlobalMeta.AutocallValue:0.00})";
|
string taLabel = $"Trigger Autocall {data.GlobalMeta.TriggerAutocallPerc:0}% ({data.GlobalMeta.AutocallValue:0.00})";
|
||||||
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
||||||
(float)data.GlobalMeta.TriggerAutocallPerc, AutocallColor, 1.5f, false, taLabel);
|
(float)data.GlobalMeta.TriggerAutocallPerc, AutocallColor, 1.5f, false, taLabel, rightLabels);
|
||||||
constLegend.Add((taLabel, AutocallColor, false, 1.5f));
|
constLegend.Add((taLabel, AutocallColor, false, 1.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +119,7 @@ public static class SkiaChartRendererV2
|
|||||||
{
|
{
|
||||||
string pwLabel = $"{worstOf.Sottostante} ({worstOf.PriceWorst:0.00})";
|
string pwLabel = $"{worstOf.Sottostante} ({worstOf.PriceWorst:0.00})";
|
||||||
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
DrawHorizontalLineWithLabel(canvas, plotArea, minY, maxY,
|
||||||
(float)worstOf.PriceWorstPerc, PrezzoWorstColor, 1f, true, pwLabel);
|
(float)worstOf.PriceWorstPerc, PrezzoWorstColor, 1f, true, pwLabel, rightLabels);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Serie ──────────────────────────────────────────────────────
|
// ── Serie ──────────────────────────────────────────────────────
|
||||||
@@ -159,10 +160,12 @@ public static class SkiaChartRendererV2
|
|||||||
}
|
}
|
||||||
|
|
||||||
DrawSeriesV2(canvas, plotArea, ulPoints, minDate, maxDate, minY, maxY, color, thickness);
|
DrawSeriesV2(canvas, plotArea, ulPoints, minDate, maxDate, minY, maxY, color, thickness);
|
||||||
DrawSeriesEndLabel(canvas, plotArea, ulPoints, minDate, maxDate, minY, maxY, color, endLabel);
|
DrawSeriesEndLabel(plotArea, ulPoints, minDate, maxDate, minY, maxY, color, endLabel, rightLabels);
|
||||||
seriesLegend.Add((seriesLabel, color, false, thickness));
|
seriesLegend.Add((seriesLabel, color, false, thickness));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawRightLabels(canvas, plotArea, rightLabels);
|
||||||
|
|
||||||
// Bordo area plot
|
// Bordo area plot
|
||||||
using var borderPaint = new SKPaint
|
using var borderPaint = new SKPaint
|
||||||
{
|
{
|
||||||
@@ -372,19 +375,17 @@ public static class SkiaChartRendererV2
|
|||||||
// Label al termine di una serie (estremo destro)
|
// Label al termine di una serie (estremo destro)
|
||||||
// ═══════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
private static void DrawSeriesEndLabel(SKCanvas canvas, SKRect area,
|
private static void DrawSeriesEndLabel(SKRect area,
|
||||||
List<ChartSeriesPoint> points,
|
List<ChartSeriesPoint> points,
|
||||||
DateTime minDate, DateTime maxDate, double minY, double maxY,
|
DateTime minDate, DateTime maxDate, double minY, double maxY,
|
||||||
SKColor color, string label)
|
SKColor color, string label,
|
||||||
|
List<(float Y, string Text, SKColor Color)> rightLabels)
|
||||||
{
|
{
|
||||||
if (points.Count == 0) return;
|
if (points.Count == 0) return;
|
||||||
var last = points.Last();
|
var last = points.Last();
|
||||||
float y = ValueToY((double)last.Performance, area, minY, maxY);
|
float y = ValueToY((double)last.Performance, area, minY, maxY);
|
||||||
y = Math.Max(area.Top, Math.Min(area.Bottom, y));
|
y = Math.Max(area.Top, Math.Min(area.Bottom, y));
|
||||||
|
rightLabels.Add((y + 4, label, color));
|
||||||
var font = CreateFont(9.5f);
|
|
||||||
using var textPaint = new SKPaint { Color = color, IsAntialias = true };
|
|
||||||
canvas.DrawText(label, area.Right + 5, y + 4, SKTextAlign.Left, font, textPaint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════
|
||||||
@@ -393,7 +394,8 @@ public static class SkiaChartRendererV2
|
|||||||
|
|
||||||
private static void DrawHorizontalLineWithLabel(SKCanvas canvas, SKRect area,
|
private static void DrawHorizontalLineWithLabel(SKCanvas canvas, SKRect area,
|
||||||
double minY, double maxY,
|
double minY, double maxY,
|
||||||
float value, SKColor color, float thickness, bool dashed, string label)
|
float value, SKColor color, float thickness, bool dashed, string label,
|
||||||
|
List<(float Y, string Text, SKColor Color)> rightLabels)
|
||||||
{
|
{
|
||||||
float y = ValueToY(value, area, minY, maxY);
|
float y = ValueToY(value, area, minY, maxY);
|
||||||
if (y < area.Top || y > area.Bottom) return;
|
if (y < area.Top || y > area.Bottom) return;
|
||||||
@@ -407,10 +409,42 @@ public static class SkiaChartRendererV2
|
|||||||
linePaint.PathEffect = SKPathEffect.CreateDash(new[] { 8f, 4f }, 0);
|
linePaint.PathEffect = SKPathEffect.CreateDash(new[] { 8f, 4f }, 0);
|
||||||
|
|
||||||
canvas.DrawLine(area.Left, y, area.Right, y, linePaint);
|
canvas.DrawLine(area.Left, y, area.Right, y, linePaint);
|
||||||
|
rightLabels.Add((y + 4, label, color));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════
|
||||||
|
// Label destra — collision avoidance
|
||||||
|
// ═══════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
private static void DrawRightLabels(SKCanvas canvas, SKRect area,
|
||||||
|
List<(float Y, string Text, SKColor Color)> labels)
|
||||||
|
{
|
||||||
|
if (labels.Count == 0) return;
|
||||||
|
|
||||||
|
const float minSpacing = 13f;
|
||||||
|
|
||||||
|
var items = labels.OrderBy(l => l.Y).ToList();
|
||||||
|
float[] ys = items.Select(l => l.Y).ToArray();
|
||||||
|
|
||||||
|
// Passata discendente: sposta in basso le label troppo vicine
|
||||||
|
for (int i = 1; i < ys.Length; i++)
|
||||||
|
if (ys[i] < ys[i - 1] + minSpacing)
|
||||||
|
ys[i] = ys[i - 1] + minSpacing;
|
||||||
|
|
||||||
|
// Passata risalente: se qualcuna è finita sotto il bordo, riporta su
|
||||||
|
float maxAllowed = area.Bottom + 4f;
|
||||||
|
for (int i = ys.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (ys[i] > maxAllowed) ys[i] = maxAllowed;
|
||||||
|
maxAllowed = ys[i] - minSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
var font = CreateFont(9.5f);
|
var font = CreateFont(9.5f);
|
||||||
using var textPaint = new SKPaint { Color = color, IsAntialias = true };
|
for (int i = 0; i < items.Count; i++)
|
||||||
canvas.DrawText(label, area.Right + 5, y + 4, SKTextAlign.Left, font, textPaint);
|
{
|
||||||
|
using var textPaint = new SKPaint { Color = items[i].Color, IsAntialias = true };
|
||||||
|
canvas.DrawText(items[i].Text, area.Right + 5, ys[i], SKTextAlign.Left, font, textPaint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user