Besides the TQTXGrid widget, the column panel is probably the control I am asked the most about how to use. I fully understand that when coming from Delphi or Lazarus, some of the widgets in the QTX runtime-library can seem difficult or confusing. We are used to dragging & dropping controls onto a form, and get instant visual feedback.
Sadly, this is not how HTML5 works all the time. We have to keep in mind that HTML is ultimately a document format, where web developers are expected to provide content according to a specification within that medium. In many cases child elements have to conform to a specific displaymode and positionmode in order to render correctly.
This does not always translate well to a form designer approach.
TQTXColumnPanel is one such widget, where you really are expected to provide content by code rather than dropping child widgets at design-time.
What does TQTXColumnPanel do?
The widget exposes and wraps the fancy new column CSS attributes, most notably the “column-count” attribute which divides the content into X number of columns, dividing the width between them. The result can look somewhat like traditional newspaper columns.
The CSS attributes that defines and affects column layout in HTML are (here listed in their JavaScript prototype format (more about that further down):
- columnCount
- columnFill
- columnGap
- columnRule
- columnRuleColor
- columnRuleStyle
- columnRuleWidth
- columnSpan
- columnWidth
- columns
I should underline that TQTXColumnPanel does not expose all of these via the inspector. It exposes the bare-bones of what is needed to set a panel into column mode. You can access the other attributes through the widget’s Style property, or directly through the widget handle (more about that towards the end of this post).
To get to grips with the widget I suggest you drag & drop a few TQTXDomDiv widget instances onto the column-panel. TQTXDomDiv is a clean, thin wrapper over the HTML DIV element – which works really well with column layout.
As the name says (div is from the Latin diversus, meaning miscellaneous) the DIV element is a generic element that developers can use for anything (basically just a rectangular region). This is the element type that most other HTML5 elements inherits from behind the scenes of the document object model. If you know Delphi, this can be seen as a parallel to TWinControl.
In HTML, different element types (read: tags) have different rules associated with them. In this case you want child widgets to have a displaymode of cdBlock, and positionmode set to cpInitial.
Run in a full browser
The Quartex IDE uses a small and fast embedded browser called Miniblink to preview applications. This is a tiny footprint browser that is barely 30 megabytes in size, and it’s capable of rendering HTML4 and a fair subset of HTML5 – which is quite impressive considering the footprint.
As amazing as Miniblink is, it only supports a subset of HTML5. It is an embedded browser after all and not really intended to rival desktop browsers. I included it purely because I found it useful when developing RTL code designed for mobile applications. You should ignore it if you need to work with cutting edge HTML5.
Miniblink gave me a sort of lowest common denominator to aim for with regards to element and feature support. But Miniblink is not a replacement for a full modern browser. I strongly urge developers to run and test their applications in their system browsers.
Column support is sadly one the features Miniblink lacks, so make sure you click the “Open in browser” button (on the toolbar, next to the Execute button) to run your project in the system default browser.
I presume you have installed a modern browser such as Chrome, Firefox or Edge? If not, make sure you install one of these first. Once installed, go into the IDE’s preferences dialog and select the browser for use (this can be found under the Network tab).
Remember to save any changes to your form before clicking the Build button, and then click the “Execute in browser” button to open the project in your system browser of choice.
Populating the panel
Personally I prefer to do things from code as much as possible. The so-called “code first” approach where you try to write classes and units in code (since that is more easily made portable), and only use the form designer for the most complex or time consuming of tasks. I realize that this is not how most people work, but working with HTML5 will force you to approach the DOM from a code-first perspective in some cases.
In the case above, we could have edited the InnerHTML property of each panel by clicking on the [..] button in the inspector (on the same row as the InnerHTML property). That is perfectly valid and the data will be baked into your code and assigned to the property during construction.
However, for this example I have set the content via code, like this:
You could also have simplified the whole process even further and dropped the TQTXDOMDiv widget’s completely, by writing this:
In terms of speed the above method is the fastest, since there is no widget creation cycle involved.
While a bit outside the scope of this article you can access the div’s by code by using the standard JavaScript functions. Once you have the handle for these elements, you can pass that handle in the constructor of TQTXWidget, and it will adopt the handle as it’s own. But for static content like this, a TQTXWidget really is overkill and meaningless unless you need to work with one of the columns directly.
Dealing with widget height
One thing you will notice when populating a TQTXColumnPanel with widgets via the form designer, is that their height will always be whatever you designed them as at design-time. This is the because the inspector values are written to the instance during construction -and in this particular instance it would actually be better if it didn’t.
This can be solved by accessing the raw HTML style of the widgets directly from code and setting the height to 100%. The inspector cannot do this since it only works with intrinsic, typed datatypes – so it is not possible to assign “100%” to the height of an element from the inspector (it expects an integer value).
We can solve this by adding the following code to the form initialization:
Widget1.Style.height := "100%";
Widget2.Style.height := "100%";
Widget3.Style.height := "100%";
This is true for all widgets by the way, you can access CSS directly for every widget directly, and override whatever was set in the inspector at design-time. The Style property in Pascal of a widget is just a helper object which maps the most common CSS styles by name, just to make it easy to work with from your object pascal code.
If you need to access a style that is not exposed via the Style property, you can go the immediate route through the widget (read: element) handle:
Widget1.Handle.style["some-name"] := Value;
Widget1.Handle.style.someName := Value;
The above two lines does the exact same thing, except the first accesses the style attribute through the CSS collection (hence the hyphen in the name which all such names use), while the other accesses the attribute directly via the JavaScript prototype property (read: object property).
If you look at JavaScript code and how people operate with CSS values, you will notice this distinction. It is faster to access it directly via the prototype, but traditionally it has been safer to use the named CSS collection. This is because different browsers use slightly different names for things, usually just prefixes.
A bit of context: Firefox uses the “-moz-” prefix, while Safari and Chrome use the “-webkit-” prefix. But in the past few years all the browsers have agreed to support non-prefixed named properties. So instead of for example “-moz-transition” or “-webkit-transition”, you can simply write “transition” and all browsers will understand it.
Hope this helps
I hope this little post have helped clear up how to use TQTXColumnPanel. Keep in mind that the knowledge we have talked about here, such as accessing the underlying element’s styles through the handle property, is universal within QTX. It applies not just to this widget, but all of them.