Skip to content
Waldemar Derr edited this page Oct 17, 2019 · 39 revisions
  TAQ = class sealed (TAQBase)
  public
    class function Managed: TAQ;
    class function Unmanaged: TAQ;

    class function Take(...): TAQ;

    class function HasActiveActors(...): Boolean;

    class function GetUniqueID: Integer;

    class function Ease(...): TEaseFunction;
    class function EaseReal(...): Real;
    class function EaseInteger(...): Integer;
    class function EaseColor(...): TColor;
    class function EasePoint(...): TPoint; 
    class function EaseRect(...): TRect;
    class function EaseString(...): String;

  // Public instance related stuff
  public
    constructor Create; override;
    destructor Destroy; override;

    function Each(...): TAQ;
    function EachInterval(...): TAQ;
    function EachTimer(...): TAQ;
    function EachAnimation(...): TAQ;
    function EachDelay(...): TAQ;
    function EachRepeat(...): TAQ;

    function NewChain: TAQ;
    function EndChain: TAQ;

    function Die: TAQ;

    function Append(AObject: TObject): TAQ; overload;
    function Append(const Objects: TObjectArray): TAQ; overload;
    function Append(Objects: TObjectList): TAQ; overload;
    function AppendAQ(AQ: TAQ): TAQ;

    function ChildrenAppend(Recurse: Boolean = False; ChildrenFiller: TEachFunction = nil): TAQ;
    function ChildrenChain(Recurse: Boolean = False; ChildrenFiller: TEachFunction = nil): TAQ;
    function ParentsAppend(Recurse: Boolean = False; ParentsFiller: TEachFunction = nil): TAQ;
    function ParentsChain(Recurse: Boolean = False; ParentsFiller: TEachFunction = nil): TAQ;

    function MultiplexChain: TAQ;
    function DemultiplexChain: TAQ;

    function AnimationActorsChain(ID: Integer = 0; IncludeOrphans: Boolean = False): TAQ;
    function IntervalActorsChain(ID: Integer = 0; IncludeOrphans: Boolean = False): TAQ;
    function TimerActorsChain(ID: Integer = 0; IncludeOrphans: Boolean = False): TAQ;
    function DelayActorsChain(ID: Integer = 0; IncludeOrphans: Boolean = False): TAQ;

    function FinishAnimations(ID: Integer = 0): TAQ;
    function CancelAnimations(ID: Integer = 0): TAQ;
    function FinishTimers(ID: Integer = 0): TAQ;
    function CancelTimers(ID: Integer = 0): TAQ;
    function CancelDelays(ID: Integer = 0): TAQ;
    function CancelIntervals(ID: Integer = 0): TAQ;

    function FilterChain(ByClass: TClass): TAQ; overload;
    function FilterChain(FilterEach: TEachFunction): TAQ; overload;

    function ExcludeChain(ByClass: TClass): TAQ; overload;
    function ExcludeChain(AObject: TObject): TAQ; overload;
    function ExcludeChain(const Objects: TObjectArray): TAQ; overload;
    function ExcludeChain(Objects: TObjectList): TAQ; overload;
    function ExcludeChain(AQ: TAQ): TAQ; overload;
    function ExcludeChain(ExcludeEach: TEachFunction): TAQ; overload;

    function IfThen(Condition: Boolean): TAQ;
    function IfElse: TAQ;
    function IfEnd: TAQ;

    function IfAll(EachFunction: TEachFunction): TAQ;
    function IfAny(EachFunction: TEachFunction): TAQ;

    function IfContains(AObject: TObject): TAQ;

    function IfContainsAny(ByClass: TClass): TAQ; overload;
    function IfContainsAny(const Objects: TObjectArray): TAQ; overload;
    function IfContainsAny(Objects: TObjectList): TAQ; overload;
    function IfContainsAny(AQ: TAQ): TAQ; overload;

    function IfContainsAll(ByClass: TClass): TAQ; overload;
    function IfContainsAll(const Objects: TObjectArray): TAQ; overload;
    function IfContainsAll(Objects: TObjectList): TAQ; overload;
    function IfContainsAll(AQ: TAQ): TAQ; overload;

    function SliceChain(StartIndex: Integer; Count: Integer = 0): TAQ;

    function DebugMessage(HeadMessage: String = ''; Caption: String = ''): TAQ;

    function Plugin: T;

    function Contains(AObject: TObject): Boolean;
    procedure Clean;

    property CurrentInterval: TInterval read FCurrentInterval;
  end;

TAQ.Managed

class function TAQ.Managed: TAQ;

Returns a managed TAQ instance

Managed means, that you don't need to worry about memory leaks ;-) There is a simple (but maybe powerful) garbage collector implementation. By requesting for a managed instance it looks primary in the garabage for an died TAQ instance and in case returning it.

TAQ.Unmanaged

class function TAQ.Unmanaged: TAQ;

Returns a unmanaged TAQ instance

It's just the opposite of TAQ.Managed and it is the same as TAQ.Create

TAQ.Take

class function TAQ.Take(AObject: TObject): TAQ;
class function TAQ.Take(const Objects: TObjectArray): TAQ;
class function TAQ.Take(Objects: TObjectList): TAQ;
class function TAQ.Take<T: class>(Objects: TObjectList<T>): TAQ;

Creates a new or recycle an existing managed TAQ instance

There are several overloaded versions of the method. Each of them creates or retake an matching TAQ instance from the garbage collector. The returned TAQ instance contains the passed object(s) and you can work with them.

TAQ.HasActiveActors

class function TAQ.HasActiveActors(CheckActors: TActorRoles; AObject: TObject; 
  ID: Integer = 0): Boolean;

Determines, whether there are active actors for a specific object

Determines, whether there are active actors (running animation, delay...) for AObject in general or optional (if passed) for the actor with the specified ID.

Example

if not TAQ.HasActiveActors([arAnimation], MyButton, BoundsAnimationID) then
  Take(MyButton)
    .Plugin<TAQPControlAnimations>
    .BoundsAnimation(..., BoundsAnimationID);

TAQ.GetUniqueID

class function TAQ.GetUniqueID: Integer;

Returns a unique ID

The returned ID can be used to identify an animation, delay, timer, interval etc. It is recommended to save such IDs in class variables and fill them in an class constructor, see following example.

Example

TMyClass = class
private 
  class var MyAnimationID: Integer;
  class constructor Create;
end;

class constructor TMyClass.Create;
begin
  MyAnimationID := TAQ.GetUniqueID;
end;

TAQ.Ease

There are three overloaded versions of this method and each of them is a little bit special, so we list them separetly. The returned anonymous function of type TEaseFunction can be passed to the specific EaseX methods.

class function TAQ.Ease(EaseType: TEaseType;
  EaseModifier: TEaseModifier = emIn): TEaseFunction;

This method returns a TEaseFunction for the passed TEaseType, which can be optional modified with TEaseModifier

class function TAQ.Ease(const EaseTypes: array of TEaseType;
  EaseModifier: TEaseModifier = emIn): TEaseFunction;

This version creates a TEaseFunction which wraps more than one TEaseType. The returned function returns normalized and compiled values. Additionally you can optional pass a TEaseModifier.

class function TAQ.Ease(const EaseFunction: TEaseFunction = nil;
  EaseModifier: TEaseModifier = emIn): TEaseFunction;

This overloaded version takes any TEaseFunction and modify it with TEaseModifier. This is useful for custom TEaseFunctions.

TAQ.EaseReal

class function TAQ.EaseReal(StartValue, EndValue, Progress: Real; EaseType: TEaseType;
  EaseModifier: TEaseModifier = emIn): Real; 
class function TAQ.EaseReal(StartValue, EndValue, Progress: Real;
  const EaseFunction: TEaseFunction): Real; overload;

Interpolates between the start and end value for the Real type

EaseReal interpolates (easing) between the passed StartValue and EndValue. Progress is a value between 0 and 1. The concrete easing is defined whether by the passed TEaseType or a TEaseFunction.

TAQ.EaseInteger

class function TAQ.EaseInteger(StartValue, EndValue: Integer; Progress: Real; 
  EaseType: TEaseType; EaseModifier: TEaseModifier = emIn): Integer; overload;
class function TAQ.EaseInteger(StartValue, EndValue: Integer; Progress: Real;
  const EaseFunction: TEaseFunction): Integer; overload;

Interpolates between the start and end value for the Integer type

EaseInteger interpolates (easing) between the passed StartValue and EndValue. Progress is a value between 0 and 1. The concrete easing is defined whether by the passed TEaseType or a TEaseFunction.

TAQ.EaseColor

class function TAQ.EaseColor(StartColor, EndColor: TColor; Progress: Real; 
  EaseType: TEaseType; EaseModifier: TEaseModifier = emIn): TColor; overload;
class function TAQ.EaseColor(StartColor, EndColor: TColor; Progress: Real;
  const EaseFunction: TEaseFunction): TColor; overload;

Interpolates between the start and end color

EaseColor interpolates (easing) between the passed StartColor and EndColor. Progress is a value between 0 and 1. The concrete easing is defined whether by the passed TEaseType or a TEaseFunction.

TAQ.EasePoint

class function TAQ.EasePoint(StartPoint, EndPoint: TPoint; Progress: Real; 
  EaseType: TEaseType; EaseModifier: TEaseModifier = emIn): TPoint; overload;
class function TAQ.EasePoint(StartPoint, EndPoint: TPoint; Progress: Real;
  const EaseFunction: TEaseFunction): TPoint; overload;

Interpolates between the start and end point

EasePoint interpolates (easing) between the passed StartPoint and EndPoint. Progress is a value between 0 and 1. The concrete easing is defined whether by the passed TEaseType or a TEaseFunction.

TAQ.EaseRect

class function TAQ.EaseRect(StartRect, EndRect: TRect; Progress: Real; 
  EaseType: TEaseType; EaseModifier: TEaseModifier = emIn): TRect; overload;
class function TAQ.EaseRect(StartRect, EndRect: TRect; Progress: Real;
  const EaseFunction: TEaseFunction): TRect; overload;

Interpolates between the start and end rect

EaseRect interpolates (easing) between the passed StartRect and EndRect. Progress is a value between 0 and 1. The concrete easing is defined whether by the passed TEaseType or a TEaseFunction.

TAQ.EaseString

class function TAQ.EaseString(const StartString, EndString: String; Progress: Real; 
  EaseType: TEaseType; EaseModifier: TEaseModifier = emIn): String; overload;
class function TAQ.EaseString(const StartString, EndString: String; Progress: Real;
  const EaseFunction: TEaseFunction): String; overload;

Interpolates between the start and end string

EaseString interpolates (easing) between the passed StartString and EndString. Progress is a value between 0 and 1. The concrete easing is defined whether by the passed TEaseType or a TEaseFunction.

TAQ.Each

function TAQ.Each(const EachFunction: TEachFunction): TAQ;

Performs the passed method/closure on each in TAQ contained object

Each is the core method of AnyiQuack. Although its implementation looks simple, it is very powerful. The passed EachFunction gets as first parameter the processing TAQ instance (Self) and in the second parameter the subject object. You are able to break the Each from inside of EachFunction by returning False. Otherwise you have to return True for further/full processing.

If the current TAQ instance contains other TAQ instances (appended by TAQ.AppendAQ) and TAQ.Recurse is True (Default) EachFunction is also performed on them.

TAQ.EachInterval

function TAQ.EachInterval(Interval: Integer; const Each: TEachFunction; 
  ID: Integer = 0): TAQ;

Performs the passed method/closure on each in TAQ contained object each interval repeatedly

The passed method Each is called each Interval milliseconds, until you cancel it with TAQ.CancelIntervals. If you consider to perform several EachInterval's on the same objects, you should define an ID (see TAQ.GetUniqueID), so you'll be able to cancel only the specific intervaled Each.

A running interval can be canceled with TAQ.CancelIntervals.

TAQ.EachTimer

function TAQ.EachTimer(Duration: Integer; const Each: TEachFunction; 
  const LastEach: TEachFunction = nil; ID: Integer = 0): TAQ;

Calls the passed Each method periodically on each in TAQ contained object as long as the timer runs, the LastEach method will be called once when the timer expires

From the performing Each function you can access the associated interval with AQ.CurrentInterval and use further runtime specific informations e.g. AQ.CurrentInterval.Progress.

The passed Each method is called in high frequency, but in adapting manner.

A runnig timer can be canceled with TAQ.CancelTimers.

Example

procedure TForm1.FormCreate(Sender: TObject);
begin
  Take(Sender)
    .EachTimer(9000,
      function(AQ: TAQ; O: TObject): Boolean
      begin
        with TForm(O) do
          Caption := Format('Pos(X: %d; Y: %d); Progress: %.5f',
            [Left, Top, AQ.CurrentInterval.Progress]);
        Result := True;
      end,
      function(AQ: TAQ; O: TObject): Boolean
      begin
        TForm(O).Caption := 'Tracking of the form placement finished.';
        Result := True;
      end);
end;

TAQ.EachAnimation

function TAQ.EachAnimation(Duration: Integer; Each: TEachFunction; 
  LastEach: TEachFunction = nil; ID: Integer = 0): TAQ;

Performs an abstract animation on each in TAQ contained object

Abstract animation means, that at this place you get all the key requirements for an animation, but TAQ itself can't do any meaningful animations here. The animation has a fixed length, which is defined by the Duration parameter. The animation is performed on the contained objects, they are passed one by one to the Each method in the second parameter. The progress of the animation is available inside the passed Each method through AQ.CurrentInterval.Progress (AQ stands for the first parameter in the Each method). With the "Easing-Methods" of TAQ, such as TAQ.EaseInteger, you can interpolate between a start and end value.

Internally there is no big difference between TAQ.EachAnimation and TAQ.EachTimer, but explicit entity so we can differ in flow logic.

A runnig animation can be canceled with TAQ.CancelAnimations or finished with TAQ.FinishAnimations.

Example

procedure TForm1.FormClick(Sender: TObject);
var
  StartLeft, EndLeft: Integer;
  SenderForm: TForm;
begin
  SenderForm := TForm(Sender);
  StartLeft := SenderForm.Left;
  EndLeft := SenderForm.Left + 250;

  Take(Sender)
    .EachAnimation(500,
      function(AQ: TAQ; O: TObject): Boolean
      begin
        TForm(O).Left := TAQ.EaseInteger(StartLeft, EndLeft,
          AQ.CurrentInterval.Progress, etBounce);
        Result := True;
      end);
end;

TAQ.EachDelay

function TAQ.EachDelay(Delay: Integer; const Each: TEachFunction; 
  ID: Integer = 0): TAQ;

Calls the passed Each method after the Delay on each in TAQ contained object

A running delay can be canceled with TAQ.CancelDelays.

Example

For this example place a TButton with the name SearchButton and a TEdit with the name SearchEdit. Bind the following event handler to SearchButton.OnClick and SearchEdit.OnChange.

procedure TForm1.SearchButtonClick(Sender: TObject);
begin
  Caption := 'The search is performed at ' + DateTimeToStr(Now);
end;

procedure TForm1.SearchEditChange(Sender: TObject);
begin
  Take(Sender)
    .CancelDelays
    .EachDelay(500,
      function(AQ: TAQ; O: TObject): Boolean
      begin
        SearchButton.Click;
        Result := True;
      end);
end;

Note that the "search" is just triggered, after you do not type for the defined delay (500ms).

TAQ.EachRepeat

function TAQ.EachRepeat(Times: Integer; const Each: TEachFunction): TAQ;

Calls the passed Each method on each in TAQ contained object x times

TAQ.NewChain

function TAQ.NewChain: TAQ;

Creates a new chain and returns it

A chain is like a stack, with NewChain you put a new TAQ instance on it, with EndChain you return to the previous TAQ instance.

TAQ.EndChain

function TAQ.EndChain: TAQ;

Closes the current chain and return to the previous

See TAQ.NewChain