Add BitBtn, SpeedButton, TabSet, Notebook, TabbedNotebook, MaskEdit, Outline, Bevel, Header, and ScrollBox control types

Completes the Delphi 1.0 Standard, Additional, and Win31 component
palettes. DFM parser maps Tabs/Lines/Pages/Sections.Strings to Items
and TabIndex/PageIndex to ItemIndex. Kind extended with bk* idents
for BitBtn.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Duensing 2026-03-05 15:13:59 -06:00
parent 03d44440fd
commit dd115d3727
4 changed files with 651 additions and 29 deletions

View file

@ -326,6 +326,16 @@ messages are available, dispatching each command as it arrives.
| `PopupMenu` | TPopupMenu | Context (right-click) menu |
| `MenuItem` | TMenuItem | Menu item (child of menu) |
| `RadioGroup` | TRadioGroup | Grouped radio buttons |
| `BitBtn` | TBitBtn | Button with bitmap glyph |
| `SpeedButton` | TSpeedButton | Flat toolbar-style button |
| `TabSet` | TTabSet | Tab strip (no pages) |
| `Notebook` | TNotebook | Multi-page container |
| `TabbedNotebook` | TTabbedNotebook | Tabbed multi-page container |
| `MaskEdit` | TMaskEdit | Masked text input |
| `Outline` | TOutline | Hierarchical tree list |
| `Bevel` | TBevel | Cosmetic beveled line/box |
| `Header` | THeader | Column header bar |
| `ScrollBox` | TScrollBox | Scrollable container |
### Creating Controls
@ -363,16 +373,16 @@ CTRL.SET 1 3 Text="world" Enabled=0
### Caption
- **Applies to:** Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup
- **Applies to:** Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup, BitBtn, SpeedButton
- **Format:** Quoted string
- **Example:** `Caption="Submit"`
The display text for labels, buttons, check boxes, group boxes, radio
buttons, panels, menu items, and radio groups.
buttons, panels, menu items, radio groups, and bitmap/speed buttons.
### Text
- **Applies to:** Edit, ComboBox, Memo
- **Applies to:** Edit, ComboBox, Memo, MaskEdit
- **Format:** Quoted string
- **Example:** `Text="Hello world"`
@ -385,7 +395,7 @@ CTRL.SET 1 5 Text="Line one\nLine two\nLine three"
### Items
- **Applies to:** ListBox, ComboBox, RadioGroup
- **Applies to:** ListBox, ComboBox, RadioGroup, TabSet, Notebook, TabbedNotebook, Outline, Header
- **Format:** Quoted string, items separated by `\n`
- **Example:** `Items="Red\nGreen\nBlue"`
@ -412,7 +422,7 @@ new items are added.
### MaxLength
- **Applies to:** Edit
- **Applies to:** Edit, MaskEdit
- **Format:** Integer (0 = no limit)
- **Example:** `MaxLength=50`
@ -437,7 +447,7 @@ Maximum number of characters the user can type.
### ItemIndex
- **Applies to:** ListBox, ComboBox, RadioGroup
- **Applies to:** ListBox, ComboBox, RadioGroup, TabSet, Notebook, TabbedNotebook
- **Format:** Integer (-1 = no selection)
- **Example:** `ItemIndex=2`
@ -514,8 +524,12 @@ runtime via `CTRL.SET` or by manually editing the `.form` file.
### Kind
- **Applies to:** ScrollBar
- **Format:** `0` (sbHorizontal) or `1` (sbVertical)
- **Applies to:** ScrollBar, BitBtn
- **Format:** Integer
- **Values (ScrollBar):** `0` (sbHorizontal), `1` (sbVertical)
- **Values (BitBtn):** `0` (bkCustom), `1` (bkOK), `2` (bkCancel),
`3` (bkHelp), `4` (bkYes), `5` (bkNo), `6` (bkClose), `7` (bkAbort),
`8` (bkRetry), `9` (bkIgnore), `10` (bkAll)
- **Example:** `Kind=1`
### Min
@ -627,6 +641,93 @@ encoding (virtual key + modifier flags).
Associates a PopupMenu with a control. When the user right-clicks
the control, the popup menu is displayed.
### Layout
- **Applies to:** BitBtn, SpeedButton
- **Format:** Integer 0-3
- **Values:**
- `0` — blGlyphLeft
- `1` — blGlyphRight
- `2` — blGlyphTop
- `3` — blGlyphBottom
- **Example:** `Layout=2`
Position of the glyph relative to the caption text.
### NumGlyphs
- **Applies to:** BitBtn, SpeedButton
- **Format:** Integer (1-4)
- **Example:** `NumGlyphs=2`
Number of glyph images in the bitmap (up, disabled, clicked, down).
### GroupIndex
- **Applies to:** SpeedButton
- **Format:** Integer (0 = no group)
- **Example:** `GroupIndex=1`
Speed buttons with the same non-zero GroupIndex act as a radio group.
### Down
- **Applies to:** SpeedButton
- **Format:** `0` (up) or `1` (down)
- **Example:** `Down=1`
Whether the speed button is pressed. Only meaningful when
GroupIndex is non-zero.
### AllowAllUp
- **Applies to:** SpeedButton
- **Format:** `0` (off) or `1` (on)
- **Example:** `AllowAllUp=1`
When enabled, all speed buttons in a group can be unpressed.
### EditMask
- **Applies to:** MaskEdit
- **Format:** Quoted string
- **Example:** `EditMask="(999) 000-0000;1;_"`
Input mask string (mask;save literals;blank char).
### OutlineStyle
- **Applies to:** Outline
- **Format:** Integer 0-6
- **Values:**
- `0` — osText
- `1` — osPlusMinusText
- `2` — osPlusMinus
- `3` — osPictureText
- `4` — osPicturePlusMinusText
- `5` — osTreeText
- `6` — osTreePictureText
- **Example:** `OutlineStyle=5`
### Shape
- **Applies to:** Bevel
- **Format:** Integer 0-5
- **Values:**
- `0` — bsBox
- `1` — bsFrame
- `2` — bsTopLine
- `3` — bsBottomLine
- `4` — bsLeftLine
- `5` — bsRightLine
- **Example:** `Shape=2`
### Style (Bevel)
- **Applies to:** Bevel
- **Format:** `0` (bsLowered) or `1` (bsRaised)
- **Example:** `Style=1`
### BasePath
`BasePath` is a property on `TFormClient` (not a protocol property).
@ -660,6 +761,11 @@ No `EVENT.BIND` command is needed.
| ComboBox | Change | `"new text"` |
| MenuItem | Click | (none) |
| RadioGroup | Click | `<itemIndex>` |
| BitBtn | Click | (none) |
| SpeedButton | Click | (none) |
| TabSet | Change | `<tabIndex>` |
| TabbedNotebook | Change | `<pageIndex>` |
| MaskEdit | Change | `"new text"` |
### Opt-in Events

View file

@ -36,7 +36,17 @@ typedef enum {
ctMainMenu,
ctPopupMenu,
ctMenuItem,
ctRadioGroup
ctRadioGroup,
ctBitBtn,
ctSpeedButton,
ctTabSet,
ctNotebook,
ctTabbedNotebook,
ctMaskEdit,
ctOutline,
ctBevel,
ctHeader,
ctScrollBox
} CtrlTypeE;
typedef struct {
@ -110,8 +120,26 @@ typedef struct {
int32_t parentCtrlIdx;
int32_t columns;
int32_t shortCut;
int32_t layout;
int32_t numGlyphs;
int32_t groupIndex;
int32_t down;
int32_t allowAllUp;
int32_t outlineStyle;
int32_t shape;
int32_t style;
char editMask[256];
bool hasColumns;
bool hasShortCut;
bool hasLayout;
bool hasNumGlyphs;
bool hasGroupIndex;
bool hasDown;
bool hasAllowAllUp;
bool hasOutlineStyle;
bool hasShape;
bool hasStyle;
bool hasEditMask;
} DfmCtrlT;
typedef struct {
@ -381,6 +409,36 @@ static CtrlTypeE mapClassName(const char *className) {
if (stricmp_local(className, "TRadioGroup") == 0) {
return ctRadioGroup;
}
if (stricmp_local(className, "TBitBtn") == 0) {
return ctBitBtn;
}
if (stricmp_local(className, "TSpeedButton") == 0) {
return ctSpeedButton;
}
if (stricmp_local(className, "TTabSet") == 0) {
return ctTabSet;
}
if (stricmp_local(className, "TNotebook") == 0) {
return ctNotebook;
}
if (stricmp_local(className, "TTabbedNotebook") == 0) {
return ctTabbedNotebook;
}
if (stricmp_local(className, "TMaskEdit") == 0) {
return ctMaskEdit;
}
if (stricmp_local(className, "TOutline") == 0) {
return ctOutline;
}
if (stricmp_local(className, "TBevel") == 0) {
return ctBevel;
}
if (stricmp_local(className, "THeader") == 0) {
return ctHeader;
}
if (stricmp_local(className, "TScrollBox") == 0) {
return ctScrollBox;
}
return ctUnknown;
}
@ -484,7 +542,7 @@ static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, Dfm
skipValue(data, size, pos, tag);
}
ctrl->hasText = true;
} else if (!isForm && stricmp_local(propName, "Items.Strings") == 0 && tag == vaList) {
} else if (!isForm && (stricmp_local(propName, "Items.Strings") == 0 || stricmp_local(propName, "Tabs.Strings") == 0 || stricmp_local(propName, "Lines.Strings") == 0 || stricmp_local(propName, "Pages.Strings") == 0 || stricmp_local(propName, "Sections.Strings") == 0) && tag == vaList) {
// List of strings for ListBox/ComboBox
ctrl->items[0] = '\0';
int32_t itemsLen = 0;
@ -589,7 +647,7 @@ static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, Dfm
} else if (!isForm && stricmp_local(propName, "TabOrder") == 0) {
ctrl->tabOrder = readIntValue(data, size, pos, tag);
ctrl->hasTabOrder = true;
} else if (!isForm && stricmp_local(propName, "ItemIndex") == 0) {
} else if (!isForm && (stricmp_local(propName, "ItemIndex") == 0 || stricmp_local(propName, "TabIndex") == 0 || stricmp_local(propName, "PageIndex") == 0)) {
ctrl->itemIndex = readIntValue(data, size, pos, tag);
ctrl->hasItemIndex = true;
} else if (stricmp_local(propName, "OnClick") == 0) {
@ -753,6 +811,28 @@ static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, Dfm
ctrl->kind = 0;
} else if (stricmp_local(strBuf, "sbVertical") == 0) {
ctrl->kind = 1;
} else if (stricmp_local(strBuf, "bkCustom") == 0) {
ctrl->kind = 0;
} else if (stricmp_local(strBuf, "bkOK") == 0) {
ctrl->kind = 1;
} else if (stricmp_local(strBuf, "bkCancel") == 0) {
ctrl->kind = 2;
} else if (stricmp_local(strBuf, "bkHelp") == 0) {
ctrl->kind = 3;
} else if (stricmp_local(strBuf, "bkYes") == 0) {
ctrl->kind = 4;
} else if (stricmp_local(strBuf, "bkNo") == 0) {
ctrl->kind = 5;
} else if (stricmp_local(strBuf, "bkClose") == 0) {
ctrl->kind = 6;
} else if (stricmp_local(strBuf, "bkAbort") == 0) {
ctrl->kind = 7;
} else if (stricmp_local(strBuf, "bkRetry") == 0) {
ctrl->kind = 8;
} else if (stricmp_local(strBuf, "bkIgnore") == 0) {
ctrl->kind = 9;
} else if (stricmp_local(strBuf, "bkAll") == 0) {
ctrl->kind = 10;
} else {
ctrl->kind = 0;
}
@ -818,6 +898,121 @@ static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, Dfm
} else if (!isForm && stricmp_local(propName, "ShortCut") == 0) {
ctrl->shortCut = readIntValue(data, size, pos, tag);
ctrl->hasShortCut = true;
} else if (!isForm && stricmp_local(propName, "Layout") == 0) {
if (tag == vaIdent) {
readStr(data, size, pos, strBuf, sizeof(strBuf));
if (stricmp_local(strBuf, "blGlyphLeft") == 0) {
ctrl->layout = 0;
} else if (stricmp_local(strBuf, "blGlyphRight") == 0) {
ctrl->layout = 1;
} else if (stricmp_local(strBuf, "blGlyphTop") == 0) {
ctrl->layout = 2;
} else if (stricmp_local(strBuf, "blGlyphBottom") == 0) {
ctrl->layout = 3;
} else {
ctrl->layout = 0;
}
} else {
ctrl->layout = readIntValue(data, size, pos, tag);
}
ctrl->hasLayout = true;
} else if (!isForm && stricmp_local(propName, "NumGlyphs") == 0) {
ctrl->numGlyphs = readIntValue(data, size, pos, tag);
ctrl->hasNumGlyphs = true;
} else if (!isForm && stricmp_local(propName, "GroupIndex") == 0) {
ctrl->groupIndex = readIntValue(data, size, pos, tag);
ctrl->hasGroupIndex = true;
} else if (!isForm && stricmp_local(propName, "Down") == 0) {
if (tag == vaTrue) {
ctrl->down = 1;
} else if (tag == vaFalse) {
ctrl->down = 0;
} else {
ctrl->down = readIntValue(data, size, pos, tag);
}
ctrl->hasDown = true;
} else if (!isForm && stricmp_local(propName, "AllowAllUp") == 0) {
if (tag == vaTrue) {
ctrl->allowAllUp = 1;
} else if (tag == vaFalse) {
ctrl->allowAllUp = 0;
} else {
ctrl->allowAllUp = readIntValue(data, size, pos, tag);
}
ctrl->hasAllowAllUp = true;
} else if (!isForm && stricmp_local(propName, "EditMask") == 0) {
if (tag == vaString) {
readStr(data, size, pos, ctrl->editMask, sizeof(ctrl->editMask));
} else if (tag == vaLString) {
int32_t len = readInt32LE(data, size, pos);
int32_t copyLen = (len < (int32_t)sizeof(ctrl->editMask) - 1) ? len : (int32_t)sizeof(ctrl->editMask) - 1;
memcpy(ctrl->editMask, data + *pos, copyLen);
ctrl->editMask[copyLen] = '\0';
*pos += len;
} else {
skipValue(data, size, pos, tag);
}
ctrl->hasEditMask = true;
} else if (!isForm && stricmp_local(propName, "OutlineStyle") == 0) {
if (tag == vaIdent) {
readStr(data, size, pos, strBuf, sizeof(strBuf));
if (stricmp_local(strBuf, "osText") == 0) {
ctrl->outlineStyle = 0;
} else if (stricmp_local(strBuf, "osPlusMinusText") == 0) {
ctrl->outlineStyle = 1;
} else if (stricmp_local(strBuf, "osPlusMinus") == 0) {
ctrl->outlineStyle = 2;
} else if (stricmp_local(strBuf, "osPictureText") == 0) {
ctrl->outlineStyle = 3;
} else if (stricmp_local(strBuf, "osPicturePlusMinusText") == 0) {
ctrl->outlineStyle = 4;
} else if (stricmp_local(strBuf, "osTreeText") == 0) {
ctrl->outlineStyle = 5;
} else if (stricmp_local(strBuf, "osTreePictureText") == 0) {
ctrl->outlineStyle = 6;
} else {
ctrl->outlineStyle = 0;
}
} else {
ctrl->outlineStyle = readIntValue(data, size, pos, tag);
}
ctrl->hasOutlineStyle = true;
} else if (!isForm && stricmp_local(propName, "Shape") == 0) {
if (tag == vaIdent) {
readStr(data, size, pos, strBuf, sizeof(strBuf));
if (stricmp_local(strBuf, "bsBox") == 0) {
ctrl->shape = 0;
} else if (stricmp_local(strBuf, "bsFrame") == 0) {
ctrl->shape = 1;
} else if (stricmp_local(strBuf, "bsTopLine") == 0) {
ctrl->shape = 2;
} else if (stricmp_local(strBuf, "bsBottomLine") == 0) {
ctrl->shape = 3;
} else if (stricmp_local(strBuf, "bsLeftLine") == 0) {
ctrl->shape = 4;
} else if (stricmp_local(strBuf, "bsRightLine") == 0) {
ctrl->shape = 5;
} else {
ctrl->shape = 0;
}
} else {
ctrl->shape = readIntValue(data, size, pos, tag);
}
ctrl->hasShape = true;
} else if (!isForm && stricmp_local(propName, "Style") == 0) {
if (tag == vaIdent) {
readStr(data, size, pos, strBuf, sizeof(strBuf));
if (stricmp_local(strBuf, "bsLowered") == 0) {
ctrl->style = 0;
} else if (stricmp_local(strBuf, "bsRaised") == 0) {
ctrl->style = 1;
} else {
ctrl->style = 0;
}
} else {
ctrl->style = readIntValue(data, size, pos, tag);
}
ctrl->hasStyle = true;
} else {
skipValue(data, size, pos, tag);
}
@ -962,7 +1157,10 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
"CheckBox", "ListBox", "ComboBox", "Memo",
"Image", "GroupBox", "RadioButton", "Panel",
"ScrollBar", "MediaPlayer",
"MainMenu", "PopupMenu", "MenuItem", "RadioGroup"
"MainMenu", "PopupMenu", "MenuItem", "RadioGroup",
"BitBtn", "SpeedButton", "TabSet", "Notebook",
"TabbedNotebook", "MaskEdit", "Outline", "Bevel",
"Header", "ScrollBox"
};
char escaped[8192];
@ -1065,6 +1263,34 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
if (ctrl->hasShortCut && ctrl->shortCut != 0) {
fprintf(out, " ShortCut=%d", ctrl->shortCut);
}
if (ctrl->hasLayout && ctrl->layout != 0) {
fprintf(out, " Layout=%d", ctrl->layout);
}
if (ctrl->hasNumGlyphs && ctrl->numGlyphs > 1) {
fprintf(out, " NumGlyphs=%d", ctrl->numGlyphs);
}
if (ctrl->hasGroupIndex && ctrl->groupIndex != 0) {
fprintf(out, " GroupIndex=%d", ctrl->groupIndex);
}
if (ctrl->hasDown && ctrl->down) {
fprintf(out, " Down=1");
}
if (ctrl->hasAllowAllUp && ctrl->allowAllUp) {
fprintf(out, " AllowAllUp=1");
}
if (ctrl->hasEditMask && ctrl->editMask[0] != '\0') {
escapeStr(ctrl->editMask, escaped, sizeof(escaped));
fprintf(out, " EditMask=\"%s\"", escaped);
}
if (ctrl->hasOutlineStyle && ctrl->outlineStyle != 0) {
fprintf(out, " OutlineStyle=%d", ctrl->outlineStyle);
}
if (ctrl->hasShape) {
fprintf(out, " Shape=%d", ctrl->shape);
}
if (ctrl->hasStyle && ctrl->style != 0) {
fprintf(out, " Style=%d", ctrl->style);
}
fprintf(out, "\n");
@ -1072,8 +1298,8 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
// Auto-wired: Button/CheckBox→Click, Edit→Change, ListBox→Select,
// ComboBox→Select+Change, Memo→Change
bool autoClick = (ctrl->type == ctButton || ctrl->type == ctCheckBox || ctrl->type == ctRadioButton || ctrl->type == ctMenuItem || ctrl->type == ctRadioGroup);
bool autoChange = (ctrl->type == ctEdit || ctrl->type == ctComboBox || ctrl->type == ctMemo || ctrl->type == ctScrollBar);
bool autoClick = (ctrl->type == ctButton || ctrl->type == ctCheckBox || ctrl->type == ctRadioButton || ctrl->type == ctMenuItem || ctrl->type == ctRadioGroup || ctrl->type == ctBitBtn || ctrl->type == ctSpeedButton);
bool autoChange = (ctrl->type == ctEdit || ctrl->type == ctComboBox || ctrl->type == ctMemo || ctrl->type == ctScrollBar || ctrl->type == ctTabSet || ctrl->type == ctTabbedNotebook || ctrl->type == ctMaskEdit);
bool autoSelect = (ctrl->type == ctListBox || ctrl->type == ctComboBox);
if (ctrl->hasOnClick && !autoClick) {

View file

@ -13,7 +13,8 @@ unit FormCli;
interface
uses
SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, Menus, MPlayer, WinTypes, WinProcs;
SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, Menus, Buttons, Tabs,
TabNotBk, Mask, Outline, MPlayer, WinTypes, WinProcs;
const
MaxMsgLen = 4096;
@ -30,7 +31,10 @@ type
ctCheckBox, ctListBox, ctComboBox, ctMemo,
ctImage, ctGroupBox, ctRadioButton, ctPanel,
ctScrollBar, ctMediaPlayer,
ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup);
ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup,
ctBitBtn, ctSpeedButton, ctTabSet, ctNotebook,
ctTabbedNotebook, ctMaskEdit, ctOutline, ctBevel,
ctHeader, ctScrollBox);
{ Bound event flags }
TBoundEvent = (beDblClick, beKeyDown, beKeyUp,
@ -111,8 +115,11 @@ type
procedure HandleRadioButtonClick(Sender: TObject);
procedure HandleScrollBarChange(Sender: TObject);
procedure HandleMenuItemClick(Sender: TObject);
procedure HandleMaskEditChange(Sender: TObject);
procedure HandleMediaPlayerNotify(Sender: TObject);
procedure HandleRadioGroupClick(Sender: TObject);
procedure HandleTabSetChange(Sender: TObject);
procedure HandleTabbedNotebookChange(Sender: TObject);
{ Outgoing event helpers }
procedure SendEvent(FormId, CtrlId: Integer; const EventName: string; const Data: string);
@ -726,6 +733,50 @@ begin
end;
procedure TFormClient.HandleTabSetChange(Sender: TObject);
var
Tag: Longint;
FormId: Integer;
CtrlId: Integer;
begin
Tag := (Sender as TComponent).Tag;
FormId := Tag shr 16;
CtrlId := Tag and $FFFF;
SendEvent(FormId, CtrlId, 'Change', IntToStr((Sender as TTabSet).TabIndex));
end;
procedure TFormClient.HandleTabbedNotebookChange(Sender: TObject);
var
Tag: Longint;
FormId: Integer;
CtrlId: Integer;
begin
Tag := (Sender as TComponent).Tag;
FormId := Tag shr 16;
CtrlId := Tag and $FFFF;
SendEvent(FormId, CtrlId, 'Change', IntToStr((Sender as TTabbedNotebook).PageIndex));
end;
procedure TFormClient.HandleMaskEditChange(Sender: TObject);
var
Tag: Longint;
FormId: Integer;
CtrlId: Integer;
Escaped: array[0..4095] of Char;
Txt: string;
begin
Tag := (Sender as TComponent).Tag;
FormId := Tag shr 16;
CtrlId := Tag and $FFFF;
Txt := (Sender as TMaskEdit).Text;
StrPCopy(FTmpBuf, Txt);
EscapeString(FTmpBuf, Escaped, SizeOf(Escaped));
SendEvent(FormId, CtrlId, 'Change', '"' + StrPas(Escaped) + '"');
end;
procedure TFormClient.HandleFormClose(Sender: TObject;
var Action: TCloseAction);
var
@ -767,6 +818,16 @@ begin
(CR^.Control as TMenuItem).OnClick := HandleMenuItemClick;
ctRadioGroup:
(CR^.Control as TRadioGroup).OnClick := HandleRadioGroupClick;
ctBitBtn:
(CR^.Control as TBitBtn).OnClick := HandleButtonClick;
ctSpeedButton:
(CR^.Control as TSpeedButton).OnClick := HandleButtonClick;
ctTabSet:
(CR^.Control as TTabSet).OnChange := HandleTabSetChange;
ctTabbedNotebook:
(CR^.Control as TTabbedNotebook).OnChange := HandleTabbedNotebookChange;
ctMaskEdit:
(CR^.Control as TMaskEdit).OnChange := HandleMaskEditChange;
end;
end;
@ -935,14 +996,17 @@ begin
ctPanel: (CR^.Control as TPanel).Caption := StrPas(Unesc);
ctMenuItem: (CR^.Control as TMenuItem).Caption := StrPas(Unesc);
ctRadioGroup: (CR^.Control as TRadioGroup).Caption := StrPas(Unesc);
ctBitBtn: (CR^.Control as TBitBtn).Caption := StrPas(Unesc);
ctSpeedButton: (CR^.Control as TSpeedButton).Caption := StrPas(Unesc);
end;
end
else if S = 'Text' then
begin
UnescapeString(Value, Unesc, SizeOf(Unesc));
case CR^.CtrlType of
ctEdit: (CR^.Control as TEdit).Text := StrPas(Unesc);
ctEdit: (CR^.Control as TEdit).Text := StrPas(Unesc);
ctComboBox: (CR^.Control as TComboBox).Text := StrPas(Unesc);
ctMaskEdit: (CR^.Control as TMaskEdit).Text := StrPas(Unesc);
ctMemo:
begin
{ Convert \n to Lines }
@ -1043,6 +1107,106 @@ begin
if Start^ <> #0 then
(CR^.Control as TRadioGroup).Items.Add(StrPas(Start));
end;
ctTabSet:
begin
(CR^.Control as TTabSet).Tabs.Clear;
P := Unesc;
Start := P;
while P^ <> #0 do
begin
if P^ = #10 then
begin
P^ := #0;
(CR^.Control as TTabSet).Tabs.Add(StrPas(Start));
Inc(P);
Start := P;
end
else
Inc(P);
end;
if Start^ <> #0 then
(CR^.Control as TTabSet).Tabs.Add(StrPas(Start));
end;
ctNotebook:
begin
(CR^.Control as TNotebook).Pages.Clear;
P := Unesc;
Start := P;
while P^ <> #0 do
begin
if P^ = #10 then
begin
P^ := #0;
(CR^.Control as TNotebook).Pages.Add(StrPas(Start));
Inc(P);
Start := P;
end
else
Inc(P);
end;
if Start^ <> #0 then
(CR^.Control as TNotebook).Pages.Add(StrPas(Start));
end;
ctTabbedNotebook:
begin
(CR^.Control as TTabbedNotebook).Pages.Clear;
P := Unesc;
Start := P;
while P^ <> #0 do
begin
if P^ = #10 then
begin
P^ := #0;
(CR^.Control as TTabbedNotebook).Pages.Add(StrPas(Start));
Inc(P);
Start := P;
end
else
Inc(P);
end;
if Start^ <> #0 then
(CR^.Control as TTabbedNotebook).Pages.Add(StrPas(Start));
end;
ctOutline:
begin
(CR^.Control as TOutline).Lines.Clear;
P := Unesc;
Start := P;
while P^ <> #0 do
begin
if P^ = #10 then
begin
P^ := #0;
(CR^.Control as TOutline).Lines.Add(StrPas(Start));
Inc(P);
Start := P;
end
else
Inc(P);
end;
if Start^ <> #0 then
(CR^.Control as TOutline).Lines.Add(StrPas(Start));
end;
ctHeader:
begin
(CR^.Control as THeader).Sections.Clear;
P := Unesc;
Start := P;
while P^ <> #0 do
begin
if P^ = #10 then
begin
P^ := #0;
(CR^.Control as THeader).Sections.Add(StrPas(Start));
Inc(P);
Start := P;
end
else
Inc(P);
end;
if Start^ <> #0 then
(CR^.Control as THeader).Sections.Add(StrPas(Start));
end;
end;
end
else if S = 'Checked' then
@ -1075,7 +1239,9 @@ begin
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctEdit then
(CR^.Control as TEdit).MaxLength := N;
(CR^.Control as TEdit).MaxLength := N
else if CR^.CtrlType = ctMaskEdit then
(CR^.Control as TMaskEdit).MaxLength := N;
end
else if S = 'ReadOnly' then
begin
@ -1101,9 +1267,12 @@ begin
begin
N := StrToIntDef(StrPas(Value), -1);
case CR^.CtrlType of
ctListBox: (CR^.Control as TListBox).ItemIndex := N;
ctComboBox: (CR^.Control as TComboBox).ItemIndex := N;
ctRadioGroup: (CR^.Control as TRadioGroup).ItemIndex := N;
ctListBox: (CR^.Control as TListBox).ItemIndex := N;
ctComboBox: (CR^.Control as TComboBox).ItemIndex := N;
ctRadioGroup: (CR^.Control as TRadioGroup).ItemIndex := N;
ctTabSet: (CR^.Control as TTabSet).TabIndex := N;
ctNotebook: (CR^.Control as TNotebook).PageIndex := N;
ctTabbedNotebook: (CR^.Control as TTabbedNotebook).PageIndex := N;
end;
end
else if S = 'Stretch' then
@ -1160,7 +1329,9 @@ begin
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctScrollBar then
(CR^.Control as TScrollBar).Kind := TScrollBarKind(N);
(CR^.Control as TScrollBar).Kind := TScrollBarKind(N)
else if CR^.CtrlType = ctBitBtn then
(CR^.Control as TBitBtn).Kind := TBitBtnKind(N);
end
else if S = 'Min' then
begin
@ -1307,6 +1478,66 @@ begin
PCR := FindCtrl(FR, N);
if (PCR <> nil) and (PCR^.Control is TPopupMenu) and (CR^.Control is TControl) then
(CR^.Control as TControl).PopupMenu := PCR^.Control as TPopupMenu;
end
else if S = 'Layout' then
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctBitBtn then
(CR^.Control as TBitBtn).Layout := TButtonLayout(N)
else if CR^.CtrlType = ctSpeedButton then
(CR^.Control as TSpeedButton).Layout := TButtonLayout(N);
end
else if S = 'NumGlyphs' then
begin
N := StrToIntDef(StrPas(Value), 1);
if CR^.CtrlType = ctBitBtn then
(CR^.Control as TBitBtn).NumGlyphs := N
else if CR^.CtrlType = ctSpeedButton then
(CR^.Control as TSpeedButton).NumGlyphs := N;
end
else if S = 'GroupIndex' then
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctSpeedButton then
(CR^.Control as TSpeedButton).GroupIndex := N;
end
else if S = 'Down' then
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctSpeedButton then
(CR^.Control as TSpeedButton).Down := (N <> 0);
end
else if S = 'AllowAllUp' then
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctSpeedButton then
(CR^.Control as TSpeedButton).AllowAllUp := (N <> 0);
end
else if S = 'EditMask' then
begin
if CR^.CtrlType = ctMaskEdit then
begin
UnescapeString(Value, Unesc, SizeOf(Unesc));
(CR^.Control as TMaskEdit).EditMask := StrPas(Unesc);
end;
end
else if S = 'OutlineStyle' then
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctOutline then
(CR^.Control as TOutline).OutlineStyle := TOutlineStyle(N);
end
else if S = 'Shape' then
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctBevel then
(CR^.Control as TBevel).Shape := TBevelShape(N);
end
else if S = 'Style' then
begin
N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctBevel then
(CR^.Control as TBevel).Style := TBevelStyle(N);
end;
end;
@ -1405,6 +1636,26 @@ begin
Result := ctMenuItem
else if S = 'RadioGroup' then
Result := ctRadioGroup
else if S = 'BitBtn' then
Result := ctBitBtn
else if S = 'SpeedButton' then
Result := ctSpeedButton
else if S = 'TabSet' then
Result := ctTabSet
else if S = 'Notebook' then
Result := ctNotebook
else if S = 'TabbedNotebook' then
Result := ctTabbedNotebook
else if S = 'MaskEdit' then
Result := ctMaskEdit
else if S = 'Outline' then
Result := ctOutline
else if S = 'Bevel' then
Result := ctBevel
else if S = 'Header' then
Result := ctHeader
else if S = 'ScrollBox' then
Result := ctScrollBox
else
Result := ctUnknown;
end;
@ -1562,6 +1813,26 @@ begin
Comp := TMenuItem.Create(FR^.Form);
ctRadioGroup:
Comp := TRadioGroup.Create(FR^.Form);
ctBitBtn:
Comp := TBitBtn.Create(FR^.Form);
ctSpeedButton:
Comp := TSpeedButton.Create(FR^.Form);
ctTabSet:
Comp := TTabSet.Create(FR^.Form);
ctNotebook:
Comp := TNotebook.Create(FR^.Form);
ctTabbedNotebook:
Comp := TTabbedNotebook.Create(FR^.Form);
ctMaskEdit:
Comp := TMaskEdit.Create(FR^.Form);
ctOutline:
Comp := TOutline.Create(FR^.Form);
ctBevel:
Comp := TBevel.Create(FR^.Form);
ctHeader:
Comp := THeader.Create(FR^.Form);
ctScrollBox:
Comp := TScrollBox.Create(FR^.Form);
end;
if Comp = nil then

View file

@ -72,7 +72,7 @@ Event data varies by event type:
|-----------|-------------------------------|
| Click | (none), or `<itemIndex>` (RadioGroup) |
| DblClick | (none) |
| Change | `"new text"` or `<position>` (ScrollBar) |
| Change | `"new text"`, `<position>` (ScrollBar), or `<index>` (TabSet, TabbedNotebook) |
| Select | `<index> "selected text"` |
| KeyDown | `<vkCode>` |
| KeyUp | `<vkCode>` |
@ -105,6 +105,16 @@ Event data varies by event type:
| PopupMenu | TPopupMenu | (none) |
| MenuItem | TMenuItem | Click |
| RadioGroup | TRadioGroup | Click |
| BitBtn | TBitBtn | Click |
| SpeedButton | TSpeedButton | Click |
| TabSet | TTabSet | Change |
| Notebook | TNotebook | (none) |
| TabbedNotebook | TTabbedNotebook | Change |
| MaskEdit | TMaskEdit | Change |
| Outline | TOutline | (none) |
| Bevel | TBevel | (none) |
| Header | THeader | (none) |
| ScrollBox | TScrollBox | (none) |
MainMenu and PopupMenu use `0 0 0 0` geometry (non-visual). MenuItem
uses `0 0 0 0` geometry and requires a `Parent` property to specify its
@ -123,16 +133,16 @@ not support opt-in events.
| Property | Applies To | Value Format |
|-------------|---------------------------------------------|-------------------------------------------|
| Caption | Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup | Quoted string |
| Text | Edit, ComboBox, Memo | Quoted string (`\n` for line breaks) |
| Items | ListBox, ComboBox, RadioGroup | Quoted string (`\n`-delimited) |
| Caption | Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup, BitBtn, SpeedButton | Quoted string |
| Text | Edit, ComboBox, Memo, MaskEdit | Quoted string (`\n` for line breaks) |
| Items | ListBox, ComboBox, RadioGroup, TabSet, Notebook, TabbedNotebook, Outline, Header | Quoted string (`\n`-delimited) |
| Checked | CheckBox, RadioButton, MenuItem | 0 or 1 |
| Enabled | All | 0 or 1 |
| Visible | All | 0 or 1 |
| MaxLength | Edit | Integer |
| MaxLength | Edit, MaskEdit | Integer |
| ReadOnly | Edit, Memo | 0 or 1 |
| ScrollBars | Memo | 0-3 (ssNone..ssBoth) |
| ItemIndex | ListBox, ComboBox, RadioGroup | Integer (-1 = none) |
| ItemIndex | ListBox, ComboBox, RadioGroup, TabSet, Notebook, TabbedNotebook | Integer (-1 = none) |
| TabOrder | All windowed controls | Integer |
| Stretch | Image | 0 or 1 |
| Center | Image | 0 or 1 |
@ -141,7 +151,7 @@ not support opt-in events.
| BevelOuter | Panel | 0-2 (bvNone, bvLowered, bvRaised) |
| BevelInner | Panel | 0-2 (bvNone, bvLowered, bvRaised) |
| BorderStyle | Panel | 0-1 (bsNone, bsSingle) |
| Kind | ScrollBar | 0-1 (sbHorizontal, sbVertical) |
| Kind | ScrollBar, BitBtn | Integer (see below) |
| Min | ScrollBar | Integer |
| Max | ScrollBar | Integer |
| Position | ScrollBar | Integer |
@ -155,6 +165,15 @@ not support opt-in events.
| Columns | RadioGroup | Integer (number of columns) |
| ShortCut | MenuItem | Integer (Delphi ShortCut value) |
| PopupMenu | Any TControl | Integer (ctrlId of PopupMenu) |
| Layout | BitBtn, SpeedButton | 0-3 (blGlyphLeft..blGlyphBottom) |
| NumGlyphs | BitBtn, SpeedButton | Integer (1-4) |
| GroupIndex | SpeedButton | Integer (0 = no group) |
| Down | SpeedButton | 0 or 1 |
| AllowAllUp | SpeedButton | 0 or 1 |
| EditMask | MaskEdit | Quoted string |
| OutlineStyle| Outline | 0-6 (osText..osTreePictureText) |
| Shape | Bevel | 0-5 (bsBox..bsRightLine) |
| Style | Bevel | 0-1 (bsLowered, bsRaised) |
File path properties (Picture, FileName) are resolved relative to the
client's `BasePath` setting. Subdirectories are allowed (e.g.,