Units renamed & Chart refactored
This commit is contained in:
+53
-82
@@ -18,7 +18,7 @@ uses
|
||||
Myc.Trade.Types,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Signals,
|
||||
Myc.Lazy;
|
||||
Myc.Mutable;
|
||||
|
||||
type
|
||||
TMycChart = class(TStyledControl)
|
||||
@@ -59,18 +59,14 @@ type
|
||||
constructor Create(AParent: TPanel);
|
||||
end;
|
||||
|
||||
TAxisLayer = class(TLayer)
|
||||
TAxisLayer = class abstract(TLayer)
|
||||
private
|
||||
FOwner: TMycChart;
|
||||
protected
|
||||
function GetOwner: TMycChart; override; final;
|
||||
// Paint crosshair and other axis related overlays
|
||||
procedure Paint(
|
||||
const Canvas: TCanvas;
|
||||
const Viewport: TRectF;
|
||||
ViewStartIndex, ViewCount: Int64;
|
||||
const MousePos: TPointF
|
||||
); virtual;
|
||||
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; ViewStartIndex, ViewCount: Int64; IdxAtMousePos: Integer); virtual;
|
||||
abstract;
|
||||
public
|
||||
constructor Create(AOwner: TMycChart);
|
||||
end;
|
||||
@@ -78,12 +74,7 @@ type
|
||||
TXAxisLayer = class(TAxisLayer)
|
||||
protected
|
||||
// Paint crosshair and time caption
|
||||
procedure Paint(
|
||||
const Canvas: TCanvas;
|
||||
const Viewport: TRectF;
|
||||
ViewStartIndex, ViewCount: Int64;
|
||||
const MousePos: TPointF
|
||||
); override;
|
||||
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; ViewStartIndex, ViewCount: Int64; IdxAtMousePos: Integer); override;
|
||||
// This function delivers the text for the caption.
|
||||
function GetCaption(Idx: Int64): String; virtual; abstract;
|
||||
end;
|
||||
@@ -119,6 +110,11 @@ type
|
||||
property Weight: Single read FWeight write SetWeight;
|
||||
end;
|
||||
|
||||
TDragPoint = record
|
||||
Point: TPointF;
|
||||
Idx: Int64;
|
||||
end;
|
||||
|
||||
private
|
||||
FPanelList: TObjectList<TPanel>;
|
||||
FXAxisSeries: TMycChart.TXAxisLayer;
|
||||
@@ -127,8 +123,8 @@ type
|
||||
FViewStartIndex: Int64;
|
||||
FViewCount: Int64;
|
||||
FIsDragging: Boolean;
|
||||
FDragStartPoint: TPointF;
|
||||
FMousePos: TPointF;
|
||||
FMousePos: TDragPoint;
|
||||
FDragStartPoint: TDragPoint;
|
||||
FIsMouseInControl: Boolean;
|
||||
FJumpButtonRect: TRectF;
|
||||
FJumpButtonHot: Boolean;
|
||||
@@ -139,6 +135,7 @@ type
|
||||
FHotResizePanelIndex: Integer;
|
||||
function GetPanel(Index: Integer): TPanel;
|
||||
function GetPanelCount: Integer;
|
||||
function CreateDragPoint(X, Y: Single): TDragPoint;
|
||||
protected
|
||||
procedure Paint; override;
|
||||
procedure DoIdle;
|
||||
@@ -221,6 +218,15 @@ begin
|
||||
Repaint;
|
||||
end;
|
||||
|
||||
function TMycChart.CreateDragPoint(X, Y: Single): TDragPoint;
|
||||
begin
|
||||
Result.Point.X := X;
|
||||
Result.Point.Y := Y;
|
||||
Result.Idx := Round((LocalRect.Right - X) * (FViewCount - 1) / LocalRect.Width);
|
||||
if (Result.Idx < 0) or (Result.Idx >= FXAxisSeries.Series.Count) then
|
||||
Result.Idx := -1;
|
||||
end;
|
||||
|
||||
function TMycChart.GetPanel(Index: Integer): TPanel;
|
||||
begin
|
||||
Result := FPanelList[Index];
|
||||
@@ -333,8 +339,12 @@ begin
|
||||
end;
|
||||
|
||||
// Draw crosshair if mouse is in control
|
||||
if FIsMouseInControl and Assigned(FXAxisSeries) and not FIsDragging and not FIsResizing then
|
||||
FXAxisSeries.Paint(Self.Canvas, rect, FViewStartIndex, FViewCount, FMousePos);
|
||||
if FIsMouseInControl and Assigned(FXAxisSeries) and (FMousePos.Idx >= 0 ) then
|
||||
begin
|
||||
// Do not draw crosshair if mouse is over the jump button
|
||||
if not FJumpButtonRect.Contains(FMousePos.Point) then
|
||||
FXAxisSeries.Paint(Self.Canvas, rect, FViewStartIndex, FViewCount, FViewStartIndex + FMousePos.Idx );
|
||||
end;
|
||||
|
||||
// --- Draw the "jump to latest" button (code unchanged) ---
|
||||
isButtonVisible := (FViewStartIndex > 0);
|
||||
@@ -380,6 +390,8 @@ end;
|
||||
|
||||
procedure TMycChart.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
||||
begin
|
||||
inherited MouseDown(Button, Shift, X, Y);
|
||||
|
||||
// Check for button press first
|
||||
if (Button = TMouseButton.mbLeft) and (FViewStartIndex > 0) and FJumpButtonRect.Contains(PointF(X, Y)) then
|
||||
begin
|
||||
@@ -393,16 +405,15 @@ begin
|
||||
begin
|
||||
FIsResizing := true;
|
||||
FResizePanelIndex := FHotResizePanelIndex;
|
||||
FDragStartPoint := TPointF.Create(X, Y);
|
||||
FDragStartPoint := CreateDragPoint(X, Y);
|
||||
Capture;
|
||||
exit;
|
||||
end;
|
||||
|
||||
inherited MouseDown(Button, Shift, X, Y);
|
||||
if (Button = TMouseButton.mbLeft) then
|
||||
begin
|
||||
FIsDragging := true;
|
||||
FDragStartPoint := TPointF.Create(X, Y);
|
||||
FDragStartPoint := CreateDragPoint(X, Y);
|
||||
Capture;
|
||||
end;
|
||||
end;
|
||||
@@ -427,10 +438,7 @@ begin
|
||||
end;
|
||||
|
||||
// Stop panning
|
||||
if FIsDragging then
|
||||
begin
|
||||
FIsDragging := false;
|
||||
end;
|
||||
FIsDragging := false;
|
||||
|
||||
// Stop button press
|
||||
if FJumpButtonPressed then
|
||||
@@ -459,12 +467,14 @@ begin
|
||||
|
||||
if not FIsMouseInControl then
|
||||
FIsMouseInControl := true;
|
||||
FMousePos := TPointF.Create(X, Y);
|
||||
FMousePos := CreateDragPoint(X, Y);
|
||||
|
||||
// --- Panel Resizing Logic ---
|
||||
if FIsResizing then
|
||||
begin
|
||||
dy := Y - FDragStartPoint.Y;
|
||||
dy := Y - FDragStartPoint.Point.Y;
|
||||
if Abs(dx) < 2 then // Threshold to avoid jitter
|
||||
exit;
|
||||
|
||||
var panel1 := Panels[FResizePanelIndex];
|
||||
var panel2 := Panels[FResizePanelIndex + 1];
|
||||
@@ -502,7 +512,7 @@ begin
|
||||
panel2.Weight := (newH2 / (newH1 + newH2)) * twoPanelWeight;
|
||||
end;
|
||||
|
||||
FDragStartPoint.Y := Y; // Update start point for next move
|
||||
FDragStartPoint := CreateDragPoint( FDragStartPoint.Point.X, Y ); // Update start point for next move
|
||||
Repaint;
|
||||
exit; // Do not continue with other mouse move logic
|
||||
end;
|
||||
@@ -510,17 +520,17 @@ begin
|
||||
// --- Chart Panning Logic ---
|
||||
if FIsDragging then
|
||||
begin
|
||||
dx := X - FDragStartPoint.X;
|
||||
dx := X - FDragStartPoint.Point.X;
|
||||
if Abs(dx) < 2 then // Threshold to avoid jitter
|
||||
begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
if not Assigned(FXAxisSeries) or (FViewCount <= 0) then
|
||||
exit;
|
||||
|
||||
indexDelta := Round(dx / (Self.Width / FViewCount));
|
||||
FViewStartIndex := FViewStartIndex + indexDelta;
|
||||
indexDelta := FMousePos.Idx - FDragStartPoint.Idx;
|
||||
FDragStartPoint.Idx := FDragStartPoint.Idx + indexDelta;
|
||||
|
||||
FViewStartIndex := FViewStartIndex - indexDelta;
|
||||
|
||||
// Clamp values
|
||||
maxIndex := FXAxisSeries.Series.Count - FViewCount;
|
||||
@@ -531,8 +541,6 @@ begin
|
||||
if (FViewStartIndex > maxIndex) then
|
||||
FViewStartIndex := maxIndex;
|
||||
|
||||
FDragStartPoint.X := X;
|
||||
FDragStartPoint.Y := Y;
|
||||
Repaint;
|
||||
exit;
|
||||
end;
|
||||
@@ -574,12 +582,11 @@ end;
|
||||
|
||||
procedure TMycChart.MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean);
|
||||
var
|
||||
mousePos: TPointF;
|
||||
anchorIndex: Double;
|
||||
zoomFactor: Double;
|
||||
newViewCount: Int64;
|
||||
ratio: Double;
|
||||
begin
|
||||
inherited MouseWheel(Shift, WheelDelta, Handled);
|
||||
|
||||
Handled := true;
|
||||
|
||||
// An X-axis series is required for zooming
|
||||
@@ -590,11 +597,6 @@ begin
|
||||
if (xAxisCount = 0) or (Width <= 0) then
|
||||
exit;
|
||||
|
||||
mousePos := ScreenToLocal(Screen.MousePos);
|
||||
|
||||
// Calculate the anchor index for a right-to-left axis
|
||||
anchorIndex := FViewStartIndex + ((Self.Width - mousePos.X) / Self.Width) * FViewCount;
|
||||
|
||||
if (WheelDelta > 0) then
|
||||
zoomFactor := 0.8 // Zoom In
|
||||
else
|
||||
@@ -610,15 +612,12 @@ begin
|
||||
if (newViewCount = FViewCount) then
|
||||
exit;
|
||||
|
||||
// Adjust start index to keep anchor point stable
|
||||
if (FViewCount > 0) then
|
||||
ratio := (anchorIndex - FViewStartIndex) / FViewCount
|
||||
else
|
||||
ratio := 0;
|
||||
|
||||
FViewStartIndex := Round(anchorIndex - (ratio * newViewCount));
|
||||
FViewCount := newViewCount;
|
||||
|
||||
var oldIdx := FViewStartIndex + FMousePos.Idx;
|
||||
FMousePos := CreateDragPoint( FMousePos.Point.X, FMousePos.Point.Y );
|
||||
FViewStartIndex := oldIdx - FMousePos.Idx;
|
||||
|
||||
// Clamp start index
|
||||
if (FViewStartIndex < 0) then
|
||||
FViewStartIndex := 0;
|
||||
@@ -818,51 +817,23 @@ begin
|
||||
Result := FOwner;
|
||||
end;
|
||||
|
||||
procedure TMycChart.TAxisLayer.Paint(
|
||||
const Canvas: TCanvas;
|
||||
const Viewport: TRectF;
|
||||
ViewStartIndex, ViewCount: Int64;
|
||||
const MousePos: TPointF
|
||||
);
|
||||
begin
|
||||
// Do nothing in base class
|
||||
end;
|
||||
|
||||
{ TMycChart.TXAxisLayer }
|
||||
|
||||
procedure TMycChart.TXAxisLayer.Paint(
|
||||
const Canvas: TCanvas;
|
||||
const Viewport: TRectF;
|
||||
ViewStartIndex, ViewCount: Int64;
|
||||
const MousePos: TPointF
|
||||
);
|
||||
procedure TMycChart.TXAxisLayer.Paint(const Canvas: TCanvas; const Viewport: TRectF; ViewStartIndex, ViewCount: Int64; IdxAtMousePos:
|
||||
Integer);
|
||||
var
|
||||
idx: Int64;
|
||||
caption: string;
|
||||
captionRect: TRectF;
|
||||
padding: Single;
|
||||
indexFromMouse: Double;
|
||||
candleX: Single;
|
||||
textSize: TRectF;
|
||||
begin
|
||||
// Do not draw crosshair if mouse is over the jump button
|
||||
if Owner.JumpButtonRect.Contains(MousePos) then
|
||||
exit;
|
||||
|
||||
// Do not draw if no data or view is invalid
|
||||
if (not Assigned(Series)) or (Series.Count = 0) or (ViewCount <= 1) then
|
||||
exit;
|
||||
|
||||
// 1. Calculate the data index from the mouse X position
|
||||
indexFromMouse := ViewStartIndex + (Viewport.Right - MousePos.X) * (ViewCount - 1) / Viewport.Width;
|
||||
idx := Round(indexFromMouse);
|
||||
|
||||
// Check if the calculated index is valid for the series
|
||||
if (idx < 0) or (idx >= Series.Count) then
|
||||
exit;
|
||||
|
||||
// Snap X to the candle's center
|
||||
candleX := Viewport.Right - ((idx - ViewStartIndex) * (ViewCount - 1)) / (ViewCount - 1) * (Viewport.Width / (ViewCount - 1));
|
||||
candleX := Viewport.Right - ((IdxAtMousePos - ViewStartIndex) * (ViewCount - 1)) / (ViewCount - 1) * (Viewport.Width / (ViewCount - 1));
|
||||
|
||||
// 2. Draw the vertical line at the snapped position
|
||||
Canvas.Stroke.Kind := TBrushKind.Solid;
|
||||
@@ -871,7 +842,7 @@ begin
|
||||
Canvas.DrawLine(PointF(candleX, Viewport.Top), PointF(candleX, Viewport.Bottom), 1.0);
|
||||
|
||||
// 3. Get the caption for the index
|
||||
caption := GetCaption(idx);
|
||||
caption := GetCaption(IdxAtMousePos);
|
||||
if caption.IsEmpty then
|
||||
exit;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user