Skip to content

KIDEx YAML Attributes (RTTI-based property discovery)

Starting with Kittox 4.x, the KIDEx visual editor discovers YAML metadata properties at runtime via Delphi RTTI attributes, replacing the external MetadataTemplates YAML files used in previous versions.

When you right-click a node in the KIDEx tree editor, the popup menu is built dynamically by reading the RTTI annotations on the Delphi class that represents that node.

Attribute types

Kittox defines six custom attribute classes in EF.YAML.Attributes.pas:

YamlNode — Scalar properties

The most common attribute. Marks a property as mapped to a YAML node with an optional default value.

pascal
// Without default value
[YamlNode('Expression', 'SQL expression for computed fields')]
property Expression: string read GetExpression;

// With default value (always a string).
// NOTE for Boolean properties: the default carries the INVERSE of the
// runtime default — IsVisible defaults to True at runtime, so the attribute
// declares 'False'. See "Boolean defaults — the inverse convention" below.
[YamlNode('IsVisible', 'False', 'Field visibility in views')]
property IsVisible: Boolean read GetIsVisible;

// Localizable property (generates DisplayLabel + DisplayLabel2)
[YamlNode('DisplayLabel', '', 'Label shown in forms and grids', True)]
property DisplayLabel: string read GetDisplayLabel;

In KIDEx, scalar properties appear as "Add {NodePath}" or "Add {NodePath}: {Default}" menu items when the node is not yet present. When you pick the item, KIDEx writes the attribute's default verbatim as the new node's value — the same rule for every type.

Boolean defaults — the inverse convention

The second argument of [YamlNode] means, uniformly for every type, "the value that Add writes". For strings, enums and integers this is the real default and acts as an editable starter (e.g. add Font-Size: 12px, then change it). But a Boolean has only two values, so writing the default would create a node identical to the convention — a guaranteed no-op (you never add a node just to keep the default). Therefore Boolean properties declare the inverse of their runtime default, so that "Add" always writes the meaningful, behaviour-changing value:

NodeRuntime default (when absent)[YamlNode] default"Add" writes
IsVisibleTrue'False'IsVisible: False (hide)
PreventAddingFalse'True'PreventAdding: True (hide Add button)
IsKeyFalse'True'IsKey: True
HttpOnlyTrue (recommended)'False'HttpOnly: False (deliberate opt-out)

This keeps the KIDEx "Add" logic a single type-agnostic line and makes the attribute the only source of truth for what the menu produces. The runtime is unaffected — attributes are pure metadata; getters keep their own GetBoolean('X', <default>) fallback. A handful of Boolean nodes whose runtime default is computed (e.g. AutoOpen/PagingTools, default not IsLarge/IsLarge, or UserSelection) are already declared inverted and need no further change.

YamlRequiredNode — Required scalar properties

Inherits from YamlNode. Used for properties that must be present (e.g. ModelName, Model).

pascal
[YamlRequiredNode('ModelName', 'Unique model identifier')]
property ModelName: string read GetModelName;

Required nodes appear first in the popup menu.

YamlContainer — Collection of N children

Marks a property as a container of homogeneous children (e.g. Fields, Rules). In KIDEx, containers are always visible in the menu with "Add {NodePath} child".

pascal
[YamlContainer('Fields', TKModelField, 'Data fields of this model')]
property Fields: TKModelFields read GetFields;

[YamlContainer('Rules', TKRule, 'Business rules applied to this field')]
property Rules: TKRules read GetRules;

YamlSubNode — Single config block

Marks a property as a sub-object with a fixed set of properties. KIDEx navigates into the sub-node class via RTTI to discover its own properties.

pascal
[YamlSubNode('HTMLEditor', TKHTMLEditorConfig, 'Rich-text editor toolbar options')]
property HTMLEditor: TKHTMLEditorConfig read GetHTMLEditor;

[YamlSubNode('Controller', TKViewTableControllerConfig, 'List/form controller settings')]
property ControllerConfig: TKViewTableControllerConfig read GetControllerConfig;

SubNode menu items appear only if the node is not already present: "Add {NodePath}".

YamlChildType — Container child types

Applied to container classes (not properties) to list the types of children that can be added. Multiple attributes can be applied to the same class.

pascal
[YamlChildType('StringField', 'String(10)', 'String field with max length')]
[YamlChildType('IntegerField', 'Integer', 'Integer numeric field')]
[YamlChildType('DateField', 'Date', 'Date field')]
TKModelFields = class(TKMetadataItem)

In KIDEx, child type entries always appear in the menu as "Add {Name}: {DefaultValue}" or "Add {Name}".

YamlEnumValue — Enum value mapping

Applied to enumerated types to map ordinal values to YAML strings. Used by KIDEx to populate combo box drop-downs.

pascal
[YamlEnumValue('Top', 'Label above the field')]
[YamlEnumValue('Left', 'Label to the left, left-aligned')]
[YamlEnumValue('Right', 'Label to the left, right-aligned')]
TKLabelAlign = (laTop, laLeft, laRight);

How KIDEx resolves the class for a node

When you select a node in the KIDEx tree editor, KIDEx determines which framework class to query for RTTI:

Node typeResolution
TKModelField, TKViewField, TKViewTable, TKDataView, TKLayout, TKModel, TKViewDirect class match
TKModelFields, TKRules, TKViewFields, TKViewTablesDirect class match (container classes)
TKTreeView, TKTreeViewFolder, TKTreeViewNodeDirect class match
TEFComponentConfigMaps to TKConfig
TEFNode with name ending in "Controller"Resolved via the controller registry
Any other TEFNodeAutomatic: walks up to the parent, reads its [YamlSubNode] annotations, and returns the matching sub-node class

The automatic parent-based resolution means that adding a [YamlSubNode] annotation to a parent class is sufficient to make KIDEx discover the sub-node's properties — no hardcoded mapping needed.

KIDEx popup menu behavior

When you right-click a node, KIDEx:

  1. Resolves the framework class for the node
  2. Reads its property annotations
  3. Reads its child-type annotations
  4. If either is present, builds the menu from RTTI:
    • Required scalars appear first
    • Scalar/SubNode items appear only if not already present
    • Container items always appear (can add multiple children)
    • ChildType items always appear (can add multiple instances)
  5. The generic "Add Child" action is hidden when RTTI is active
  6. "Delete Node" is always available

Annotated classes overview

Metadata classes

ClassFileScalarsContainersSubNodesChildTypes
TKModelKitto.Metadata.Models.pas111 (Rules)
TKModelFieldKitto.Metadata.Models.pas271 (Rules)3 (HTMLEditor, Thumbnail, PreviewWindow)
TKModelFieldsKitto.Metadata.Models.pas14 field types
TKRulesKitto.Metadata.Models.pas13 rule types
TKViewKitto.Metadata.Views.pas3
TKDataViewKitto.Metadata.DataView.pas1 (MainTable)
TKViewTableKitto.Metadata.DataView.pas91 (Rules)1 (Controller)
TKViewFieldKitto.Metadata.DataView.pas261 (Rules)
TKLayoutKitto.Metadata.Views.pas94 (Field, FieldSet, Row, Pagebreak)
TKConfigKitto.Config.pas86 (Server, Auth, AccessControl, UserFormats, LogTextFile, Theme)

Controller classes

ClassFileOwn scalarsInherited from
TKXPanelControllerBaseKitto.Html.Panel.pas10 (Title, Width, Height, ...)
TKXDataPanelControllerKitto.Html.DataPanel.pas5 (PreventAdding, ...)Panel
TKXListPanelControllerKitto.Html.List.pasDataPanel
TKXFormPanelControllerKitto.Html.Form.pas1 (Operation)Panel
TKXTabPanelControllerKitto.Html.TabPanel.pas2 (TabIconsVisible, TabsVisible)Panel
TKXTilePanelControllerKitto.Html.TilePanel.pas6 (TileWidth, ...)Panel
TKXHtmlPanelControllerKitto.Html.HtmlPanel.pas2 (Html, FileName)Panel
TKXLoginPanelControllerKitto.Html.Login.pas3 (ExtraWidth, ExtraHeight, LabelWidth)
TKXChartPanelControllerKitto.Html.ChartPanel.pasDataPanel + Chart SubNode

Tool controller classes

ClassFileOwn scalars
TKXToolControllerKitto.Html.Tools.pas1 (DisplayLabel)
TKXDownloadFileControllerKitto.Html.Files.pas4 (FileName, ClientFileName, ContentType, PersistentFileName)
TKXUploadFileControllerKitto.Html.Files.pas4 (Path, AcceptedWildcards, ContentType, MaxUploadSize)
TExportTextToolControllerKitto.Tool.Export.pas5 (IncludeHeader, FixedLength, Delimiter, QuoteChar, UseDisplayLabels)
TExportExcelToolControllerKitto.Tool.ADO.pas3 (ExcelRangeName, TemplateFileName, UseDisplayLabels)
TFOPToolControllerKitto.Tool.FOP.pas1 (TransformFileName)
TXSLToolControllerKitto.Tool.XSL.pas1 (TransformFileName)

Sub-node config classes

Defined in Kitto.Metadata.SubNodes.pas and Kitto.Metadata.SubNodes2.pas:

ClassPurposeProperties
TKHTMLEditorConfigRich-text toolbar options8 boolean flags
TKThumbnailConfigPicture thumbnail dimensionsWidth, Height
TKPreviewWindowConfigFile preview popup dimensionsWidth, Height
TKFilterPanelConfigFilter panel settingsDisplayLabel, LabelWidth, Connector, Collapsed, ColumnWidth, LabelAlign
TKServerConfigHTTP server settingsPort, SessionTimeOut, ThreadPoolSize
TKAuthConfigAuthentication7 scalars + Defaults SubNode
TKViewTableControllerConfigViewTable controller9 scalars + ToolViews, PopupWindow, Grouping, FormController SubNodes
TKToolViewsConfigTool views container13 child types (DownloadCSV, UploadFile, ...)
TKToolViewItemSingle tool view itemDisplayLabel, ImageName, Controller, RequireSelection, AutoRefresh, ConfirmationMessage
TKChartConfigChart configuration5 scalars + Legend, Series, Axes
TKGroupingConfigRow grouping5 scalars + ShowCount SubNode
TKThemeConfigColour themeMode/IconStyle/IconSize (enum), UserSelection, Font-Family, Font-Size + Light, Dark SubNodes
TKThemeModeConfigPer-mode palette (Light/Dark)Primary-Color

How to annotate a new class

When adding a new controller or metadata class:

  1. Add EF.YAML.Attributes to the interface uses clause
  2. Add {$RTTI EXPLICIT PROPERTIES([vcPublic])} before the class declaration
  3. For each config property, add the appropriate attribute:
pascal
{$RTTI EXPLICIT PROPERTIES([vcPublic])}
TMyController = class(TKXPanelControllerBase)
private
  function GetMyOption: Boolean;
public
  // MyOption defaults to False at runtime, so declare 'True' (the inverse):
  // "Add MyOption" then writes the meaningful value. See "Boolean defaults".
  [YamlNode('MyOption', 'True', 'Enable custom option')]
  property MyOption: Boolean read GetMyOption;
end;
  1. If the class is a container, add [YamlChildType] attributes before the class
  2. If the class has sub-node children, add [YamlSubNode] properties pointing to the sub-node class
  3. KIDEx will automatically discover the new properties via RTTI — no template files needed

TIP

All default values in [YamlNode] must be strings, even for Boolean or Integer properties. Use 'True', 'False', '100' — not bare True, False, 100. This is because Delphi RTTI does not support Variant parameters in attribute constructors.

TIP

For Boolean properties, declare the inverse of the runtime default (see Boolean defaults — the inverse convention above) so that the KIDEx "Add" menu writes the meaningful value rather than a no-op.

Released under Apache License, Version 2.0.