Please enable JavaScript to view this site.

Codolex

For each activity, we need to implement the following files:

 

Codolex.Activity.YourActivityName.Pluggable.pas

Codolex.Activity.YourActivityName.Component.Interfaces.pas

Codolex.Activity.YourActivityName.Component.pas

Codolex.Activity.YourActivityName.Editor.pas

Codolex.Activity.YourActivityName.Editor.dfm

Codolex.Activity.YourActivityName.Storage.pas

Codolex.Activity.YourActivityName.Validator.pas

Codolex.Activity.YourActivityName.CodeGen.Delphi.pas

 

In the example folder, there is an example for every listed file. Remove the example files from the project to prevent errors when adding your activity files. To start, we can copy these files into a new folder, rename them, and add them to the project. The easiest way is to rename every instance of example in the names and contents to your activity name. Continuing with our example, let's name this "Playsound".

clip0031

After we added the files for PlaySound, we can look at the files one by one.

 

Pluggable

 

This class is the base for your activity and connects all the needed units. In this class, you can provide the details for the activity, like the name and description. The pluggable class defined here is added to the list of activities, supported by the plugin, in the

 

"CodolexComponents.YourActivityName.Plugin.pas".

 

The declaration should look something like this:

 

TPlaySoundPluggableComponent = class(TInterfacedObject, IFlowPluggableComponent, IFlowPluggableComponentWithValidation, IFlowPluggableComponentWithEditor)

public

 function Name: string;

 function Description: string;

function Category: string;

 

function CreateComponent(const Flow: IFlow): IFlowComponent;

 

function CodeGenerator(const FlowComponent: IFlowComponent): IFlowPluggableComponentCodeGen;

function Storage(const Container: TContainer): IFlowPluggableComponentStorage;

function ShowEditor(const FlowComponent: IFlowComponent; const Container: TContainer): TModalResult;

 

function Validator(const FlowComponent: IFlowComponent): ICodolexValidator;

function CreateEditor: IFlowPluggableComponentEditor;

 

function SupportedLanguages: TCodolexLanguages;

end;

 

Let's go over a few functions to clarify what they are needed for.

Name: Provides the name for the validator and the storage.

Description: Provides the default description for your activity. This is how the activity will be shown in the palette.

Category: Defines the category the activity can be found in in the palette. It's recommended to provide a unique name that does not conflict with the other Codolex categories. Keep them the same for the activities you create here. For both activities, let's use the Playsound category.

SupportedLanguages: This returns a list of languages that this activity is going to support. For now, Delphi is the only language used. When more languages are supported by Codolex, be sure to update this section when you want to use it for another language. The available languages are specified in the 'TCodolexLanguage' Enum from the 'CodeGen.Language.Defines' unit.

GetTags: This function is not present in the example yet but could be a great function to add. If your activity has multiple options, and you know a few of these options are going to be used quite often, you can add a tag for each of these options to make them faster to select. 

Other functions of the pluggable component: The other functions are needed for Codolex to create the instance and storage files etc. Assuming you copied them correctly, these don't need to be changed.

 

Component interface

 

To create your activity you need a component. This of course starts with creating the interface. Your interface has to be inherited from the 'IFlowActivity' interface. In your interface, you can specify the properties you need for your activity.

 

The first thing you need to resolve here is the GUID, Every activity needs its own GUID, which you can create via the shortcut ctrl+shift+g. Replace the {$MESSAGE WARN 'Set GUID'} with a unique GUID.

 

In our case, we need the user to provide a file for the sound and the mode for the Playsound function (SND_NODEFAULT Or SND_ASYNC Or SND_LOOP). So we can define a property SoundFile of the type IVariable and PlayMode of the type String

 

type

IFlowActivityPlaySound = interface(IFlowActivity)

   ['{344D0F87-1DB3-4C89-B5E6-57A638C649E9}']

 

  function GetSoundFile: IVariable;

  function GetPlayMode: string;

 

  procedure SetSoundFile(const Value: IVariable);

  procedure SetPlayMode(const Value: string);

 

  property SoundFile: IVariable read GetSoundFile write SetSoundFile;

  property PlayMode: string read GetPlayMode write SetPlayMode;

 end;

 

Component Implementation

 

When you've finished your interface, you also need to make its implementation. This is done by creating a class that inherits from the 'TFlowActivity' class, as well as the 'IFlowComponent' interface and your newly created interface.

 

Implement the property functions. The complex fields like IVariable should be ignored by the JSONMarshaller in the storage because we need to serialize the references ourselves, so we add the [JSONMarshalled(False)] directive before the variable declaration. Add the REST.Json.Types unit to the interface uses section as well. Otherwise, this directive gets ignored, and you will get errors.

 

 

uses

 ...

REST.Json.Types,

...;

 

 TFlowActivityPlaySound = class(TFlowActivity, IFlowComponent, IFlowActivityPlaySound)

const

   FQName = 'Template.Core.PlaySound';

   DescriptionOfComponent = 'Play sound';

 private

   [JSONMarshalled(False)]

   FSoundFile: IVariable;

 

   FPlayMode: string;

...

 

 

Next to the properties, you can also see the 2 constants. FQName and DescriptionOfComponent. These properties define the name and description of your component. The name is the same as described in the Pluggable section above. The description is used in your flow as the name of the activity.

 

There are also some procedures which are used by Codolex. The first is InitComponent. In this procedure, you set the name, description, and icon of your component. This is done by setting the variables of the TFlowActivity we inherited in the class.

 

For the Icon, you can use any UTF-8 character and color. Note that Delphi is not able to display all UTF-8 characters and might show as an invalid character in the IDE. You can also provide a color to use in the activity. It's recommended to keep them the same for all the activities you add in one component.

 

procedure TFlowActivityPlaySound.InitComponent;

begin

 inherited;

 

 FComponentName := FQName;

 FComponentDescription := DescriptionOfComponent;

 

Icon.Init('▸', clWebLimeGreen);

end;

 

In the init component function, it's also possible to add default values to the properties defined in the interface.

procedure TFlowActivityPlaySound.InitComponent;

begin

 ...

 FPlayMode := 'Async';

 ...

end;

With the inherited function InitializeElement, you can specify a return variable. If you would like this variable to be of a custom type, then use the next piece of code

 

procedure TFlowActivityPlaySound.InitializeElement;

begin

 inherited;

 

 _ReturnVariable.VariableType := TDataType.Custom_;

 _ReturnVariable.CustomTypeName := 'TYourCustomType';

end;

 

 

Editor

 

Next, continuing with our example, the editor file will be the property screen for this activity. This is the screen you are going to see in Codolex when editing an activity. Here you can define how the user can provide input for the properties. You can design this screen the way you like it, or, the way you think will be the most intuitive for the user of the activity. For ordinal types, you can use the default input fields that Delphi has to offer. For example, if you want the user to fill in a name, just put a TEdit on the form with a label that clarifies that is the name input. And just like that, the description input that is already present.

 

clip0032

 

Another option is to use the editor's inputs provided by Codolex, since they are needed for complex properties like the IVariable. These editors are unavailable during design time, so you must initialize them at runtime. This can be done in the initialize section. 

 

But before we can initialize them, we need to define a few things. 

 

1. Add these uses for your input to the interface section. (if not there already)

 

Codolex.Activity.SoundFile.Component.Interfaces,

Codolex.Modelling.Helper.Interfaces,

ProjectModel.Variable.Interfaces,

Codolex.Editors.PropertyInput.FlowVariable;

 

To see the other editors that are available, use the autocomplete on "Codolex.Editors.PropertyInput." this will provide you with options for entity selection, lookups, etc.

If the editor does not see the available classes. Try to compile the project, and restart so load the configuration correctly

 

2. Create class properties for the IVariable and the FlowVariable input.

 

Private

 FSoundFile :IVariable;

 FSoundFileInput: TfrmFlowVariableInput;

...

 

3. Add a resource string for the label name. This is not mandatory but could make it easier to change language settings if needed.

 

resourcestring

SoundFileInputLabel = 'Sound file (.wav)';

 

4. Place a panel in the designer where you want the input to be. This panel is going to be filled with the input with alClient alignment. It's recommended to give this panel the alTop align setting, as seen in the picture above.

 

5. Add the "OnChange" function for the sound file. This function can be assigned to the "OnChange" event after adding the input, to save the value after the user provides input.

 

procedure OnSoundFileChange(Variable: IVariable);

...

procedure TfrmActivityPlaySound.OnSoundFileChange(Variable: IVariable);

begin

FSoundFIle := Variable;

end;

 

6. Add the CanChooseVariable function for the sound file input. This function also needs to be assigned after the input is created. In this case, it's needed to provide the right suggestions for the user in the input field. Here you can also limit the options for the user. For example, we want the user to select a string for the sound file. 

 

function TfrmActivityPlaySound.CanChooseVariableForSoundFile(Variable: IVariable): Boolean;

begin

Result := TFlowVariableDefines.IsVariableCurrentSelected(Variable, FSoundFile) or

 TFlowVariableDefines.IsVariableString(Variable);

end;

 

The "TFlowVariableDefines" class can be used for all sorts of helpers. Here is how you can check for a custom type:

function TfrmActivityPlaySound.CanChooseVariableForSoundFile(Variable: IVariable): Boolean;

begin

Result := False;

 

 if Assigned(Variable) then

 begin

   var IsCustom := Variable.VariableType = TDataType.Custom_;

   if IsCustom then

   begin

     var CustomType := Variable.CustomTypeName.ToLower;

    Result := CustomType = 'Array of const';

   end;

 end;

end;

 

7. Now we can go on with the initialization code.

var FSoundFileInput := TfrmFlowVariableInput.AddInput(

 pnlSoundFile,

 FFlowModellingHelper,

 FlowComponent,

 FSoundFile

);

 

We can use the AddInput function from the Input form class to create the component and add in the class property we created in step 1. By providing the panel, the class automatically adds the component as a child of the panel.

 

After we have created the input. we have to add some properties and events and initialize the component.

FSoundFileInput.LabelName := SoundFileInputLabel;

FSoundFileInput.InitializeInput;

FSoundFileInput.OnChange := OnSoundFileChange;

FSoundFileInput.OnFilter := CanChooseVariableForSoundFile;

FSoundFileInput.LoadVariableSelection;

 

Repeat these steps for all your properties to make every property available in the editor. When all the editors are set up, the user can edit the value of the input. But, for the changes to be persistent, we need to save the values in the component as well.

 

This can be done with the function SaveToModel.

procedure TfrmActivityPlaySound.SaveToModel;

begin

 FActivity.SoundFile := FSoundFile;

 FActivity.Description := editDescription.Text;

 FActivity.PlayMode := FPlayMode;

end;

 

Be sure to fill in the contents of this function to save all your properties.

 

Storage

 

The storage unit describes how your activity should be saved to and read from the JSON. This is done using 3 classes. Storage, Serializer, and Deserializer. We don't need to worry about the storage class. This class is here to provide an instance of the serializer and deserializer.

 

The serializer needs a DoSerialize function that serializes the references for us that we did mark as "JSONMarshalled False". 

procedure TActivityPlaySoundSerializer.DoAfterSerialize;

var

Activity: IFlowActivityPlaySound;

begin

 inherited;

 

Activity := FEntity;

 SerializeReference<IVariable>(FJson, 'SoundFile', Activity.SoundFile);

end;

 

We need to deserialize the same references in the afterparse.

 

procedure TActivityPlaySoundDeserializer.DoAfterParse;

var

Activity: IFlowActivityPlaySound;

begin

 inherited;

 

Activity := FEntity;

 DeserializeReference(FJson, 'SoundFile', procedure (const Variable: IVariable)

   begin

    Activity.SoundFile := Variable;

   end);

end;

 

Remember, these functions need to be overridden in the declaration to make use of the inherited function.

© by GDK Software