Architecture
The EPL GUI system provides cross-platform native components for Windows, Linux, and macOS with:
- TUIMatrix - Main layout manager with JSON-based responsive schemes
- BaseWidget hierarchy - Consistent widget base with events
- TWinSignal - Thread-safe event queue for GUI updates
- TProtocol - Encrypted client-server communication for thin clients
Widget Hierarchy
BaseControl (V_* native)
│
BaseWidget
├── Position (x, y, width, height)
├── Event handlers (OnChange, OnClick)
├── Theme support
│
├── TForm (window container)
├── TWButton (button)
├── TWEditBox (text input)
├── TWLabel (text display)
├── TWCheckBox (checkbox)
├── TWComboBox (dropdown)
├── TWListView (list/table)
├── TWTreeView (tree)
├── TWTab (tabbed container)
├── TWImage (image display)
├── TWScrollBar (scrollbar)
└── ... more widgets
TUIMatrix Layout Manager
TUIMatrix manages widget layout with JSON-based responsive schemes for different screen sizes.
Layout Methods
// Load layout scheme from JSON
Matrix->LoadScheme("layout.json");
// Apply scheme for current screen size
Matrix->ApplyScheme();
// Dynamic positioning expressions
Widget->SetPosition("after:OtherWidget", "below:Header");
Widget->SetSize("form_width:20", "100");
Position Expressions
| Expression | Meaning |
after:Widget | Position after another widget |
below:Widget | Position below another widget |
form_right:20 | 20px from form right edge |
form_bottom:20 | 20px from form bottom |
center | Center in parent |
Application Structure
// Font definitions
string MainFontName = "Roboto.etf";
string BkpFontName = "DejaVu.etf";
include_file "$Studio/ElastLib/main_inc.epl";
// Main application class
class TMatrix : public TUIMatrix {
TWTitle TitleBar;
TForm MainForm;
TWButton MyButton;
TWEditBox NameField;
}
TMatrix @Matrix;
// Called when GUI is ready
virtual void TMatrix::OnGUIReady() {
// Setup event handlers
MyButton->ProcObj = this_obj;
MyButton->OnChange = "OnButtonClick";
// Initialize widgets
NameField->Text = "Default";
}
// Event handler
void TMatrix::OnButtonClick() {
string name = NameField->Text;
print "Button clicked! Name: " . name . "\n";
}
Event Handling
// Setup event handler
MyButton->ProcObj = this_obj; // Object to call method on
MyButton->OnChange = "OnClick"; // Method name to call
// Event handler method
void TMatrix::OnClick() {
// Handle button click
}
// Alternative: Convention-based naming
void TMatrix::MyButton_OnClick() {
// Auto-wired if naming convention matches
}
Common Events
| Event | Triggered When |
OnChange | Value changes (buttons, checkboxes) |
OnClick | Widget clicked |
OnDoubleClick | Widget double-clicked |
OnFocus | Widget gains focus |
OnBlur | Widget loses focus |
OnKeyPress | Key pressed |
OnSelect | Item selected (lists, trees) |
JSON GUI Definitions
GUI layouts can be defined declaratively in JSON with responsive schemes:
{
"schemes": {
"phone": { "minWidth": 0, "maxWidth": 480 },
"tablet": { "minWidth": 481, "maxWidth": 1024 },
"desktop": { "minWidth": 1025 }
},
"widgets": {
"MainForm": {
"type": "TForm",
"title": "My Application",
"width": 800,
"height": 600
},
"NameLabel": {
"type": "TWLabel",
"text": "Name:",
"parent": "MainForm",
"x": 20,
"y": 20
},
"NameField": {
"type": "TWEditBox",
"parent": "MainForm",
"x": "after:NameLabel:10",
"y": 20,
"width": 200
},
"SubmitBtn": {
"type": "TWButton",
"text": "Submit",
"parent": "MainForm",
"x": "form_right:20",
"y": "form_bottom:20"
}
}
}
Client-Server GUI Architecture
EPL supports thin client applications where GUI runs locally but logic executes on a server.
Architecture
┌─────────────────┐ ┌─────────────────┐
│ GUI Client │◄───────►│ App Server │
│ │ TProtocol│ │
│ - Renders GUI │ (RSA+AES)│ - Business │
│ - User input │ │ logic │
│ - TWinSignal │ │ - Database │
│ event queue │ │ - Processing │
└─────────────────┘ └─────────────────┘
Client Implementation
class TMyClient : public TUIMatrix {
TProtocol Protocol;
int hApp;
}
virtual void TMyClient::OnGUIReady() {
// Initialize protocol
Protocol = new TProtocol;
Protocol->AddPubKey("/path/to/server_public.key");
// Register application callback
Protocol->RegisterApp("MyApp-GUID", hApp, this_obj, "OnServerResponse");
// Connect to server
Protocol->Connect("192.168.1.100:3360");
Protocol->RunClient(hApp);
}
// Handle server responses
void TMyClient::OnServerResponse(int ptr CtrlCode, variant ptr Data) {
if(CtrlCode == 1){
// Update GUI with server data
UpdateList(Data);
}
}
TProtocol Reference
Binary protocol framework with RSA + AES256 encryption.
Initialization
TProtocol Protocol;
Protocol = new TProtocol;
// Load encryption keys
Protocol->AddPubKey("/path/to/public.key");
Protocol->AddPrvKey("/path/to/private.key");
// Register app with callback
int hApp;
Protocol->RegisterApp("MyApp-GUID", hApp, this_obj, "OnData");
// Connect
Protocol->Connect("192.168.1.100:9000");
Sending Requests
// Create request
TProtocol::TReq Req;
Req = new TProtocol::TReq;
// Start request with control code
Protocol->StartAppRequest(hApp, ctrlCode, Req);
// Add data
Protocol->AddVariant(Req, myData);
Protocol->AddString(Req, "text");
Protocol->AddInt(Req, 123);
// Send
Protocol->Send(Req);
Receiving Data
// Callback signature
void TMyClass::OnData(int ptr CtrlCode, variant ptr Data) {
// CtrlCode identifies message type
// Data contains server response
if(CtrlCode == 1){
// Process response
string result = Data["result"];
}
}
Stream Types (AppItem)
| Type | Value | Description |
| Video | 1 | Video stream |
| Audio | 2 | Audio stream |
| App | 3 | Application data |
| Initial | 5 | Initial connection |
Thread-Safe GUI Updates
GUI updates from background threads must use TWinSignal to queue events:
// From background thread, queue GUI update
TWinSignal Signal;
Signal = new TWinSignal;
Signal->TargetObj = Widget; // Target widget
Signal->SignalType = SIG_UPDATE; // Signal type
Signal->Data = newData; // Data to pass
PostSignal(Signal); // Queue for main thread
// Main thread processes signals automatically
Signal Types
| Signal | Description |
SIG_UPDATE | Update widget content |
SIG_REFRESH | Refresh/redraw widget |
SIG_CALLBACK | Call method on main thread |
SIG_CLOSE | Close widget/form |
Safe Pattern
// Network callback (runs on network thread)
void TMyClient::OnServerResponse(int ptr CtrlCode, variant ptr Data) {
// DON'T update GUI directly here!
// Queue update for main thread
TWinSignal Signal;
Signal = new TWinSignal;
Signal->TargetObj = this_obj;
Signal->SignalType = SIG_CALLBACK;
Signal->CallbackMethod = "UpdateGUI";
Signal->Data = Data;
PostSignal(Signal);
}
// Runs on main thread (safe to update GUI)
void TMyClient::UpdateGUI(variant ptr Data) {
MyListView->Clear();
foreach(Data["items"]; int i; variant item){
MyListView->AddItem(item["name"]);
}
}