Skip to content

Latest commit

 

History

History
679 lines (530 loc) · 23 KB

File metadata and controls

679 lines (530 loc) · 23 KB
applyTo **/Forms/**/*.{4DForm,4dm}

4D Forms Architecture Guidelines

TLDR - Non-Negotiable Rules

  • Use form class-driven architecture: keep logic in form classes, and delegate from method.4dm and object methods.
  • Keep .4DForm schema-compliant; move advanced visual blending to assets.
  • For PNG light/dark + HiDPI, always provide 4 variants: xxx.png, xxx_dark.png, xxx@2x.png, xxx@2x_dark.png.
  • Prefer CSS theming for native objects when dark mode/theme adaptation is required.
  • Initialize full form state in class constructor before loading data.
  • Use currentItemSource for listbox selection; do not use LISTBOX Get table source.
  • Use async state objects with running, progress.value, progress.message, and error field.
  • During async actions, show progress UI and hide trigger actions consistently.
  • Use naming groups with @ suffix for bulk visibility control.
  • Keep styling and business logic separated.

Form Structure and Schema

4D forms must follow the schema defined in the formsSchema.json file located in the .github/.instructions directory. This schema defines the structure and valid properties for form.4DForm files, including object types, properties, events, and styling options.

Transparency and Visual Effects

  • IMPORTANT: 4D form rendering does not reliably support transparency for native object fill and stroke values.
  • AVOID semi-transparent native colors (rgba(...)) for liquid/frosted effects on form objects.
  • RECOMMENDED PATTERN: Build layered effects with picture objects using PNG/SVG assets.
  • WHEN USING SVG ASSETS for adaptive themes; include @media (prefers-color-scheme: dark) in SVG styles when dark mode behavior is required.
  • WHEN USING PNG ASSETS WITH LIGHT/DARK + HIDPI SUPPORT: Provide all four files for each image base name:
    • xxx.png (light, 1x)
    • xxx_dark.png (dark, 1x)
    • xxx@2x.png (light, 2x/Retina)
    • xxx@2x_dark.png (dark, 2x/Retina)
  • KEEP SCHEMA COMPLIANCE: Use schema-valid properties in .4DForm and move advanced visual blending into image assets.

Core Form Architecture Pattern

This project uses a Form Class-driven architecture where each form has a dedicated class that acts as a complete view controller:

Form-Class Binding

  • Every form declares its class in form.4DForm: "formClass": "formMenu"
  • Form classes (e.g., formMenu.4dm) contain ALL form logic as properties and methods
  • Form methods (method.4dm) delegate immediately to the form class: Form.formEventHandler(Form event code)
  • Object methods delegate to form class handlers: Form.btnGeneratePeopleEventHandler(Form event code)

Form Class Structure Pattern

// File: Sources/Classes/formVectorize.4dm
property providersGen : Object        // Dropdown data: {values: Collection; index: Integer}
property modelsGen : Object          // Dynamic model list based on provider
property actions : Object            // State management for async operations
property AIText : Text              // Real-time streaming text content

Class constructor()
    // Initialize all form state in constructor
    This.providersGen:={values: []; index: 0}
    This.actions:={embedding: {running: 0; progress: {value: 0; message: ""}}}
    
    // Load data from database and populate dropdowns
    var $providers:=ds.providerSettings.providersAvailable("embedding")
    This.providersGen.values:=$providers.extract("name")

//MARK: - Form & form objects event handlers
Function formEventHandler($formEventCode : Integer)
    Case of 
        : ($formEventCode=On Load)
            OBJECT SET VISIBLE(*; "progress@"; False)

Function btnGeneratePeopleEventHandler($formEventCode : Integer)
    Case of 
        : ($formEventCode=On Clicked)
            This.actions.generatingPeople.running:=1
            // Start async operation with Form reference
            cs.AI_PeopleGenerator.me.generatePeopleAsync(params; Form)

//MARK: - Form actions callback functions  
Function terminateGeneratePeople()
    // Handle completion of async operations
    OBJECT SET VISIBLE(*; "progress@"; False)

//MARK: - Computed properties
Function get embeddingDateTime() : Text
    return String(This.actions.embedding.info.embeddingDate; "dd/MM/yyyy")

Data Binding & State Management

Standard Data Source Patterns

// In form.4DForm - bind directly to form class properties
{
    "dataSource": "Form.providersEmb",           // Dropdown lists
    "dataSource": "Form.actions.embedding.progress.value",  // Progress bars
    "dataSource": "Form.actions.embedding.running",         // Spinners (0/1)
    "dataSource": "ds.person.all().length"                  // Database queries
}

Dropdown List Pattern

// Always use this structure for dropdowns
This.providersEmb:={values: []; index: 0}

// Dynamic model loading based on provider selection
Function providersEmbListEventHandler($formEventCode : Integer)
    Case of 
        : ($formEventCode=On Data Change)
            This.modelsEmb:=This.setModelList(This.providersEmb; "embedding")

Function setModelList($providerList : Object; $kind : Text) : Object
    var $provider:=ds.providerSettings.query("name = :1"; $providerList.currentValue).first()
    var $models:=$provider.embeddingModels.models
    return {values: $models.extract("model"); index: 0}

Listbox Selection Pattern

// Form class property for selected item
property currentCountry : Object
property filteredData : Collection

// Form constructor initialization
Class constructor()
    This.currentCountry:=Null
    This.filteredData:=[]
// In form.4DForm - CRITICAL: Use currentItemSource for selection binding
{
    "type": "listbox",
    "dataSource": "Form.countryData",
    "currentItemSource": "Form.currentCountry",  // THIS IS ESSENTIAL
    "listboxType": "collection",
    "method": "ObjectMethods/listboxCountry.4dm"
}
// Event handler using the bound current item
Function listboxCountryEventHandler($formEventCode : Integer)
    Case of 
        : ($formEventCode=On Selection Change)
            If (This.currentCountry#Null)
                // Access selected item properties directly
                This.filteredData:=This.allData.query("country = :1"; This.currentCountry.country)
            Else 
                This.filteredData:=[]
            End if 
    End case

// AVOID: Never use LISTBOX Get table source - causes syntax errors
// WRONG: $selectedRow:=LISTBOX Get table source(*; "listboxName")
// RIGHT: Use currentItemSource binding and This.currentItem

Async Operation State Pattern

// Standard pattern for long-running operations
This.actions:={
    embedding: {
        running: 0;                    // 0=stopped, 1=running (for spinner)
        progress: {value: 0; message: ""}; // Progress bar + status text
        status: "Missing"              // Overall status: "Missing"/"Done"/"In progress"
    }
}

// Start async operation
Function btnVectorizeEventHandler($formEventCode : Integer)
    This.actions.embedding.running:=1
    OBJECT SET VISIBLE(*; "progress@"; True)   // Show progress UI
    OBJECT SET VISIBLE(*; "btnVectorize"; False) // Hide trigger button
    
    // Use CALL WORKER for background processing with Form reference
    CALL WORKER("embedding-worker"; Formula(cs.AI_PersonVectorizer.me.vectorize($1)); Form)

Subform Communication Pattern

Parent-Child Form Communication

// Parent form (menu) manages subforms via OBJECT SET SUBFORM
Function tabMenuEventHandler($formEventCode : Integer)
    Case of 
        : (This.menu.currentValue="Data Gen & Embeddings 🪄")
            OBJECT SET SUBFORM(*; "Subform"; "vectorize")

// Child forms communicate back via EXECUTE METHOD IN SUBFORM  
Function terminateGeneratePeople()
    If (Current form name="menu")  // Called from subform
        EXECUTE METHOD IN SUBFORM("Subform"; Formula(Form.terminateGeneratePeople()); *)
    Else  // Direct form usage
        OBJECT SET VISIBLE(*; "progress@"; False)

Form Communication Rules

  • Upward: Child forms call parent via EXECUTE METHOD IN SUBFORM
  • Downward: Parent controls child via OBJECT SET SUBFORM
  • Async callbacks: Always check Current form name and delegate appropriately

UI Visibility & Progress Management

Progress UI Pattern

// Use object naming convention with @ wildcard for groups
OBJECT SET VISIBLE(*; "embedding@"; True)    // Show all embedding-related objects
OBJECT SET VISIBLE(*; "btn@"; False)         // Hide all buttons during operation

// Progress components always include:
// - spinner (running: 0/1)
// - progress bar (value: 0-100) 
// - status text (message: Text)

Dynamic UI State Updates

// Real-time text streaming (e.g., AI generation)
Function progressGeneratePeople($input : Object)
    If (Not(Undefined($input.AIText)))
        Form.AIText+=$input.AIText
        // Auto-scroll to bottom
        HIGHLIGHT TEXT(*; "InputAIText"; Length(Form.AIText); Length(Form.AIText))
        GOTO OBJECT(*; "InputAIText")

Form Validation & Error Handling

Input Validation Pattern

Function btnAskMeEventHandler($formEventCode : Integer)
    If (This.modelsGen.currentValue="")
        ALERT("Please select a model first")
        return 
    End if
    // Proceed with operation...

Async Error Handling

// Always include error state in actions object
This.actions.embedding.error:=""

// Handle errors in progress callbacks
Function progressVectorizing($progress : Object)
    If (Not(Undefined($progress.error)))
        This.actions.embedding.error:=$progress.error
        This.actions.embedding.running:=0  // Stop operation

Worker Integration Pattern

Background Processing with Form Updates

// Start worker with form reference for callbacks
CALL WORKER(String(Session.id)+"-embedding"; Formula(cs.AI_PersonVectorizer.me.vectorizePeople($1; $2; $3; $4)); $provider; $model; $recomputeAll; Form)

// Worker calls back to form methods for progress updates
Function progressCallback($progress : Object)
    // Worker implementation calls: $form.progressVectorizing($progress)

Critical Form Development Rules

1. Object Naming Conventions

  • Progress groups: Use @ suffix (e.g., embedding@, btn@) for bulk visibility control
  • Consistent naming: btnAction, providersTypeList, progressMessage, statusSpinner

2. Form Class Requirements

  • ALWAYS include constructor with complete state initialization
  • ALWAYS use MARK comments: //MARK: - Form & form objects event handlers
  • ALWAYS delegate from form/object methods to form class handlers
  • NEVER put business logic directly in form methods

3. Listbox Selection Requirements

  • ALWAYS use currentItemSource binding for listbox selection handling
  • ALWAYS declare a property for the selected item (e.g., property currentCountry : Object)
  • ALWAYS initialize the selection property to Null in constructor
  • NEVER use LISTBOX Get table source or similar commands - they cause syntax errors
  • ACCESS selected item via This.currentItem in event handlers
  • PATTERN: "currentItemSource": "Form.currentItem" in form.4DForm

4. Listbox Row Height Configuration

  • ALWAYS use string format with unit for rowHeight: "rowHeight": "32px" (NOT numeric 32)
  • NUMERIC VALUES FAIL: Using "rowHeight": 40 causes automatic row height calculation
  • STRING FORMAT WORKS: Using "rowHeight": "32px" enforces fixed row height
  • OPTIONAL: Add "verticalAlign": "middle" to center content within rows
  • TRUNCATION: Use "truncateMode": "withEllipsis" on columns to prevent text wrapping
  • RESIZING: Use "resizingMode": "legacy" for traditional sizing behavior
  • EXAMPLE:
    {
        "type": "listbox",
        "rowHeight": "32px",
        "verticalAlign": "middle",
        "columns": [
            {
                "name": "ColName",
                "truncateMode": "withEllipsis"
            }
        ]
    }

5. Data Loading Best Practices

  • ALWAYS load data in the constructor, not in form events
  • INITIALIZE collections to empty arrays [] to prevent null reference errors
  • LOAD data synchronously in constructor for immediate display
  • AVOID loading data in On Load form event - forms appear empty initially
  • PATTERN: Initialize properties first, then load data in same constructor call
  • EXAMPLE: This.countries:=[] then This.loadCountryData()

6. Async Operation Standards

  • ALWAYS use running: 0/1 for spinner data source
  • ALWAYS include progress object with value and message
  • ALWAYS hide action buttons and show progress UI during operations
  • ALWAYS handle both direct form and subform execution contexts

7. Data Binding Best Practices

  • USE Form.property syntax for form class properties
  • USE ds.table.query() for direct database binding
  • AVOID global variables - everything goes through form class properties

8. Form Communication

  • DELEGATE all communication through form class methods
  • CHECK Current form name in shared callback functions
  • USE EXECUTE METHOD IN SUBFORM for parent-child communication

UI Design & Styling Architecture

Design System Approach

Establish a consistent visual design system with defined color schemes and typography:

Color Palette Strategy

// Organize colors by function, not specific values
// Header/Title sections
"headerBackground": "#colorValue"

// Informational states  
"infoNormal": "#colorValue"      // Normal status/info cards
"infoWarning": "#colorValue"     // Warning/missing states
"infoSuccess": "#colorValue"     // Success/completed states

// Background hierarchy
"contentBackground": "#colorValue"
"containerBackground": "#colorValue" 
"borderColor": "#colorValue"

// Interactive elements
"inputBackground": "#colorValue"
"inputBorder": "#colorValue"

Typography System

// Establish consistent font hierarchy
"fontFamily": "PreferredFont"    // Choose primary font for all text
"titleSize": number              // Page titles/headers
"sectionSize": number            // Section titles  
"bodySize": number               // Body text, inputs, buttons
"smallSize": number              // List details, small labels

Component Styling Patterns

Information Card Pattern

// Standard info card structure - define your own consistent styling
{
    "type": "rectangle",
    "borderRadius": "consistentValue",
    "stroke": "borderColor", 
    "fill": "normalStateColor",    // For normal state
    "fill": "warningStateColor"    // For error/warning state
}

Header Section Pattern

// Consistent header styling across forms
{
    "type": "rectangle",
    "fill": "headerBackgroundColor",
    "stroke": "transparent",
    "height": "standardHeaderHeight"
}

Input Field Styling

// Standard input appearance
{
    "fontFamily": "primaryFont",
    "fontSize": "bodyTextSize",
    "borderStyle": "none",        // Remove default borders for custom styling
    "fill": "transparent",        // Transparent background
    "hideFocusRing": true        // Clean focus appearance
}

// Rounded input containers
{
    "borderRadius": "inputContainerRadius",    // For text input containers
    "borderRadius": "largeContainerRadius"     // For larger interaction areas
}

Layout & Spacing Conventions

Grid System & Positioning

// Establish consistent spacing measurements for your project
"top": "standardTopMargin",     "left": "standardLeftMargin"     // Content positioning
"width": "sidebarWidth",        "height": "sidebarHeight"       // Sidebar dimensions
"width": "formWidth",           "height": "formHeight"          // Form dimensions

// Button sizing standards
"width": "actionButtonWidth",   "height": "actionButtonHeight"  // Primary action buttons
"width": "iconButtonWidth",     "height": "iconButtonHeight"    // Icon/small buttons

// Progress elements
"height": "progressBarHeight"   // Progress indicators
"height": "statusTextHeight"    // Status messages

Visual Hierarchy Rules

  1. Page titles: Use largest font size, consistent positioning
  2. Section titles: Secondary font size, consistent alignment
  3. Content areas: Use rounded rectangles with consistent border radius
  4. Separator lines: Consistent color and width throughout
  5. Action buttons: Consistent styling and visual identification patterns

CSS Support & Web Components

4D Forms CSS Support

  • Limited CSS support: 4D forms support CSS2 syntax but adapted for 4D-specific needs
  • StyleSheet files: Use /SOURCES/styleSheets.css, /SOURCES/styleSheets_mac.css, /SOURCES/styleSheets_windows.css
  • Form-specific CSS: Can be specified via "css" property in form.4DForm for custom stylesheets
  • Priority order: JSON properties override CSS unless !important is used
  • CSS classes: Objects can use "class" property to apply CSS class styles
  • shared_css.json: Used for Qodly Pages, not 4D forms

CSS Selector Support in 4D Forms

/* Object type selector */
button {
    font-family: Helvetica Neue;
    font-size: 20px;
}

/* Object name selector */
#okButton {
    font-family: Helvetica Neue;
    font-size: 20px;
}

/* Class selector */
.okButtons {
    font-family: Helvetica Neue;
    text-align: center;
}

/* Universal selector */
* {
    fill: gray;
}

/* Attribute selector */
[borderStyle] {
    stroke: purple;
}

4D-Specific CSS Features

  • CSS property keys: Must match the property names defined in formsSchema.json (e.g., fill, stroke, borderStyle, fontSize) — not arbitrary CSS properties
  • Attribute mapping: 4D names (fill, stroke) or CSS names (background-color, color) both accepted
  • Path syntax: icon: url("/RESOURCES/Images/Buttons/edit.png");
  • Color formats: CSS color names, hex values, rgb() function

Supported Media Queries

4D forms support two media queries that can be combined:

/* System color scheme */
@media (prefers-color-scheme: dark) { }
@media (prefers-color-scheme: light) { }

/* 4D form theme — possible values: win-classic, mac-classic, fluent-ui, liquid-glass */
@media (form-theme: liquid-glass) { }
@media (form-theme: fluent-ui) { }
@media (form-theme: mac-classic) { }
@media (form-theme: win-classic) { }

/* Combined */
@media (form-theme: liquid-glass) and (prefers-color-scheme: dark) { }
@media (form-theme: fluent-ui) and (prefers-color-scheme: light) { }

Example: Theme-Aware CSS Class

/* Default (all themes, light mode) */
.panelCard {
    fill: #FFFFFF;
    stroke: #F0F4F9;
}

@media (prefers-color-scheme: dark) {
    .panelCard {
        fill: #1a2332;
        stroke: #2a3f4f;
    }
}

@media (form-theme: liquid-glass) and (prefers-color-scheme: light) {
    .panelCard {
        fill: #F8FAFC;
        stroke: #E2E8F0;
    }
}

@media (form-theme: liquid-glass) and (prefers-color-scheme: dark) {
    .panelCard {
        fill: #1e2d3d;
        stroke: #334155;
    }
}

Then in form.4DForm, assign the class to objects:

"Rectangle2": {
    "type": "rectangle",
    "class": "panelCard"
}

This Project's Approach

  • CSS files are preferred when theming or dark mode adaptation is needed for native objects
  • Create CSS files when the user requests it: /SOURCES/styleSheets.css (cross-platform), /SOURCES/styleSheets_mac.css (macOS only), /SOURCES/styleSheets_windows.css (Windows only)
  • Inline JSON is acceptable for one-off or non-themed values (positions, sizes, non-color properties)
  • Web areas: Used for complex interactive components (styling is separate topic)

UI State Management Patterns

Progress Indication Design

// Consistent progress UI pattern across all forms
"progressBar": {
    "type": "progress",
    "height": "progressBarHeight",
    "max": 100,
    "dataSource": "Form.actions.operation.progress.value"
},
"statusMessage": {
    "fontSize": "bodyTextSize",
    "fontFamily": "primaryFont", 
    "borderStyle": "none",
    "fill": "transparent"
},
"spinner": {
    "type": "spinner",
    "width": "spinnerWidth", "height": "spinnerHeight",
    "dataSource": "Form.actions.operation.running"
}

Dynamic Visibility Control

// UI state transitions using object naming conventions
OBJECT SET VISIBLE(*; "operationGroup@"; True)    // Show progress group
OBJECT SET VISIBLE(*; "btn@"; False)              // Hide action buttons

// Color-coded status indication in form classes
Case of 
    : (This.status="Missing")
        $color:="warningColor"     // For missing/error states
    : (This.status="Done") 
        $color:="successColor"     // For success states
    : (This.status="In progress")
        $color:="processingColor"  // For processing states
End case

Form-Specific Design Patterns

Master-Detail Interface Pattern

  • Main panel: Primary content area for complex interactions
  • Side panel: Secondary content browser (listbox + subform)
  • Separators: Consistent dividing lines between sections
  • Input areas: Grouped input controls with consistent styling

Dashboard Layout Pattern

  • Info cards: Grid of status rectangles with state-based colors
  • Action areas: Grouped controls by function with consistent placement
  • Progress overlays: Temporary UI that replaces action controls during operations

List Interface Pattern

  • Search controls: Consistent positioning and spacing
  • Results area: Full-width data display with custom styling
  • Filter sidebar: Secondary content in consistent container styling

UI Development Guidelines

1. Color Usage Rules

  • ESTABLISH a consistent color palette for your project
  • ASSIGN specific colors to states (error/warning, success, normal, disabled)
  • USE semantic naming for colors rather than direct hex values
  • AVOID introducing new colors without updating the design system

2. Typography Consistency

  • CHOOSE a primary font family for the entire application
  • DEFINE a clear hierarchy with consistent font sizes
  • MAINTAIN consistent font sizes within component types
  • CONSIDER readability across different screen sizes

3. Layout Standards

  • USE borderRadius consistently for modern appearance
  • ESTABLISH standard spacing and sizing conventions
  • MAINTAIN consistent margins and padding throughout forms
  • GROUP related elements visually with background containers

4. Interactive Elements

  • STANDARDIZE button appearances and behaviors
  • USE consistent progress indication patterns (spinner + progress bar + status text)
  • IMPLEMENT clear visual feedback for user interactions
  • CONSIDER accessibility requirements for interactive elements

5. Component Reusability

  • CREATE reusable styling patterns for common components
  • DOCUMENT your design system decisions for team consistency
  • USE consistent object naming conventions for easy maintenance
  • MAINTAIN separation between styling and business logic

This design system ensures consistent, professional appearance across all forms while maintaining the modern look expected in contemporary applications.