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 | | `PopupMenu` | TPopupMenu | Context (right-click) menu |
| `MenuItem` | TMenuItem | Menu item (child of menu) | | `MenuItem` | TMenuItem | Menu item (child of menu) |
| `RadioGroup` | TRadioGroup | Grouped radio buttons | | `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 ### Creating Controls
@ -363,16 +373,16 @@ CTRL.SET 1 3 Text="world" Enabled=0
### Caption ### 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 - **Format:** Quoted string
- **Example:** `Caption="Submit"` - **Example:** `Caption="Submit"`
The display text for labels, buttons, check boxes, group boxes, radio 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 ### Text
- **Applies to:** Edit, ComboBox, Memo - **Applies to:** Edit, ComboBox, Memo, MaskEdit
- **Format:** Quoted string - **Format:** Quoted string
- **Example:** `Text="Hello world"` - **Example:** `Text="Hello world"`
@ -385,7 +395,7 @@ CTRL.SET 1 5 Text="Line one\nLine two\nLine three"
### Items ### Items
- **Applies to:** ListBox, ComboBox, RadioGroup - **Applies to:** ListBox, ComboBox, RadioGroup, TabSet, Notebook, TabbedNotebook, Outline, Header
- **Format:** Quoted string, items separated by `\n` - **Format:** Quoted string, items separated by `\n`
- **Example:** `Items="Red\nGreen\nBlue"` - **Example:** `Items="Red\nGreen\nBlue"`
@ -412,7 +422,7 @@ new items are added.
### MaxLength ### MaxLength
- **Applies to:** Edit - **Applies to:** Edit, MaskEdit
- **Format:** Integer (0 = no limit) - **Format:** Integer (0 = no limit)
- **Example:** `MaxLength=50` - **Example:** `MaxLength=50`
@ -437,7 +447,7 @@ Maximum number of characters the user can type.
### ItemIndex ### ItemIndex
- **Applies to:** ListBox, ComboBox, RadioGroup - **Applies to:** ListBox, ComboBox, RadioGroup, TabSet, Notebook, TabbedNotebook
- **Format:** Integer (-1 = no selection) - **Format:** Integer (-1 = no selection)
- **Example:** `ItemIndex=2` - **Example:** `ItemIndex=2`
@ -514,8 +524,12 @@ runtime via `CTRL.SET` or by manually editing the `.form` file.
### Kind ### Kind
- **Applies to:** ScrollBar - **Applies to:** ScrollBar, BitBtn
- **Format:** `0` (sbHorizontal) or `1` (sbVertical) - **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` - **Example:** `Kind=1`
### Min ### Min
@ -627,6 +641,93 @@ encoding (virtual key + modifier flags).
Associates a PopupMenu with a control. When the user right-clicks Associates a PopupMenu with a control. When the user right-clicks
the control, the popup menu is displayed. 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
`BasePath` is a property on `TFormClient` (not a protocol property). `BasePath` is a property on `TFormClient` (not a protocol property).
@ -660,6 +761,11 @@ No `EVENT.BIND` command is needed.
| ComboBox | Change | `"new text"` | | ComboBox | Change | `"new text"` |
| MenuItem | Click | (none) | | MenuItem | Click | (none) |
| RadioGroup | Click | `<itemIndex>` | | RadioGroup | Click | `<itemIndex>` |
| BitBtn | Click | (none) |
| SpeedButton | Click | (none) |
| TabSet | Change | `<tabIndex>` |
| TabbedNotebook | Change | `<pageIndex>` |
| MaskEdit | Change | `"new text"` |
### Opt-in Events ### Opt-in Events

View file

@ -36,7 +36,17 @@ typedef enum {
ctMainMenu, ctMainMenu,
ctPopupMenu, ctPopupMenu,
ctMenuItem, ctMenuItem,
ctRadioGroup ctRadioGroup,
ctBitBtn,
ctSpeedButton,
ctTabSet,
ctNotebook,
ctTabbedNotebook,
ctMaskEdit,
ctOutline,
ctBevel,
ctHeader,
ctScrollBox
} CtrlTypeE; } CtrlTypeE;
typedef struct { typedef struct {
@ -110,8 +120,26 @@ typedef struct {
int32_t parentCtrlIdx; int32_t parentCtrlIdx;
int32_t columns; int32_t columns;
int32_t shortCut; 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 hasColumns;
bool hasShortCut; bool hasShortCut;
bool hasLayout;
bool hasNumGlyphs;
bool hasGroupIndex;
bool hasDown;
bool hasAllowAllUp;
bool hasOutlineStyle;
bool hasShape;
bool hasStyle;
bool hasEditMask;
} DfmCtrlT; } DfmCtrlT;
typedef struct { typedef struct {
@ -381,6 +409,36 @@ static CtrlTypeE mapClassName(const char *className) {
if (stricmp_local(className, "TRadioGroup") == 0) { if (stricmp_local(className, "TRadioGroup") == 0) {
return ctRadioGroup; 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; 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); skipValue(data, size, pos, tag);
} }
ctrl->hasText = true; 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 // List of strings for ListBox/ComboBox
ctrl->items[0] = '\0'; ctrl->items[0] = '\0';
int32_t itemsLen = 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) { } else if (!isForm && stricmp_local(propName, "TabOrder") == 0) {
ctrl->tabOrder = readIntValue(data, size, pos, tag); ctrl->tabOrder = readIntValue(data, size, pos, tag);
ctrl->hasTabOrder = true; 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->itemIndex = readIntValue(data, size, pos, tag);
ctrl->hasItemIndex = true; ctrl->hasItemIndex = true;
} else if (stricmp_local(propName, "OnClick") == 0) { } 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; ctrl->kind = 0;
} else if (stricmp_local(strBuf, "sbVertical") == 0) { } else if (stricmp_local(strBuf, "sbVertical") == 0) {
ctrl->kind = 1; 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 { } else {
ctrl->kind = 0; 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) { } else if (!isForm && stricmp_local(propName, "ShortCut") == 0) {
ctrl->shortCut = readIntValue(data, size, pos, tag); ctrl->shortCut = readIntValue(data, size, pos, tag);
ctrl->hasShortCut = true; 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 { } else {
skipValue(data, size, pos, tag); 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", "CheckBox", "ListBox", "ComboBox", "Memo",
"Image", "GroupBox", "RadioButton", "Panel", "Image", "GroupBox", "RadioButton", "Panel",
"ScrollBar", "MediaPlayer", "ScrollBar", "MediaPlayer",
"MainMenu", "PopupMenu", "MenuItem", "RadioGroup" "MainMenu", "PopupMenu", "MenuItem", "RadioGroup",
"BitBtn", "SpeedButton", "TabSet", "Notebook",
"TabbedNotebook", "MaskEdit", "Outline", "Bevel",
"Header", "ScrollBox"
}; };
char escaped[8192]; 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) { if (ctrl->hasShortCut && ctrl->shortCut != 0) {
fprintf(out, " ShortCut=%d", ctrl->shortCut); 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"); 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, // Auto-wired: Button/CheckBox→Click, Edit→Change, ListBox→Select,
// ComboBox→Select+Change, Memo→Change // ComboBox→Select+Change, Memo→Change
bool autoClick = (ctrl->type == ctButton || ctrl->type == ctCheckBox || ctrl->type == ctRadioButton || ctrl->type == ctMenuItem || ctrl->type == ctRadioGroup); 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); 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); bool autoSelect = (ctrl->type == ctListBox || ctrl->type == ctComboBox);
if (ctrl->hasOnClick && !autoClick) { if (ctrl->hasOnClick && !autoClick) {

View file

@ -13,7 +13,8 @@ unit FormCli;
interface interface
uses 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 const
MaxMsgLen = 4096; MaxMsgLen = 4096;
@ -30,7 +31,10 @@ type
ctCheckBox, ctListBox, ctComboBox, ctMemo, ctCheckBox, ctListBox, ctComboBox, ctMemo,
ctImage, ctGroupBox, ctRadioButton, ctPanel, ctImage, ctGroupBox, ctRadioButton, ctPanel,
ctScrollBar, ctMediaPlayer, ctScrollBar, ctMediaPlayer,
ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup); ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup,
ctBitBtn, ctSpeedButton, ctTabSet, ctNotebook,
ctTabbedNotebook, ctMaskEdit, ctOutline, ctBevel,
ctHeader, ctScrollBox);
{ Bound event flags } { Bound event flags }
TBoundEvent = (beDblClick, beKeyDown, beKeyUp, TBoundEvent = (beDblClick, beKeyDown, beKeyUp,
@ -111,8 +115,11 @@ type
procedure HandleRadioButtonClick(Sender: TObject); procedure HandleRadioButtonClick(Sender: TObject);
procedure HandleScrollBarChange(Sender: TObject); procedure HandleScrollBarChange(Sender: TObject);
procedure HandleMenuItemClick(Sender: TObject); procedure HandleMenuItemClick(Sender: TObject);
procedure HandleMaskEditChange(Sender: TObject);
procedure HandleMediaPlayerNotify(Sender: TObject); procedure HandleMediaPlayerNotify(Sender: TObject);
procedure HandleRadioGroupClick(Sender: TObject); procedure HandleRadioGroupClick(Sender: TObject);
procedure HandleTabSetChange(Sender: TObject);
procedure HandleTabbedNotebookChange(Sender: TObject);
{ Outgoing event helpers } { Outgoing event helpers }
procedure SendEvent(FormId, CtrlId: Integer; const EventName: string; const Data: string); procedure SendEvent(FormId, CtrlId: Integer; const EventName: string; const Data: string);
@ -726,6 +733,50 @@ begin
end; 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; procedure TFormClient.HandleFormClose(Sender: TObject;
var Action: TCloseAction); var Action: TCloseAction);
var var
@ -767,6 +818,16 @@ begin
(CR^.Control as TMenuItem).OnClick := HandleMenuItemClick; (CR^.Control as TMenuItem).OnClick := HandleMenuItemClick;
ctRadioGroup: ctRadioGroup:
(CR^.Control as TRadioGroup).OnClick := HandleRadioGroupClick; (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;
end; end;
@ -935,14 +996,17 @@ begin
ctPanel: (CR^.Control as TPanel).Caption := StrPas(Unesc); ctPanel: (CR^.Control as TPanel).Caption := StrPas(Unesc);
ctMenuItem: (CR^.Control as TMenuItem).Caption := StrPas(Unesc); ctMenuItem: (CR^.Control as TMenuItem).Caption := StrPas(Unesc);
ctRadioGroup: (CR^.Control as TRadioGroup).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;
end end
else if S = 'Text' then else if S = 'Text' then
begin begin
UnescapeString(Value, Unesc, SizeOf(Unesc)); UnescapeString(Value, Unesc, SizeOf(Unesc));
case CR^.CtrlType of 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); ctComboBox: (CR^.Control as TComboBox).Text := StrPas(Unesc);
ctMaskEdit: (CR^.Control as TMaskEdit).Text := StrPas(Unesc);
ctMemo: ctMemo:
begin begin
{ Convert \n to Lines } { Convert \n to Lines }
@ -1043,6 +1107,106 @@ begin
if Start^ <> #0 then if Start^ <> #0 then
(CR^.Control as TRadioGroup).Items.Add(StrPas(Start)); (CR^.Control as TRadioGroup).Items.Add(StrPas(Start));
end; 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;
end end
else if S = 'Checked' then else if S = 'Checked' then
@ -1075,7 +1239,9 @@ begin
begin begin
N := StrToIntDef(StrPas(Value), 0); N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctEdit then 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 end
else if S = 'ReadOnly' then else if S = 'ReadOnly' then
begin begin
@ -1101,9 +1267,12 @@ begin
begin begin
N := StrToIntDef(StrPas(Value), -1); N := StrToIntDef(StrPas(Value), -1);
case CR^.CtrlType of case CR^.CtrlType of
ctListBox: (CR^.Control as TListBox).ItemIndex := N; ctListBox: (CR^.Control as TListBox).ItemIndex := N;
ctComboBox: (CR^.Control as TComboBox).ItemIndex := N; ctComboBox: (CR^.Control as TComboBox).ItemIndex := N;
ctRadioGroup: (CR^.Control as TRadioGroup).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;
end end
else if S = 'Stretch' then else if S = 'Stretch' then
@ -1160,7 +1329,9 @@ begin
begin begin
N := StrToIntDef(StrPas(Value), 0); N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctScrollBar then 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 end
else if S = 'Min' then else if S = 'Min' then
begin begin
@ -1307,6 +1478,66 @@ begin
PCR := FindCtrl(FR, N); PCR := FindCtrl(FR, N);
if (PCR <> nil) and (PCR^.Control is TPopupMenu) and (CR^.Control is TControl) then if (PCR <> nil) and (PCR^.Control is TPopupMenu) and (CR^.Control is TControl) then
(CR^.Control as TControl).PopupMenu := PCR^.Control as TPopupMenu; (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;
end; end;
@ -1405,6 +1636,26 @@ begin
Result := ctMenuItem Result := ctMenuItem
else if S = 'RadioGroup' then else if S = 'RadioGroup' then
Result := ctRadioGroup 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 else
Result := ctUnknown; Result := ctUnknown;
end; end;
@ -1562,6 +1813,26 @@ begin
Comp := TMenuItem.Create(FR^.Form); Comp := TMenuItem.Create(FR^.Form);
ctRadioGroup: ctRadioGroup:
Comp := TRadioGroup.Create(FR^.Form); 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; end;
if Comp = nil then if Comp = nil then

View file

@ -72,7 +72,7 @@ Event data varies by event type:
|-----------|-------------------------------| |-----------|-------------------------------|
| Click | (none), or `<itemIndex>` (RadioGroup) | | Click | (none), or `<itemIndex>` (RadioGroup) |
| DblClick | (none) | | DblClick | (none) |
| Change | `"new text"` or `<position>` (ScrollBar) | | Change | `"new text"`, `<position>` (ScrollBar), or `<index>` (TabSet, TabbedNotebook) |
| Select | `<index> "selected text"` | | Select | `<index> "selected text"` |
| KeyDown | `<vkCode>` | | KeyDown | `<vkCode>` |
| KeyUp | `<vkCode>` | | KeyUp | `<vkCode>` |
@ -105,6 +105,16 @@ Event data varies by event type:
| PopupMenu | TPopupMenu | (none) | | PopupMenu | TPopupMenu | (none) |
| MenuItem | TMenuItem | Click | | MenuItem | TMenuItem | Click |
| RadioGroup | TRadioGroup | 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 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 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 | | Property | Applies To | Value Format |
|-------------|---------------------------------------------|-------------------------------------------| |-------------|---------------------------------------------|-------------------------------------------|
| Caption | Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup | Quoted string | | Caption | Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup, BitBtn, SpeedButton | Quoted string |
| Text | Edit, ComboBox, Memo | Quoted string (`\n` for line breaks) | | Text | Edit, ComboBox, Memo, MaskEdit | Quoted string (`\n` for line breaks) |
| Items | ListBox, ComboBox, RadioGroup | Quoted string (`\n`-delimited) | | Items | ListBox, ComboBox, RadioGroup, TabSet, Notebook, TabbedNotebook, Outline, Header | Quoted string (`\n`-delimited) |
| Checked | CheckBox, RadioButton, MenuItem | 0 or 1 | | Checked | CheckBox, RadioButton, MenuItem | 0 or 1 |
| Enabled | All | 0 or 1 | | Enabled | All | 0 or 1 |
| Visible | All | 0 or 1 | | Visible | All | 0 or 1 |
| MaxLength | Edit | Integer | | MaxLength | Edit, MaskEdit | Integer |
| ReadOnly | Edit, Memo | 0 or 1 | | ReadOnly | Edit, Memo | 0 or 1 |
| ScrollBars | Memo | 0-3 (ssNone..ssBoth) | | 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 | | TabOrder | All windowed controls | Integer |
| Stretch | Image | 0 or 1 | | Stretch | Image | 0 or 1 |
| Center | 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) | | BevelOuter | Panel | 0-2 (bvNone, bvLowered, bvRaised) |
| BevelInner | Panel | 0-2 (bvNone, bvLowered, bvRaised) | | BevelInner | Panel | 0-2 (bvNone, bvLowered, bvRaised) |
| BorderStyle | Panel | 0-1 (bsNone, bsSingle) | | BorderStyle | Panel | 0-1 (bsNone, bsSingle) |
| Kind | ScrollBar | 0-1 (sbHorizontal, sbVertical) | | Kind | ScrollBar, BitBtn | Integer (see below) |
| Min | ScrollBar | Integer | | Min | ScrollBar | Integer |
| Max | ScrollBar | Integer | | Max | ScrollBar | Integer |
| Position | ScrollBar | Integer | | Position | ScrollBar | Integer |
@ -155,6 +165,15 @@ not support opt-in events.
| Columns | RadioGroup | Integer (number of columns) | | Columns | RadioGroup | Integer (number of columns) |
| ShortCut | MenuItem | Integer (Delphi ShortCut value) | | ShortCut | MenuItem | Integer (Delphi ShortCut value) |
| PopupMenu | Any TControl | Integer (ctrlId of PopupMenu) | | 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 File path properties (Picture, FileName) are resolved relative to the
client's `BasePath` setting. Subdirectories are allowed (e.g., client's `BasePath` setting. Subdirectories are allowed (e.g.,