Custom elements
Blox has an open architecture: when the built-in libraries do not have the shape
you need, you build your own block by descending from TTMSFNCBloxBlock and
drawing a path, then register it so it appears in the
Selector and ToolBar
alongside the built-in shapes. This chapter shows how to draw a custom block,
give it link points, and register it (and how to re-categorise existing blocks).
Drawing a custom block
Override GetBlockPath and add geometry to the supplied path. Coordinates are
in the block's original rectangle space (0–100 on each axis), so the shape scales
automatically with the block. AddPolygon takes a TTMSFNCBloxPointArray built
from TMSFNCBloxPoint(X, Y); AddRectangle and AddEllipse cover the simple
cases.
type
THexagonBlock = class(TTMSFNCBloxBlock)
protected
procedure GetBlockPath(APath: TTMSFNCBloxPath; ADrawer: TTMSFNCBloxBlockDrawer); override;
public
procedure UpdateLinkPoints; virtual;
end;
procedure THexagonBlock.GetBlockPath(APath: TTMSFNCBloxPath;
ADrawer: TTMSFNCBloxBlockDrawer);
var
LPoly: TTMSFNCBloxPointArray;
begin
{ Coordinates are in the 0..100 original-rectangle space, so the shape scales. }
SetLength(LPoly, 7);
LPoly[0] := TMSFNCBloxPoint(25, 0);
LPoly[1] := TMSFNCBloxPoint(75, 0);
LPoly[2] := TMSFNCBloxPoint(100, 50);
LPoly[3] := TMSFNCBloxPoint(75, 100);
LPoly[4] := TMSFNCBloxPoint(25, 100);
LPoly[5] := TMSFNCBloxPoint(0, 50);
LPoly[6] := TMSFNCBloxPoint(25, 0);
APath.AddPolygon(LPoly);
end;
procedure THexagonBlock.UpdateLinkPoints;
begin
with OriginalRect do
begin
LinkPoints.Clear;
LinkPoints.AddLink(Left, (Bottom - Top) / 2, aoLeft);
LinkPoints.AddLink(Right, (Bottom - Top) / 2, aoRight);
end;
end;
Tip
Override UpdateLinkPoints to give your block link points at meaningful
positions (see Working with blocks for the
coordinate convention).
Registering elements
Custom blocks become available to the selector and toolbar through the
OnRegisterElements event. Call RegisterElement(AClass, AId, ACaption, ACategory) from the FMX.TMSFNCBloxUIRegistration unit (or the VCL./WEB.
equivalent). The selector and toolbar pick up the new category automatically.
procedure TForm1.BloxControl1RegisterElements(Sender: TObject);
begin
{ RegisterElement(class, id, caption, category). An empty id uses the class name. }
RegisterElement(THexagonBlock, '', 'Hexagon', 'My Shapes');
end;
Re-categorising and refreshing
Calling RegisterElement again for an existing class re-registers it, so you can
move blocks between categories or rename their caption. After changing
registrations at runtime, call Rebuild on the selector and/or toolbar so they
reflect the change:
procedure TForm1.ConsolidateCategory;
begin
{ Re-registering an existing class moves it to the new category. }
RegisterElement(TTMSFNCBloxBlock, '', 'Simple block', 'My Blocks');
RegisterElement(TTMSFNCBloxTextBlock, '', 'Text block', 'My Blocks');
RegisterElement(TTMSFNCBloxLine, '', 'Line', 'My Blocks');
{ Refresh the palette so the new category shows. }
BloxSelector1.Rebuild;
end;
Putting it together
The full custom-element flow ties the pieces together: register the custom block
from OnRegisterElements, then create and add an instance — combining
registration with programmatic insertion of the custom shape:
procedure TForm1.BloxControl1RegisterElements(Sender: TObject);
begin
RegisterElement(THexagonBlock, '', 'Hexagon', 'My Shapes');
end;
procedure TForm1.AddHexagonClick(Sender: TObject);
var
LBlock: THexagonBlock;
begin
LBlock := THexagonBlock.Create;
LBlock.Left := 80;
LBlock.Top := 80;
LBlock.Width := 140;
LBlock.Height := 120;
LBlock.Text := 'Custom';
LBlock.FillColor := MakeGraphicsColor(150, 80, 200);
LBlock.TextColor := gcWhite;
BloxControl1.Blox.Add(LBlock);
end;
Pitfalls
- Draw in 0–100 space, not pixels.
GetBlockPathcoordinates are relative to the original rectangle so the shape scales with the block; using pixel values produces a shape that does not resize. - Register before the control needs the block. Wire
OnRegisterElements(or callRegisterElementson the control) before relying on a custom id inStartInsertingElement. - Refresh the palette after runtime re-registration. The selector/toolbar do
not always auto-refresh — call
Rebuildif a new block does not appear.