diff --git a/AuraTrader/AuraTrader.dpr b/AuraTrader/AuraTrader.dpr index 7cf3b33..5d16e48 100644 --- a/AuraTrader/AuraTrader.dpr +++ b/AuraTrader/AuraTrader.dpr @@ -11,7 +11,6 @@ uses TestModule in 'TestModule.pas', DynamicFMXControl in 'DynamicFMXControl.pas', FirstStrategy in 'FirstStrategy.pas', - Myc.Fmx.Chart in 'Myc.Fmx.Chart.pas', Myc.Trade.DataArray in '..\Src\Myc.Trade.DataArray.pas', Myc.FMX.Chart.Series in '..\Src\Myc.FMX.Chart.Series.pas'; diff --git a/AuraTrader/AuraTrader.dproj b/AuraTrader/AuraTrader.dproj index 236e6a0..eca116a 100644 --- a/AuraTrader/AuraTrader.dproj +++ b/AuraTrader/AuraTrader.dproj @@ -139,7 +139,6 @@ - diff --git a/Src/Myc.Core.Lazy.pas b/Src/Myc.Core.Lazy.pas index aa297bf..89c57c2 100644 --- a/Src/Myc.Core.Lazy.pas +++ b/Src/Myc.Core.Lazy.pas @@ -12,8 +12,11 @@ uses type TMycNullMutable = class(TInterfacedObject, TMutable.IMutable) private + FValue: T; function GetChanged: TSignal; function GetValue: T; + public + constructor Create(AValue: T); end; TMycMutableBase = class(TInterfacedObject, TMutable.IMutable) @@ -50,35 +53,6 @@ type procedure SetValue(const Value: T); end; - TMycNullLazy = class(TInterfacedObject, TLazy.ILazy) - protected - function GetChanged: TState; - public - function Pop(out Res: T): Boolean; - end; - - TMycLazyBase = class(TInterfacedObject, TLazy.ILazy) - strict private - FChanged: TFlag; - FChangeState: TSignal.TSubscription; - function GetChanged: TState; - protected - function GetValue: T; virtual; abstract; - public - constructor Create(const AChanged: TSignal); - destructor Destroy; override; - function Pop(out Res: T): Boolean; - end; - - TMycFuncLazy = class(TMycLazyBase) - private - FProc: TFunc; - protected - function GetValue: T; override; - public - constructor Create(const AChanged: TSignal.ISignal; const AProc: TFunc); - end; - TMycProtectedMutable = class(TInterfacedObject, TMutable.IMutable, TWriteable.IWriteable) private FWriteable: TWriteable.IWriteable; @@ -97,6 +71,12 @@ implementation uses System.Generics.Defaults; +constructor TMycNullMutable.Create(AValue: T); +begin + inherited Create; + FValue := AValue; +end; + { TMycNullMutable } function TMycNullMutable.GetChanged: TSignal; @@ -106,7 +86,7 @@ end; function TMycNullMutable.GetValue: T; begin - Result := Default(T); + Result := FValue; end; { TMycMutableBase } @@ -163,64 +143,6 @@ begin end; end; -{ TMycNullLazy } - -function TMycNullLazy.GetChanged: TState; -begin - Result := TState.Null; -end; - -function TMycNullLazy.Pop(out Res: T): Boolean; -begin - Res := Default(T); - Result := true; -end; - -{ TMycLazyBase } - -constructor TMycLazyBase.Create(const AChanged: TSignal); -begin - inherited Create; - FChanged := TFlag.CreateFlag; - FChanged.Notify; - FChangeState := AChanged.Subscribe(FChanged); -end; - -destructor TMycLazyBase.Destroy; -begin - FChangeState.Unsubscribe; - inherited; -end; - -function TMycLazyBase.GetChanged: TState; -begin - Result := FChanged.State; -end; - -function TMycLazyBase.Pop(out Res: T): Boolean; -begin - Result := FChanged.State.IsSet; - if Result then - begin - Res := GetValue; - FChanged.Reset; - end; -end; - -{ TMycFuncLazy } - -constructor TMycFuncLazy.Create(const AChanged: TSignal.ISignal; const AProc: TFunc); -begin - inherited Create(AChanged); - FProc := AProc; - Assert(Assigned(FProc)); -end; - -function TMycFuncLazy.GetValue: T; -begin - Result := FProc(); -end; - constructor TMycFuncMutable.Create(const AChanged: TSignal; const AProc: TFunc); begin inherited Create(AChanged); diff --git a/Src/Myc.Fmx.Chart.Series.pas b/Src/Myc.Fmx.Chart.Series.pas index 23cf722..88fb61e 100644 --- a/Src/Myc.Fmx.Chart.Series.pas +++ b/Src/Myc.Fmx.Chart.Series.pas @@ -7,9 +7,10 @@ uses System.SyncObjs, System.UITypes, FMX.Graphics, + Myc.Signals, + Myc.Lazy, Myc.Trade.DataArray, Myc.Trade.DataPoint, - Myc.Lazy, Myc.Fmx.Chart; type @@ -25,13 +26,13 @@ type TChartSeriesReceiver = class(TMycProcessor) strict private FCurrData: TMycDataArray; - FLookback: TWriteable; + FLookback: TMutable; private FData: TWriteable>; protected function ProcessData(const Value: T): Boolean; override; public - constructor Create(const ALookback: TWriteable); + constructor Create(const ALookback: TMutable); property Data: TWriteable> read FData; end; @@ -47,28 +48,46 @@ type protected function GetCount: Int64; override; function GetTotalCount: Int64; override; - procedure Update; override; + procedure Update; public - constructor Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider); + constructor Create(const ADataProvider: IMycDataProvider; const ALookback: TMutable); destructor Destroy; override; property Data: TMycDataArray read FData; end; - { TChartOhlcSeries } - TChartOhlcSeries = class(TChartSeriesProcessor) + { TChartCustomLayer } + TChartCustomLayer = class(TMycChart.TLayer) private - FUpColor: TAlphaColor; - FDownColor: TAlphaColor; + FSeries: TChartSeriesProcessor; + protected + function GetSeries: TMycChart.TSeries; override; + function GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; override; + procedure Update; override; + procedure Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); override; + public + constructor Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider); + destructor Destroy; override; + end; + + { TChartOhlcLayer } + TChartOhlcLayer = class(TChartCustomLayer) + private + FUpColor: TMutable; + FDownColor: TMutable; protected function GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; override; procedure Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); override; public - constructor Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider; const AUpColor, ADownColor: TAlphaColor); + constructor Create( + AOwner: TMycChart; + const ADataProvider: IMycDataProvider; + const AUpColor, ADownColor: TMutable + ); end; - { TChartLineSeries } - TChartLineSeries = class(TChartSeriesProcessor) + { TChartLineLayer } + TChartLineLayer = class(TChartCustomLayer) private FLineColor: TAlphaColor; FLineWidth: Single; @@ -106,7 +125,7 @@ end; { TChartSeriesReceiver } -constructor TChartSeriesReceiver.Create(const ALookback: TWriteable); +constructor TChartSeriesReceiver.Create(const ALookback: TMutable); begin inherited Create; FLookback := ALookback; @@ -123,14 +142,14 @@ end; { TChartSeriesProcessor } -constructor TChartSeriesProcessor.Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider); +constructor TChartSeriesProcessor.Create(const ADataProvider: IMycDataProvider; const ALookback: TMutable); begin - inherited Create(AOwner); + inherited Create; + FDataProvider := ADataProvider; FLock := TSpinLock.Create(false); FDataSeries := TMycDataArray.CreateEmpty; FData := FDataSeries; - FDataProvider := ADataProvider; - FReceiver := TChartSeriesReceiver.Create(AOwner.Lookback); + FReceiver := TChartSeriesReceiver.Create(ALookback); FReceiverTag := FDataProvider.Link(FReceiver); end; @@ -155,9 +174,9 @@ begin FData := FReceiver.Data.Value; end; -{ TChartLineSeries } +{ TChartLineLayer } -constructor TChartLineSeries.Create( +constructor TChartLineLayer.Create( AOwner: TMycChart; const ADataProvider: IMycDataProvider; const ALineColor: TAlphaColor; @@ -169,7 +188,7 @@ begin FLineWidth := ALineWidth; end; -function TChartLineSeries.GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; +function TChartLineLayer.GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; var i: Int64; begin @@ -177,7 +196,7 @@ begin MaxValue := -MaxDouble; for i := First to Last do begin - var v := Data.Items[i]; + var v := FSeries.Data.Items[i]; if not IsNaN(v) then begin MinValue := Min(MinValue, v); @@ -187,7 +206,7 @@ begin Result := (MinValue <> MaxDouble); end; -procedure TChartLineSeries.Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); +procedure TChartLineLayer.Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); var points: TPathData; i, n: Int64; @@ -196,15 +215,15 @@ begin try // Skip warmup data n := First; - while (n <= Last) and (IsNaN(Data[n])) do + while (n <= Last) and (IsNaN(FSeries.Data[n])) do inc(n); if (n > Last) then exit; - points.MoveTo(TPointF.Create(XForm(n), YForm(Data[n]))); + points.MoveTo(TPointF.Create(XForm(n), YForm(FSeries.Data[n]))); for i := n + 1 to Last do - if not IsNaN(Data[i]) then - points.LineTo(TPointF.Create(XForm(i), YForm(Data[i]))); + if not IsNaN(FSeries.Data[i]) then + points.LineTo(TPointF.Create(XForm(i), YForm(FSeries.Data[i]))); Canvas.Stroke.Color := FLineColor; Canvas.Stroke.Thickness := FLineWidth; @@ -214,20 +233,23 @@ begin end; end; -{ TChartOhlcSeries } +{ TChartOhlcLayer } -constructor TChartOhlcSeries.Create( +constructor TChartOhlcLayer.Create( AOwner: TMycChart; const ADataProvider: IMycDataProvider; - const AUpColor, ADownColor: TAlphaColor + const AUpColor, ADownColor: TMutable ); begin inherited Create(AOwner, ADataProvider); FUpColor := AUpColor; FDownColor := ADownColor; + + FUpColor.Changed.Subscribe(Owner.NeedRepaint); + FDownColor.Changed.Subscribe(Owner.NeedRepaint); end; -function TChartOhlcSeries.GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; +function TChartOhlcLayer.GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; var i: Int64; begin @@ -236,14 +258,14 @@ begin for i := First to Last do begin - var item := Data.Items[i]; + var item := FSeries.Data.Items[i]; MinValue := Min(MinValue, item.Low); MaxValue := Max(MaxValue, item.High); end; Result := (MinValue <> MaxDouble); end; -procedure TChartOhlcSeries.Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); +procedure TChartOhlcLayer.Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); var i: Int64; x, candleWidth: Single; @@ -255,7 +277,7 @@ begin for i := First to Last do begin - item := Data.Items[i]; + item := FSeries.Data.Items[i]; x := XForm(i); yOpen := YForm(item.Open); yClose := YForm(item.Close); @@ -264,9 +286,9 @@ begin isUp := item.Close >= item.Open; if isUp then - Canvas.Stroke.Color := FUpColor + Canvas.Stroke.Color := FUpColor.Value else - Canvas.Stroke.Color := FDownColor; + Canvas.Stroke.Color := FDownColor.Value; Canvas.Stroke.Thickness := 1.0; Canvas.DrawLine(TPointF.Create(x, yHigh), TPointF.Create(x, yLow), 1); @@ -279,4 +301,38 @@ begin end; end; +{ TChartCustomLayer } + +constructor TChartCustomLayer.Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider); +begin + inherited Create(AOwner); + FSeries := TChartSeriesProcessor.Create(ADataProvider, AOwner.Lookback.AsMutable); +end; + +destructor TChartCustomLayer.Destroy; +begin + FSeries.Free; + inherited; +end; + +function TChartCustomLayer.GetSeries: TMycChart.TSeries; +begin + Result := FSeries; +end; + +function TChartCustomLayer.GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; +begin + Result := false; +end; + +procedure TChartCustomLayer.Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); +begin + // nope +end; + +procedure TChartCustomLayer.Update; +begin + FSeries.Update; +end; + end. diff --git a/Src/Myc.Fmx.Chart.pas b/Src/Myc.Fmx.Chart.pas index 83d041b..f286969 100644 --- a/Src/Myc.Fmx.Chart.pas +++ b/Src/Myc.Fmx.Chart.pas @@ -16,6 +16,7 @@ uses FMX.Graphics, FMX.Forms, Myc.Trade.DataPoint, + Myc.Signals, Myc.Lazy; type @@ -23,27 +24,33 @@ type public type TSeries = class abstract(TObject) - private - FOwner: TMycChart; - function GetMainSeries: TSeries; protected function GetCount: Int64; virtual; abstract; function GetTotalCount: Int64; virtual; abstract; - procedure Update; virtual; abstract; - function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; virtual; - // The signature is changed to support viewport painting with an index offset - procedure Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); virtual; - property MainSeries: TSeries read GetMainSeries; public - constructor Create(AOwner: TMycChart); property Count: Int64 read GetCount; - property Owner: TMycChart read FOwner; property TotalCount: Int64 read GetTotalCount; end; + TLayer = class abstract(TObject) + private + FOwner: TMycChart; + protected + function GetSeries: TSeries; virtual; abstract; + procedure Update; virtual; abstract; + function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; virtual; abstract; + // The signature is changed to support viewport painting with an index offset + procedure Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); virtual; abstract; + public + constructor Create(AOwner: TMycChart); + procedure Repaint; + property Owner: TMycChart read FOwner; + property Series: TSeries read GetSeries; + end; + private - FSeriesList: TList; - FXAxisSeries: TSeries; + FSeriesList: TList; + FXAxisSeries: TLayer; FLookback: TWriteable; FIdleSubscrId: TMessageSubscriptionId; FViewStartIndex: Int64; @@ -53,6 +60,7 @@ type FJumpButtonRect: TRectF; FJumpButtonHot: Boolean; FJumpButtonPressed: Boolean; + FNeedRepaint: TFlag; protected procedure Paint; override; procedure DoIdle; @@ -66,7 +74,7 @@ type // Sets the master series that defines the time scale (X-axis). This series is not drawn. // The chart will not render any data until the X-axis series is set. - function SetXAxisSeries(const DataProvider: IMycDataProvider): TSeries; + function SetXAxisSeries(const DataProvider: IMycDataProvider): TLayer; // Creates an OHLC candlestick/bar series and returns it. // The data provider directly supplies OHLC items, as timestamps are managed by the data series internally. @@ -74,16 +82,17 @@ type const DataProvider: IMycDataProvider; const AUpColor: TAlphaColor = TAlphaColors.Green; const ADownColor: TAlphaColor = TAlphaColors.Red - ): TSeries; + ): TLayer; // Creates a simple line series for double values and returns it function AddDoubleSeries( const DataProvider: IMycDataProvider; const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue; const ALineWidth: Single = 1.5 - ): TSeries; + ): TLayer; property Lookback: TWriteable read FLookback write FLookback; + property NeedRepaint: TFlag read FNeedRepaint; end; implementation @@ -92,33 +101,12 @@ uses System.Math, Myc.FMX.Chart.Series; -constructor TMycChart.TSeries.Create(AOwner: TMycChart); -begin - inherited Create; - FOwner := AOwner; -end; - -function TMycChart.TSeries.GetMainSeries: TSeries; -begin - Result := Owner.FXAxisSeries; -end; - -function TMycChart.TSeries.GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; -begin - Result := false; -end; - -procedure TMycChart.TSeries.Paint(const Canvas: TCanvas; First, Last: Int64; const XForm, YForm: TFunc); -begin - // nothing -end; - { TMycChart } constructor TMycChart.Create(AOwner: TComponent); begin inherited Create(AOwner); - FSeriesList := TObjectList.Create(true); + FSeriesList := TObjectList.Create(true); FXAxisSeries := nil; FLookback := TWriteable.CreateWriteable(1000); // Load more data for panning @@ -141,7 +129,7 @@ begin inherited; end; -function TMycChart.SetXAxisSeries(const DataProvider: IMycDataProvider): TSeries; +function TMycChart.SetXAxisSeries(const DataProvider: IMycDataProvider): TLayer; begin // Free the old one if it exists, and create the new master series FXAxisSeries.Free; @@ -150,7 +138,7 @@ begin DataProvider.Link(counter); - FXAxisSeries := TChartSeriesProcessor.Create(Self, counter.Sender); + FXAxisSeries := TChartCustomLayer.Create(Self, counter.Sender); Result := FXAxisSeries; end; @@ -158,9 +146,9 @@ function TMycChart.AddDoubleSeries( const DataProvider: IMycDataProvider; const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue; const ALineWidth: Single = 1.5 -): TSeries; +): TLayer; begin - Result := TChartLineSeries.Create(Self, DataProvider, ALineColor, ALineWidth); + Result := TChartLineLayer.Create(Self, DataProvider, ALineColor, ALineWidth); FSeriesList.Add(Result); end; @@ -168,9 +156,10 @@ function TMycChart.AddOhlcSeries( const DataProvider: IMycDataProvider; const AUpColor: TAlphaColor = TAlphaColors.Green; const ADownColor: TAlphaColor = TAlphaColors.Red -): TSeries; +): TLayer; begin - Result := TChartOhlcSeries.Create(Self, DataProvider, AUpColor, ADownColor); + Result := + TChartOhlcLayer.Create(Self, DataProvider, TMutable.Constant(AUpColor), TMutable.Constant(ADownColor)); FSeriesList.Add(Result); end; @@ -181,11 +170,11 @@ begin exit; // First update the master series - var prevTotalCount := FXAxisSeries.TotalCount; + var prevTotalCount := FXAxisSeries.Series.TotalCount; FXAxisSeries.Update; // Check if the master series itself has changed - var newTotalCount := FXAxisSeries.TotalCount; + var newTotalCount := FXAxisSeries.Series.TotalCount; var seriesChanged := prevTotalCount <> newTotalCount; // Repaint if the last bar is visible, so we display live data @@ -193,7 +182,7 @@ begin for var series in FSeriesList do begin - var seriesCount := series.TotalCount; + var seriesCount := series.Series.TotalCount; // Check if this series may need a repaint (if it lags behind the master series and these lags are currently visible) if FViewStartIndex <= prevTotalCount - seriesCount then @@ -202,7 +191,7 @@ begin // Update the series series.Update; - if seriesCount <> series.TotalCount then + if seriesCount <> series.Series.TotalCount then seriesChanged := true; end; @@ -220,7 +209,10 @@ begin end; end; - if seriesRepaint and seriesChanged then + // finally, check the repaint signal + var repaintSignal := FNeedRepaint.Reset; + + if repaintSignal or (seriesRepaint and seriesChanged) then Repaint; end; @@ -269,7 +261,6 @@ var isHot: Boolean; dx: Single; indexDelta: Int64; - mainSeries: TSeries; maxIndex: Int64; begin // Update button hot state @@ -317,8 +308,7 @@ begin FViewStartIndex := FViewStartIndex + indexDelta; // Clamp values - mainSeries := FXAxisSeries; - maxIndex := mainSeries.Count - FViewCount; + maxIndex := FXAxisSeries.Series.Count - FViewCount; if (maxIndex < 0) then maxIndex := 0; @@ -341,7 +331,6 @@ var zoomFactor: Double; newViewCount: Int64; ratio: Double; - mainSeries: TSeries; begin Handled := true; @@ -349,8 +338,8 @@ begin if not Assigned(FXAxisSeries) then exit; - mainSeries := FXAxisSeries; - if (mainSeries.Count = 0) or (Width <= 0) then + var xAxisCount := FXAxisSeries.Series.Count; + if (xAxisCount = 0) or (Width <= 0) then exit; mousePos := ScreenToLocal(Screen.MousePos); @@ -368,8 +357,8 @@ begin // Clamp zoom level if (newViewCount < 10) then newViewCount := 10; - if (newViewCount > mainSeries.Count) then - newViewCount := mainSeries.Count; + if (newViewCount > xAxisCount) then + newViewCount := xAxisCount; if (newViewCount = FViewCount) then exit; @@ -385,9 +374,9 @@ begin // Clamp start index if (FViewStartIndex < 0) then FViewStartIndex := 0; - if (FViewStartIndex + FViewCount > mainSeries.Count) then + if (FViewStartIndex + FViewCount > xAxisCount) then begin - FViewStartIndex := mainSeries.Count - FViewCount; + FViewStartIndex := xAxisCount - FViewCount; end; Repaint; @@ -409,7 +398,6 @@ procedure TMycChart.Paint; var rect: TRectF; - series: TSeries; globalMin, globalMax, seriesMin, seriesMax, padding: Double; rangeInitialized: Boolean; xTransform: TFunc; @@ -417,14 +405,13 @@ var isButtonVisible: Boolean; buttonColor: TAlphaColor; path: TPathData; - mainSeries: TSeries; masterTotalCount, seriesFirst, seriesLast: Int64; begin inherited; rect := Self.LocalRect; // The chart requires an X-axis series to be set before it can draw anything. - if not Assigned(FXAxisSeries) or (FXAxisSeries.Count <= 1) or (FViewCount <= 1) then + if not Assigned(FXAxisSeries) or (FXAxisSeries.Series.Count <= 1) or (FViewCount <= 1) then begin Canvas.Fill.Color := TAlphaColors.Gray; Canvas.FillText(rect, 'No Data', false, 1, [], TTextAlign.Center, TTextAlign.Center); @@ -432,12 +419,11 @@ begin end; rangeInitialized := false; - mainSeries := FXAxisSeries; - masterTotalCount := mainSeries.TotalCount; - for series in FSeriesList do + masterTotalCount := FXAxisSeries.Series.TotalCount; + for var series in FSeriesList do begin - if CalcView(series, masterTotalCount, seriesFirst, seriesLast) then + if CalcView(series.Series, masterTotalCount, seriesFirst, seriesLast) then begin if series.GetValueRange(seriesFirst, seriesLast, seriesMin, seriesMax) then begin @@ -472,11 +458,11 @@ begin // --- Draw Series with Offset Compensation --- // The masterTotalCount is already calculated from the range finding loop - for series in FSeriesList do + for var series in FSeriesList do begin - if CalcView(series, masterTotalCount, seriesFirst, seriesLast) then + if CalcView(series.Series, masterTotalCount, seriesFirst, seriesLast) then begin - var offset := masterTotalCount - series.TotalCount; + var offset := masterTotalCount - series.Series.TotalCount; xTransform := function(index: Double): Single begin @@ -534,4 +520,15 @@ begin end; end; +constructor TMycChart.TLayer.Create(AOwner: TMycChart); +begin + inherited Create; + FOwner := AOwner; +end; + +procedure TMycChart.TLayer.Repaint; +begin + FOwner.Repaint; +end; + end. diff --git a/Src/Myc.Lazy.pas b/Src/Myc.Lazy.pas index 3a528b4..6ffcd29 100644 --- a/Src/Myc.Lazy.pas +++ b/Src/Myc.Lazy.pas @@ -38,6 +38,7 @@ type class property Null: IMutable read FNull; class function Construct(const Changing: TSignal; const Proc: TFunc): TMutable; overload; static; + class function Constant(const Value: T): TMutable; overload; static; property Value: T read GetValue; property Changed: TSignal read GetChanged; @@ -74,38 +75,15 @@ type end; TLazy = record - type - ILazy = interface - {$REGION 'property access'} - function GetChanged: TState; - {$ENDREGION} - function Pop(out Res: T): Boolean; - property Changed: TState read GetChanged; - end; - - {$REGION 'private'} - strict private - class var - FNull: ILazy; - class constructor CreateClass; - private - FLazy: ILazy; - function GetChanged: TState; inline; - {$ENDREGION} + FMutable: TMutable; + FChanged: TFlag; + FChangeState: TSignal.TSubscription; + function GetValue: T; inline; public - constructor Create(const ALazy: ILazy); - class operator Initialize(out Dest: TLazy); - class operator Implicit(const A: ILazy): TLazy; overload; - class operator Implicit(const A: TLazy): ILazy; overload; - - class function Construct(const Changing: TSignal.ISignal; const Proc: TFunc): TLazy; overload; static; - class function Construct(const Mutable: TMutable): TLazy; overload; static; - class property Null: ILazy read FNull; - - function Pop(out Res: T): Boolean; inline; - - property Changed: TState read GetChanged; + constructor Create(const AMutable: TMutable); + function Update: Boolean; + property Value: T read GetValue; end; implementation @@ -123,7 +101,12 @@ end; class constructor TMutable.CreateClass; begin - FNull := TMycNullMutable.Create; + FNull := TMycNullMutable.Create(Default(T)); +end; + +class function TMutable.Constant(const Value: T): TMutable; +begin + Result := TMycNullMutable.Create(Value); end; class function TMutable.Construct(const Changing: TSignal; const Proc: TFunc): TMutable; @@ -156,55 +139,6 @@ begin Dest.FMutable := FNull; end; -{ TLazy } - -constructor TLazy.Create(const ALazy: ILazy); -begin - if Assigned(ALazy) then - FLazy := ALazy; -end; - -class function TLazy.Construct(const Changing: TSignal.ISignal; const Proc: TFunc): TLazy; -begin - Result := TMycFuncLazy.Create(Changing, Proc); -end; - -class constructor TLazy.CreateClass; -begin - FNull := TMycNullLazy.Create; -end; - -class function TLazy.Construct(const Mutable: TMutable): TLazy; -begin - var cap := Mutable; - Result := TMycFuncLazy.Create(cap.Changed, function: T begin exit(Mutable.Value) end); -end; - -function TLazy.GetChanged: TState; -begin - Result := FLazy.Changed; -end; - -function TLazy.Pop(out Res: T): Boolean; -begin - Result := FLazy.Pop(Res); -end; - -class operator TLazy.Implicit(const A: ILazy): TLazy; -begin - Result.Create(A); -end; - -class operator TLazy.Implicit(const A: TLazy): ILazy; -begin - Result := A.FLazy; -end; - -class operator TLazy.Initialize(out Dest: TLazy); -begin - Dest.FLazy := FNull; -end; - { TWriteable } constructor TWriteable.Create(const AWriteable: IWriteable); @@ -261,4 +195,26 @@ begin Result := A.FWriteable; end; +{ TLazy } + +constructor TLazy.Create(const AMutable: TMutable); +begin + FMutable := AMutable; + FChanged := TFlag.CreateFlag; + FChanged.Notify; + FChangeState := AMutable.Changed.Subscribe(FChanged); +end; + +function TLazy.GetValue: T; +begin + Result := FMutable.Value; +end; + +function TLazy.Update: Boolean; +begin + Result := FChanged.State.IsSet; + if Result then + FChanged.Reset; +end; + end.