User Interface
User Interface
uniGUI conveniently integrates with the RAD Studio VCL designer to leverage the knowledge of current developers, but the designer is incapable of rendering the exact user experience of the real application running in a web browser.
As was already mentioned before, VCL components are incompatible with uniGUI, but the uniGUI components keep several VCL properties which provides better compatibility when migrating VCL applications to uniGUI applications. However, uniGUI components render ExtJS components which are specifically designed and optimized for the web. That being said, even standard uniGUI components like TUniPanel take advantage of web features, while other components don't have an equivalent in the VCL. uniGUI components become Sencha Ext JS components running in the client browser, and Sencha is continuously improving their components and creating new components.
The best practice when creating the uniGUI user interface is to configure the components for the web, and to use the best components optimized for the web.
Let's show how to apply this best practice by making a comparison between VCL alignment and client-side alignment, and learning how to use TUniFieldSet and TUniFieldContainer.
VCL alignment vs web alignment
VCL alignment happens server-side, while web alignment executes it immediately client-side. If the developer uses a TPanel control with a TSplitter to adjust the panel width, dragging the TSplitter will create a sequence of requests to the server which will send updates to the client to render the changes. It is costly and makes the user interface less responsive.
VCL
Some of the VCL control properties are:
Height
Width
Top
Left
Align - alNone, alTop, alBottom, alLeft, alRight, alClient
Windows, and the VCL, which is an object oriented, component-based, encapsulation of Windows visual objects, are both based on screen physical coordinates (it is the reason for our current troubles with high resolution displays using much higher DPI).
The World Wide Web must cope with very different client devices, and it is based on relative positions.
uniGUI keeps the same VCL properties, but it also allows to override the VCL behavior and take advantage of the more powerful and flexible web properties.
Web
Alignment in Containers
Some of the Web properties are:
AlignmentControl - uniAlignmentClient, uniAlignmentServer
Layout - absolute, accordion, anchor, auto, border, fit, form, hbox, vbox, table, column
LayoutAttribs
Align - top, middle, bottom, stretch, stretchmax
Columns
Pack - start, center, end
Padding
LayoutConfig
Anchor
BodyPadding
ColSpan
ColumnWidth
DockWhenAligned
Flex
Height
IgnorePosition
Margin
Padding
Region - north, south, east, west, center (equivalent to alTop, alBottom, alRight, alLeft, alClient)
RowSpan
Split - To enable an automatic client-side splitter
Width

In the folder Demos there are several projects which show how to use the previous properties:
Clientside Alignment - Dock and Align
Clientside Alignment - Layout Accordion
Clientside Alignment - Layout Anchor
Clientside Alignment - Layout Border
Clientside Alignment - Layout Column
Clientside Alignment - Layout Fit
Clientside Alignment - Layout Form
Clientside Alignment - Layout HBox
Clientside Alignment - Layout Percentage
Clientside Alignment - Layout VBox
Clientside Alignment - Layout Table
Clientside Alignment - Layout Table Span
Clientside Alignment - Features Demo
Panels
UniGUI panels are also collapsible. The corresponding properties are:
Collapsed
CollapseDirection - cdBottom, cdDefault, cdLeft, cdRight, cdTop
Collapsible
The demo Collapsible Panels shows several collapsible panels.

This demo starts with some collapsed panels and all four of them can be collapsed or expanded using the corresponding buttons. You will notice that each collapse/expand request requires a trip to the server to render the new layout. Try changing that behavior by changing the form AlignmentControl to uniAlignmentClient and configuring the controls using the previously described web properties. Confirm that the new application executes layout changes much faster (there is no further need to ask the server for a new render).
FieldSets
TUniFieldSet
The uniGUI control TUniFieldSet contains fields (uniGUI controls/editors) which should be arranged in one column. This container will render each field according to common properties:
FieldLabel
FieldLabelAlign - alLeft, alRight, alTop
FieldLabelFont
FieldLabelSeparator - default ':'
FieldLabelWidth
Among the uniGUI controls which could be included in a TUniFieldSet is the TUniFieldContainer, which allows to create hierarchical forms like:

TUniFieldContainer
The container arranges the fields according to the web layout requested. In the previous example, FieldContainer3 is using layout table with 3 columns.

The TUniFieldContainer renders the fields in the order they were created. If that order is or becomes incorrect, the property CreateOrder should be used to indicate the desired order. The default value is zero, meaning that they should be the last fields. The desired order starts with one, and the fields will be saved on the .DFM file in that order.
Separate User Interface from Business Logic
Delphi is a RAD environment which encourages development speed. It is easy to create a small application just by dropping a few components on a form, changing some properties, adding a few event handlers, and implementing the real code which was the original goal of the application. In a few minutes, you could have a working application. It is true; RAD Studio makes it possible. But it is also true that most of the time, that application will not be a good foundation for a bigger project. Merging the user interface with the business logic, adding visual controls and data access components, having all kind of event handlers in the same unit is a good recipe for failure.
There are many solutions for disconnecting the presentation layer and the business logic. Everyone knows about MVC (Model-View-Controller), MVP (Model-View-Presenter), MVVM (Model-View-ViewModel), and similar design patterns. While some of these patterns are generic and could be implemented in several languages, development environments, and supporting frameworks, some of them are better for specific scenarios. For example, MVVM was specifically designed to take advantage of WPF (Windows Presentation Foundation), XAML, and event-driven programming.
Despite the Delphi RAD environment, several of its features allow achieving the previous goal, not necessarily following strict patterns.
Use interfaces, not forms
Let's start by showing how to apply one of the more important best practices: code against interfaces, not concrete classes.
Our program could run as a desktop application or as a Web application, but our business logic will require user interaction. If we need to ask the user for some information, we will need to use some visual artifact, but it doesn't mean that our business logic needs to know unnecessary details about it. Even better, it should be possible to write the business logic without having a concrete implementation of the artifact (form, message dialog, whatever it is).
It will be better to explain this principle with a simple example.

Example VCL form unit:
The business logic only needs access to a function GetOrModifySomeText which will return true if the text passed as parameter was modified. It doesn't need to know how that is done. If the only way of using the function is to make an explicit reference to the unit implementing the business logic, we will be adding a dependency to a VCL form and, even worse, to a particular implementation of it.
The same form, this time implemented as a uniGUI free form, requires a similar code.
The only difference here is that we don't need to explicitly release the uniGUI form.
Now, what about being able to write the business logic without having a concrete implementation of the form, or even better, how to target both VCL and uniGUI with the same code?
Let's do some refactoring.
Extract an interface exposing the form behavior
Modify both forms to implement the interface
Don't reference the forms, but the interface
Once the form was created, it can be passed around as the interface it implements. Notice that we added _ShowModal to the interface because ShowModal is different for VCL and uniGUI.
Don't access forms, except for creating and releasing instances
A Delphi form is a "view" or one of the possible implementations of the presentation layer. It should always implement one or several interfaces to clarify its purpose. Any access to the form should be done by using one of the interfaces it implements, hiding any implementation detail and avoiding future errors when modifying internal resources.
Accessing forms through interfaces avoids breaking the code using the form when the form changes. It also opens the possibility of creating different forms/views implementing the same interface. For example:
The application could share most of the business logic while targeting the Delphi VCL desktop and Delphi uniGUI (also desktop-like).
A web application built with uniGUI could provide two different user interfaces, one for desktop users (having mouse and keyboard), other for touch devices (phones and tablets).
Never introduce business logic code in forms, take advantage of Delphi features
Every line of code expressing business logic and embedded in a form creates an unnecessary dependency and impedes creating new views. A typical web application provides access to a database server through a business logic layer. The equivalent Delphi objects are forms, data modules, optional business classes, and the database connections.
Let's analyze how those Delphi objects relate to the components of the MVC pattern.

A Delphi implementation of MVC is something like this:
The View is a TForm, TUniForm, TUnimForm.
It is created at design-time and rendered at run-time.
Some properties could be updated through its interface (still a good behavior)
Most of the updates will be automatically propagated through the database components (connected during creating)
Any user action could trigger database-related updates (perfect), while others could trigger actions (also good). Other acceptable good behavior could be to execute required/expected actions like creating its own data module and releasing it.
The Controller usually is the data module used by the form, and most of the time it contains part of all the Model (at least, the database components).
In a small application, Controller and Model will be implemented in the MainModule.
Bigger applications could use a data module for some forms, in addition to the MainModule.
The Model-View-Presenter pattern suggests something similar.

In this pattern, the Presenter is the mechanism implemented in VCL or uniGUI for handling the "communication" between the Model (data module and business classes) and the View (form). It is worth mentioning that this View is passive (or as passive as possible), which means that it won't include any business logic.
In the next section we will show how to build an application targeting both uniGUI platforms (desktop and touch), but it is important to list the Delphi features that application will use.
Database controls and its events
As the goal is to keep the business logic code out of the View, we will never use data access components in a form. Instead, all these components will be hosted by data modules. In a VCL application, that data module will be automatically created and assigned to the global variable. In uniGUI the approach is different, as the MainModule will be accessible through the function uniMainModule. In both cases, any form can link its data access controls at design-time (to the corresponding data sources and datasets).
Following the principle of consuming resources when needed, always on-demand, each form requiring database access should announce/request the data module to activate/deactivate its resources (that is, open and close datasets). These actions should be done in OnCreate and OnDestroy.
In bigger applications, it could be necessary to create and destroy any additional data module used by the form.
In any case, by hosting the data access controls in the data module, any event will be implemented in the same place (or it could ask some business class defined out of the data module).
By following these practices, no business logic is added to the View. As we will mention later in this chapter, it is even possible to inject these minimal dependencies.
Requesting actions from the View without writing code (platform-independent)
What about using a menu (MainMenu or PopupMenu), pressing a button for requesting a global action or an action focused on some selected dataset record?
In a data-centric application, most of the requested actions affect the active records or some selection of records. Other actions are global and could be requested in several places in the user interface.
Delphi supports actions and action lists for a long time. Each button in a toolbar can be linked to an action. Menu items from any menu can also be linked to actions. Other buttons share that capability. If the action list is hosted by the same data module, every time the user triggers an action, the action's Execute method will be able to query the context (for example, the selected record of a dataset) and implement the requested business logic.
Of course, some actions could require interacting with the user through the same user interface it came from. While using interfaces allows the developer to access a View without knowing how it is implemented or the platform it is targeting, in this case we will need to instantiate the View for the current platform.
Let's see two possible solutions to the previous question.
Create an interface as a facade to platform-dependent services
When the VCL application or uniGUI session starts, whatever is the current platform can be saved and used later for instantiating the correct Views. Of course, the VCL application will always be VCL, but a uniGUI application could be targeting two platforms: desktop and touch. If the user session started from a touch device, any further interaction would rely on touch forms and messages.
The easiest solution for small projects is to define an interface for all the "services" (forms and messaging) required by the business logic, implement them for both targets, and instantiate the correct one according to the user session. After that, every time a form is needed, the concrete implementation, available through the interface, will be used.
Use Dependency Injection for registering multiple implementations of the same form interface
Big projects can have hundreds or even thousands of forms, making the previous solution tedious, time-consuming, and error-prone. The correct solution is to leave that task to a Dependency Injection framework like Spring4D.
It is out of the scope of this documentation, but we will show how to modify our demo to do it.