Skip to content

Screens, Input and Menus

You have a form. Now you need a program that opens it, shows data in it, lets the user edit it, and offers actions through a menu. That is the heart of every interactive 4GL program, and it follows a simple, repeatable shape.

Opening and closing a window

A form is shown inside a window. Open it with the form name (no path, no extension), give the window a title, and close it when done.

MAIN
    CLOSE WINDOW screen                    -- close the default text window first
    OPEN WINDOW w1 WITH FORM "product"     -- "product" is the compiled form
    CALL ui.Interface.setText("Product Entry")

    -- ... work with the form ...

    CLOSE WINDOW w1
END MAIN

The VDC way. You do not position windows with AT row, col, and you do not manually place the OK/Cancel buttons — the VDC client handles window placement and shows the right buttons automatically. Your job is the form and the logic.

The client places and snaps windows automatically
You never position windows with AT row, col — the client places, sizes and snaps them, and users can re-arrange or tile them at will.

Showing data: DISPLAY

To put values into form fields, use DISPLAY.

DISPLAY BY NAME matches variables to fields with the same name — the easy case:

DEFINE name  CHAR(40)
DEFINE price DECIMAL(10,2)

LET name  = "Olive Oil"
LET price = 12.50
DISPLAY BY NAME name, price          -- fills fields named "name" and "price"

DISPLAY ... TO lets you name the target field explicitly:

DISPLAY "Welcome!" TO message
DISPLAY total      TO f_total

Collecting input: INPUT

INPUT hands control to the user so they can edit fields, then returns when they accept or cancel.

The simplest form, INPUT BY NAME, edits the fields matching your variables:

DEFINE code  CHAR(8)
DEFINE name  CHAR(40)
DEFINE price DECIMAL(10,2)

INPUT BY NAME code, name, price

Add WITHOUT DEFAULTS to keep whatever is already in the variables (instead of clearing the fields first):

INPUT BY NAME code, name, price WITHOUT DEFAULTS

Reacting while the user types: control blocks

The real power of INPUT is the control blocks that fire at specific moments. You put them inside the INPUT ... END INPUT body:

INPUT BY NAME code, name, price

    BEFORE INPUT
        MESSAGE "Fill in the product details"

    AFTER FIELD code
        IF LENGTH(code CLIPPED) = 0 THEN
            ERROR "Code is required"
            NEXT FIELD code          -- send the cursor back to this field
        END IF

    AFTER FIELD price
        IF price < 0 THEN
            ERROR "Price cannot be negative"
            NEXT FIELD price
        END IF

    ON ACTION f9                     -- a button/key the user pressed
        CALL pick_category()

    AFTER INPUT
        MESSAGE "Saving..."

END INPUT

The blocks you will use most:

Block Fires when…
BEFORE INPUT Editing starts, before the first field
BEFORE FIELD f The cursor enters field f
AFTER FIELD f The cursor leaves field f — the place to validate
ON ACTION name The user triggers action name (a button or key)
AFTER INPUT The user accepts the whole form

MESSAGE "..." shows an informational line; ERROR "..." shows an error line. NEXT FIELD f moves the cursor to field f (use it to refuse a value and keep the user on the field).

Knowing whether the user accepted or cancelled

After an INPUT, check INT_FLAG. It is set when the user cancels:

INPUT BY NAME code, name, price
    -- ... control blocks ...
END INPUT

IF INT_FLAG THEN
    LET INT_FLAG = FALSE
    MESSAGE "Cancelled"
ELSE
    MESSAGE "Accepted"
END IF

A MENU shows the action buttons for a window and waits for the user to choose one. Each COMMAND is a button with the code that runs when it is pressed.

MENU "Products"
    COMMAND "New"
        CALL new_product()
    COMMAND "Edit"
        CALL edit_product()
    COMMAND "Delete"
        CALL delete_product()
    COMMAND "Close"
        EXIT MENU
END MENU

EXIT MENU leaves the menu loop (here, to close the window). Until then, the menu keeps running: after each command finishes, the user is back at the menu.

You can also handle keys/actions in a menu with ON ACTION, exactly like in INPUT:

MENU "Products"
    COMMAND "New"     CALL new_product()
    ON ACTION close   EXIT MENU
END MENU
COMMAND and ON ACTION items become an icon toolbar
Your COMMAND and ON ACTION items become this action toolbar — the client supplies crisp SVG icons and consistent placement; you only name the actions.
The same MENU as touch tiles on the mobile client
The very same MENU on the mobile client — rendered as touch tiles. One program, both clients.

Putting it together

Here is a complete, runnable program for the product.per form from chapter 3. It shows the standard shape of an interactive program: open a window, then loop on a menu, calling a function per action.

MAIN
    DEFINE code        CHAR(8)
    DEFINE name        CHAR(40)
    DEFINE category    CHAR(10)
    DEFINE price       DECIMAL(10,2)
    DEFINE active      CHAR(1)
    DEFINE description CHAR(200)

    CLOSE WINDOW screen
    OPEN WINDOW w1 WITH FORM "product"
    CALL ui.Interface.setText("Product Entry")

    MENU "Product"
        COMMAND "New"
            CALL enter_product()
                 RETURNING code, name, category, price, active, description
        COMMAND "Close"
            EXIT MENU
    END MENU

    CLOSE WINDOW w1
END MAIN

FUNCTION enter_product()
    DEFINE code        CHAR(8)
    DEFINE name        CHAR(40)
    DEFINE category    CHAR(10)
    DEFINE price       DECIMAL(10,2)
    DEFINE active      CHAR(1)
    DEFINE description CHAR(200)

    INPUT BY NAME code, name, category, price, active, description

        AFTER FIELD code
            IF LENGTH(code CLIPPED) = 0 THEN
                ERROR "Code is required"
                NEXT FIELD code
            END IF

        AFTER FIELD price
            IF price < 0 THEN
                ERROR "Price cannot be negative"
                NEXT FIELD price
            END IF

    END INPUT

    IF INT_FLAG THEN
        LET INT_FLAG = FALSE
        MESSAGE "Cancelled"
    ELSE
        MESSAGE "Product ", code CLIPPED, " entered"
    END IF

    RETURN code, name, category, price, active, description
END FUNCTION

Compile and run it (with the client listening):

fcompile -xml product.per
4glpc -o product.4ae product.4gl
./product.4ae

Notice the shape. MAIN opens the window and runs the menu; each menu command calls one function that does one job. Keep that structure — it scales from this tiny program to large applications, and it reads top-down like a table of contents.

So far our screens hold one record at a time. Next, learn to show and edit lists of records: Working with Arrays.