KIDEx YAML Attributes (RTTI-based property discovery)
The KIDEx visual editor discovers YAML metadata properties at runtime via Delphi RTTI attributes declared on the framework classes.
A node that carries no RTTI metadata is therefore not listed in the right-click menu. It is not out of reach: Add custom node, the last entry of that menu, adds a node you name yourself, and that is how you write a key the editor does not describe yet.
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.
// Without default value
[YamlNode('Expression', 'SQL expression for computed fields')]
property Expression: string read GetExpression;
// With default value (always a string).
// The default is the REAL runtime default, for every type including Boolean:
// IsVisible defaults to True at runtime, so the attribute declares 'True'.
// (The KIDEx "Add" menu flips booleans on its own — see "Boolean defaults".)
[YamlNode('IsVisible', 'True', '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.
Boolean defaults — annotation vs. menu
The default in [YamlNode] is the real runtime default — the value the framework assumes when the node is absent — for every type, booleans included. It is the single source of truth for "what does the framework do here by default?", and it is what lets the validator hint a node that merely repeats its default (see Validation is driven by the same annotations).
The KIDEx menu, however, would gain nothing by offering a boolean node set to its own default — the node would be a no-op. So for booleans only, and only when building the menu, KIDEx proposes the opposite of the declared default, so Add always writes the meaningful, behaviour-changing value. For strings, integers and enums there is no inversion: the declared default is written as an editable starter (add Font-Size: 12px, then change it).
| Node | [YamlNode] default (real runtime default) | KIDEx Add writes |
|---|---|---|
IsVisible | 'True' | IsVisible: False (hide) |
PreventAdding | 'False' | PreventAdding: True (hide Add button) |
IsKey | 'False' | IsKey: True |
The inversion is a single line inside KIDEx (ProposedScalarDefault), applied at menu-build time; the annotation on the class is never touched. The runtime is unaffected either — attributes are pure metadata, and getters keep their own GetBoolean('X', <default>) fallback. See the dedicated Metadata Validation page.
YamlRequiredNode — Required scalar properties
Inherits from YamlNode. Used for properties that must be present (e.g. ModelName, Model).
[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".
[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.
[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.
[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.
[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 type | Resolution |
|---|---|
TKModelField, TKViewField, TKViewTable, TKDataView, TKLayout, TKModel, TKView | Direct class match |
TKModelFields, TKRules, TKViewFields, TKViewTables | Direct class match (container classes) |
TKTreeView, TKTreeViewFolder, TKTreeViewNode | Direct class match |
TEFComponentConfig | Maps to TKConfig |
TEFNode with name ending in "Controller" | Resolved via the controller registry |
Any other TEFNode | Automatic: 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:
- Resolves the framework class for the node
- Reads its property annotations
- Reads its child-type annotations
- 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)
- The generic "Add Child" action is hidden when RTTI is active
- "Delete Node" is always available
Validation is driven by the same annotations
The metadata validator (Validate Metadata) reads the declarations on the classes themselves, the very ones documented above. Annotating a class therefore does two things at once — it fills the KIDEx menus, and it teaches the validator what a correct file looks like.
| Class-aware recursion | Sub-nodes and containers pass the right class down to their children, so nested attributes are validated too, and the names of container items are no longer mistaken for schema |
| Values, not just names | An enum is checked against the strings declared with [YamlEnumValue], and an integer node against being an integer |
| Default value = hint | A node whose value equals its declared default is reported as a hint: it is redundant and could be removed. For booleans the annotation holds the real default, so a value written through the (inverted) menu never matches and is never hinted |
| Macros are skipped | An empty value, or one containing a macro (%…%), is not checked: its real value is not known until runtime |
| Unknown node = warning | A node the classes do not declare is reported as a warning, not an error, in every build — a custom node is legitimate |
| Disabled nodes are skipped | A node prefixed with . is not explored: it is switched off on purpose |
Validate Metadata also runs the Layout validator. For the full picture — severities, the headless MCPKittoX --validate command, and how the tree menu relates to validation — see the dedicated Metadata Validation page.
If the validator reports a node you know is right
It is telling you the class does not declare it. Add the annotation — see How to annotate a new class — and both the menu and the validator learn it at once.
Annotated classes overview
Metadata classes
| Class | File | Scalars | Containers | SubNodes | ChildTypes |
|---|---|---|---|---|---|
TKModel | Kitto.Metadata.Models.pas | 11 | 1 (Rules) | ||
TKModelField | Kitto.Metadata.Models.pas | 27 | 1 (Rules) | 3 (HTMLEditor, Thumbnail, PreviewWindow) | |
TKModelFields | Kitto.Metadata.Models.pas | 14 field types | |||
TKRules | Kitto.Metadata.Models.pas | 13 rule types | |||
TKView | Kitto.Metadata.Views.pas | 3 | |||
TKDataView | Kitto.Metadata.DataView.pas | 1 (MainTable) | |||
TKViewTable | Kitto.Metadata.DataView.pas | 9 | 1 (Rules) | 1 (Controller) | |
TKViewField | Kitto.Metadata.DataView.pas | 26 | 1 (Rules) | ||
TKLayout | Kitto.Metadata.Views.pas | 9 | 4 (Field, FieldSet, Row, Pagebreak) | ||
TKConfig | Kitto.Config.pas | 8 | 6 (Server, Auth, AccessControl, UserFormats, LogTextFile, Theme) |
Controller classes
| Class | File | Own scalars | Inherited from |
|---|---|---|---|
TKXPanelControllerBase | Kitto.Html.Panel.pas | 10 (Title, Width, Height, ...) | |
TKXDataPanelController | Kitto.Html.DataPanel.pas | 5 (PreventAdding, ...) | Panel |
TKXListPanelController | Kitto.Html.List.pas | DataPanel | |
TKXFormPanelController | Kitto.Html.Form.pas | 1 (Operation) | Panel |
TKXTabPanelController | Kitto.Html.TabPanel.pas | 2 (TabIconsVisible, TabsVisible) | Panel |
TKXTilePanelController | Kitto.Html.TilePanel.pas | 6 (TileWidth, ...) | Panel |
TKXHtmlPanelController | Kitto.Html.HtmlPanel.pas | 2 (Html, FileName) | Panel |
TKXLoginPanelController | Kitto.Html.Login.pas | 3 (ExtraWidth, ExtraHeight, LabelWidth) | |
TKXChartPanelController | Kitto.Html.ChartPanel.pas | DataPanel + Chart SubNode |
Tool controller classes
| Class | File | Own scalars |
|---|---|---|
TKXToolController | Kitto.Html.Tools.pas | 1 (DisplayLabel) |
TKXDownloadFileController | Kitto.Html.Files.pas | 4 (FileName, ClientFileName, ContentType, PersistentFileName) |
TKXUploadFileController | Kitto.Html.Files.pas | 4 (Path, AcceptedWildcards, ContentType, MaxUploadSize) |
TExportTextToolController | Kitto.Tool.Export.pas | 5 (IncludeHeader, FixedLength, Delimiter, QuoteChar, UseDisplayLabels) |
TExportExcelToolController | Kitto.Tool.ADO.pas | 3 (ExcelRangeName, TemplateFileName, UseDisplayLabels) |
TFOPToolController | Kitto.Tool.FOP.pas | 1 (TransformFileName) |
TXSLToolController | Kitto.Tool.XSL.pas | 1 (TransformFileName) |
Sub-node config classes
Defined in Kitto.Metadata.SubNodes.pas and Kitto.Metadata.SubNodes2.pas:
| Class | Purpose | Properties |
|---|---|---|
TKHTMLEditorConfig | Rich-text toolbar options | 8 boolean flags |
TKThumbnailConfig | Picture thumbnail dimensions | Width, Height |
TKPreviewWindowConfig | File preview popup dimensions | Width, Height |
TKFilterPanelConfig | Filter panel settings | DisplayLabel, LabelWidth, Connector, Collapsed, ColumnWidth, LabelAlign |
TKServerConfig | HTTP server settings | Port, SessionTimeOut, ThreadPoolSize |
TKAuthConfig | Authentication | 7 scalars + Defaults SubNode |
TKViewTableControllerConfig | ViewTable controller | 9 scalars + ToolViews, PopupWindow, Grouping, FormController SubNodes |
TKToolViewsConfig | Tool views container | 13 child types (DownloadCSV, UploadFile, ...) |
TKToolViewItem | Single tool view item | DisplayLabel, ImageName, Controller, RequireSelection, AutoRefresh, ConfirmationMessage |
TKChartConfig | Chart configuration | 5 scalars + Legend, Series, Axes |
TKGroupingConfig | Row grouping | 5 scalars + ShowCount SubNode |
TKThemeConfig | Colour theme | Mode/IconStyle/IconSize (enum), UserSelection, Font-Family, Font-Size + Light, Dark SubNodes |
TKThemeModeConfig | Per-mode palette (Light/Dark) | Primary-Color |
How to annotate a new class
When adding a new controller or metadata class:
- Add
EF.YAML.Attributesto the interfaceusesclause - Add
{$RTTI EXPLICIT PROPERTIES([vcPublic])}before the class declaration - For each config property, add the appropriate attribute:
{$RTTI EXPLICIT PROPERTIES([vcPublic])}
TMyController = class(TKXPanelControllerBase)
private
function GetMyOption: Boolean;
public
// MyOption defaults to False at runtime, so declare 'False' (the real
// default). KIDEx's "Add" menu flips booleans on its own and will offer
// "MyOption: True" — the meaningful value. See "Boolean defaults".
[YamlNode('MyOption', 'False', 'Enable custom option')]
property MyOption: Boolean read GetMyOption;
end;- If the class is a container, add
[YamlChildType]attributes before the class - If the class has sub-node children, add
[YamlSubNode]properties pointing to the sub-node class - 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 real runtime default (see Boolean defaults — annotation vs. menu above). KIDEx flips booleans on its own when building the Add menu, so it still offers the meaningful value rather than a no-op — you do not invert the annotation yourself.
