Writing ProcScript in Uniface 10 is plain text editing. Painting a form is mouse work, and the IDE has a few habits that are not obvious the first time. This post walks through one complete example - a small login dialog - from an empty project to a compiled form, and collects the things that are worth knowing before you start.
Everything here was done with Rocket Uniface 10 Community Edition (10.4).
What we are building
A modal login form LOGIN_FRM with:
- a label and an edit box for the user name,
- a label and a masked edit box for the password,
- two buttons, Log in and Cancel,
- a status line for messages.
The form returns 1 (logged in) or 0 (cancelled) to the caller:
activate "LOGIN_FRM".exec(vOk)
if (vOk != 1)
return -1
endif
Step 1: create the component
- Open your project (browse bar:
prj:<PROJECT_NAME>). - In the Templates list on the left, find Component: Modal Form.
- Drag it onto the project list.
- Select the new entry and set its Name in the property panel on the right, for example
LOGIN_FRM.
A modal form blocks its caller until it is closed - exactly what you want for a login, a password change or a picker dialog. For a main window you would choose Component: Non-modal Form instead.
Open the new component (browse bar: cpt:LOGIN_FRM). The editor shows two tabs: Define Frames (the painting canvas plus a structure tree) and Write Script.
Step 2: paint a frame
Fields in a Uniface form live inside an entity. If the form does not show database data, a non-modeled entity is enough - a container that exists only in this component.
The palette entry for that is Non-DBMS Entity: Occurrence List.
What does not work on an empty form: dragging that template onto the canvas. What does work:
- Click the template in the palette.
- Draw a rectangle on the canvas with the mouse.
The structure tree now shows ENTITY.NOMODEL below the component. Rename it to something meaningful, for example LOGIN_DMY.NOMODEL (see "Renaming" below). Every field you paint later is addressed as FIELD.LOGIN_DMY in ProcScript.
Plan the size before you draw
Two properties of the frame surprised me:
- A frame cannot be made taller afterwards - not with the resize handle, not in the property grid. If you need more rows later, you redraw the frame (and its fields).
- Top, Left, Width and Height are read-only in the property grid. You change them on the canvas only.
So decide the layout first. A frame of roughly 25 rows by 67 columns is plenty for a small dialog; a larger maintenance dialog may need 30 by 78. Leave a few empty rows at the bottom - free rows are cheap, a too-small frame is not.
It helps to sketch the layout as a grid on paper (or in a text file) before painting:
| Row | Column 2 | Column 17 | Column 33 |
|---|---|---|---|
| 1 |
LBL_USER_NAME (14 wide) |
USER_NAME (30 wide) |
|
| 3 |
LBL_PASSWORD (14 wide) |
PASSWORD (30 wide) |
|
| 5 |
BTN_LOGIN (14 wide) |
BTN_CANCEL (14 wide) |
|
| 7 |
INFO (64 wide) |
Rows and columns are relative to the frame. Leaving one empty row between lines and one empty column between a label and its field keeps the form readable and leaves room for later.
Step 3: paint the fields
Painting a field works the same way as painting the frame: click the field template, then draw a rectangle inside the frame. The templates used here:
| Purpose | Palette entry |
|---|---|
| Label, status line | Display: Static Text |
| Single-line input | Input: EditBox (Single-line) |
| Multi-line output or input | Input: EditBox (Multi-line) |
| Password | Input: Password |
| Yes/No | Input: CheckBox |
| Button | Button: Command Button |
A few details that matter:
-
The Password template creates an edit box with a masked display (field syntax
NCR,NDI). You read the value in ProcScript like any other field; clear it right after use. -
The CheckBox template creates a boolean field. Assign
1/0and compare with it directly. - Static Text is display-only. Its value can still be set from ProcScript, which makes it a good status line.
- Draw slowly. If the mouse moves too fast after pressing the button, the IDE sometimes starts the rectangle late and you get a field in the wrong place with the wrong width. Press, pause briefly, then drag.
- Check the palette before every click. After each new field, the palette list may scroll to a different position. Clicking "where the Command Button was" can give you a completely different widget. Look first, then click.
If a field ends up in the wrong place: select it on the canvas, press Delete, and draw it again. That is usually faster than moving and resizing it.
Dropping on the tree instead
Dragging a field template onto the entity row in the structure tree opens an "Insert Frames" dialog and appends the field below the last one in the frame. That is handy when a frame is full and you just want one more field at the bottom.
Step 4: name everything
New fields get generic names like STATIC_TEXT or COMMAND_BUTTON. Rename each one right after painting it - with more than three fields on the canvas, finding "the second COMMAND_BUTTON" becomes guesswork.
The reliable way to rename:
- The new field is selected after painting, so its row in the structure tree is highlighted.
- Click that highlighted row once. An in-place rename box opens.
- Select the text, type the new name, press
Return.
Two things to know about the tree:
- It is sorted by position on the canvas (top to bottom, then left to right), not by creation time. If you paint fields in reading order, every new field appears at the end of the list.
-
Clicking a row that is already selected opens the rename box. That is how renaming works, but it is also an easy accident. If a rename box opens unexpectedly, check that the text is unchanged and press
Return(orEscape) - never type anything into it.
Use a naming scheme that tells you what a field is:
-
LBL_...for labels, -
BTN_...for buttons, - the plain business name for data fields (
USER_NAME,PASSWORD), -
INFOfor the status line.
Step 5: captions and labels
A command button shows the value of its field as its caption, and a static text shows its value as text. You can set that value in the property grid (Initial Value), or you can set it from ProcScript when the form starts:
entry SET_LABELS
LBL_USER_NAME.LOGIN_DMY = "User name"
LBL_PASSWORD.LOGIN_DMY = "Password"
BTN_LOGIN.LOGIN_DMY = "Log in"
BTN_CANCEL.LOGIN_DMY = "Cancel"
return 0
end
Setting the texts in code has two advantages: all texts of a form are in one place, and changing a caption is a text edit instead of a hunt through the property grid. The disadvantage is that the canvas shows the field names instead of the captions while you paint - which, in practice, makes it easier to see which field is which.
Step 6: write the script
Switch to Write Script. The structure tree is on the left, the editor on the right. Selecting the component row shows the component script; selecting a field row shows that field's script.
Component variables
The component script has two sections: Declarations and Script. Component variables - values that live as long as the form instance - are declared in Declarations:
variables
numeric cResult
endvariables
and referenced with dollar signs in the code:
$cResult$ = 1
Without the dollar signs, the compiler treats cResult as a field name and reports warning 1000 - Field 'CRESULT' not found. The form compiles, but the value goes nowhere.
The exec operation
A modal form is started with activate "LOGIN_FRM".exec(...), which runs the exec operation. edit hands control to the user; the statements after it run when the form is closed:
operation exec
params
numeric pResult : OUT
endparams
call SET_LABELS
$cResult$ = 0
INFO.LOGIN_DMY = ""
edit
pResult = $cResult$
end
Button actions
Keep the logic in entries of the component script and let each button only call its entry. That way every button script is three lines, and the logic is in one place where you can read it top to bottom:
entry DO_LOGIN
variables
string vUser, vPassword, vRole, vError
endvariables
vUser = USER_NAME.LOGIN_DMY
vPassword = PASSWORD.LOGIN_DMY
PASSWORD.LOGIN_DMY = ""
activate "AUTH_SVC".LOGIN(vUser, vPassword, vRole, vError)
if ($status < 0)
INFO.LOGIN_DMY = vError
return -1
endif
$cResult$ = 1
macro "^QUIT"
return 0
end
entry DO_CANCEL
$cResult$ = 0
macro "^QUIT"
return 0
end
macro "^QUIT" closes the form; control returns to the statement after edit in exec.
Then select each button in the structure tree (one click) and give it a detail trigger:
trigger detail
call DO_LOGIN
end
The detail trigger fires when the button is clicked.
Step 7: compile and test
- Compile checks and saves the component.
- Compile & Test additionally starts it.
For a form built like this, expect exactly one warning:
warning: 1016 - (Fields for) entity LOGIN_DMY not found in application model, generating now...
That is the non-modeled entity, and it is harmless. Anything else is worth reading: 1000 - Field '...' not found usually means a typo in a field name, a missing $...$ around a component variable, or a field that was never renamed.
Pitfalls collected
| Symptom | Cause | Fix |
|---|---|---|
| A dialog closes while you type |
Ctrl+A is mapped to ^ACCEPT in the IDE |
select text with Ctrl+Home / Ctrl+Shift+End
|
| Dragging the Occurrence List onto an empty form does nothing | frames are drawn, not dropped | click the template, then draw a rectangle |
| The frame is too small and cannot be enlarged | frame height is fixed after drawing | plan the size first; redraw if needed |
| Top/Left/Width cannot be edited in the property grid | position and size are canvas-only | move or redraw the field on the canvas |
| A field appears in the wrong place with the wrong width | the drag started late | press, pause, then drag slowly |
| A "Map" or "Spin Button" appears instead of a button | the palette had scrolled | check the palette position before each click |
| A rename box opens unexpectedly | second click on an already selected tree row | leave the text unchanged, press Return
|
Warning 1000 - Field 'CRESULT' not found
|
component variable used without $...$
|
write $cResult$
|
| Selecting all text in the script also removes the section headers | the selection spans Declarations and Script | select from the first line of Script to the end |
Takeaways
Plan on paper, paint in reading order. A grid with rows, columns and widths takes five minutes and saves redrawing a frame. Painting top to bottom, left to right keeps the structure tree in the same order as the canvas.
Name every field immediately. Generic names are fine for exactly one field.
Keep button scripts trivial. One call per button, the logic in component entries. The form becomes readable as a script, and the painted part is only layout.
Treat warnings as information. With non-modeled entities, 1016 is expected. Every other warning in a freshly painted form points at a naming problem that will otherwise show up at runtime.
Top comments (0)