New JQuery UI package

One of our more experienced backers, Ed Van Der Mark, has updated his JQuery-UI package for Quartex Pascal. His latest package installs both the widgets into the component palette, and also registers project templates – so that you can create new project types tailored for JQuery UI directly from the “New project” dialog.

The JQuery-UI package registers both widgets in the palette and a new project type

Here Ed’s original post:

I have made an update for the JQueryUI widgets. It’s now possible to add the widgets in the Widgets Palette of the Quartex Pascal IDE. The widgets are dependent of a Javascript framework and theme files.

Install steps

  • Download the JQueryUI.zip file by using the download-link
  • Upzip the JQueryUI.zip file in the ‘projects’ folder
  • Within this project folder you’ll see a ‘install’ folder
  • Execute ‘install.cmd’, this command copies a package file and a template file with a template folder

Restart the IDE

After (re)starting the Quartex Pascal IDE you’ll see the new widgets of JQueryUI. After starting a new project you can choose a new template JQueryUI, this template contains the needed dependencies for this framework.

It’s my first try of adding a package to the Quartex Pascal IDE. In the near future I hope to expand the widgets with useful dialogs. ?

If you are ready with testing the widgets and want to get rid of the extra JQueryUI widgets, please delete the ‘jquery.widgets.pkg’ file in the ‘packages’ directory (only possible after leaving the Quartex Pascal IDE, because of a file lock).

A parsing framework for Delphi, FPC and QTX

Text parsing is something most people rarely think about. We mostly work with file formats that already have existing parsers – be it JSON, XML or Inifiles. But there are edge cases where knowing how to properly parse a custom format or string can make all the difference.

In this article I will cover the essential architecture of the Quartex parser, the default parser for the Quartex RTL and our native framework. This framework is the codebase we wrote to implement the IDE for Quartex Pascal, and it contains a wealth of useful units. The Quartex native framework will be available to backers and customers shortly after release of Quartex Pascal.

Some background

The most difficult part of implementing a new development toolchain and RTL, is that you literally have to implement everything from scratch. In our case we compile object pascal to Javascript, which means that very little of the infrastructure we know from Delphi and Freepascal exists. At best you can write a thin wrapper that use features from the JavaScript runtime (if applicable), but when it comes to more complex functionality – you have no choice but to write it from scratch.

Since Quartex Pascal targets node.js / deno, being able to work with ini-files is useful for things like server configuration. Json is great and i could have used that, but I wanted to make the RTL as compatible as possible (or at least offer similar features to Delphi and Freepascal), so I ended up taking ‘the hard way’.

Concepts and approaches

The first two iterations of my parser were a bit rough around the edges. They worked fine, but I always ended up exhausting their design and getting frustrated, so eventually I ended up writing a new one. It was only when i reached the third revision that I finally had a mold that was flexible enough to cover all the use cases I had encountered (with includes the LDEF language parser). It seems so simple and obvious when I explain it below, but a fair bit of work went into this (and yes I realize that the model i arrived at is not new).

What I ended up with was a parsing model consisting of 4 elements that are largely decoupled:

  1. Buffer
  2. Model
    • Model objects (optional)
  3. Context
  4. Parser
    • Parser collection (optional)

The Buffer

The buffer class is where whatever text you need to parse is held. The buffer exposes basic functionality for traversing the text (the parser is forward only, with some exceptions). Much like a database table it operates with a BOF, EOF and Next() cursor mechanism (you call the Next() method to move forward by one character). All methods are inclusive, meaning that reading or comparing always includes the character where the imaginary cursor is located. The current character can be read via the current: char property.

The basic buffer class implements a rudimentary functionality. This is further expanded in TQTXParseBuffer with more elaborate behavior.

The idea of ‘inclusive reading‘ might sound like a trivial thing, but you wont believe the mess if you have functions that deviate from that exact same behavior. It seem absurdly obvious, but when writing loops and recursive code, finding that one scenario where you called Next() once to often is tricky.

The buffer builds up more complex behavior such as Read([length]), ReadWord(), ReadQuotedString(), ReadUntil(), Skip(count), Peek() and Compare(char|string). The most common comparisons such as equal, punctum, space, PasQuote, CQuote, less, more and so on – are isolated in a child class. This means you can write code that is quite readable and easy to maintain:

// move to the first char
buffer.first();

// Check that we are not BOF
// That would mean there is nothing to work with
if not buffer.BOF then
begin
  repeat
    if buffer.common.equal() then
    begin
      // we found the equal char
    end;
    buffer.Next();
  until buffer.EOF;
end;

The above mechanism of EOF / BOF works well with Pascal strings that start at index 1. BOF evaluates as offset < 1, while EOF evaluates as offset > length(text).

The buffer operates with a few sub concepts that should be known:

  1. Ignore set
  2. Line ranges (optional)
  3. Bookmarks
  4. Control characters

These are very easy concepts:

  • Ignore characters are set to [space + tab], this means the buffer will just skip them when you call buffer.ProcessIgnore().
  • Line ranges means that the parser keeps track of CR+LF and register where each line of text starts and ends within the text (the offset into the string). The text you assign to the buffer is kept as a stock string field. There was little to gain by using pointers since an integer offset into a string is pretty fast.
  • Bookmarks stores the current cursor position, column and row, and the current line ranges. Bookmarks are typically used when reading ahead to check something, so that you can quickly return to the previous state.
  • Control characters are set to [#13,#10] (on Windows, Linux operates with #10 by default), the buffer will skip these and update the line-ranges when you call buffer.ProcessCRLF.
  • Both ignore characters and control characters can be changes (type is TSysCharSet)

The model

Where the buffer provides the essential means of navigating through the text, you also need a place to store the result. For example, if you are parsing pascal code then you would identify keywords, parameters, brackets, semicolon and so on – and store these as tokens somewhere (the tokenizing stage of the compilation process). This ‘somewhere’ is where the model object comes in.

Example:

With ini-files the two entities we collect are groups, such as “[settings]”, and name-value-pairs, like “port=8090”. Differenciating between these are simple, as group names are within brackets [], while value assignments contains a = character beyond column position 1. Checking that the criteria for a value is met means reading ahead and looking for “=”. The name will be to the left of the equal char, and the value is whatever is beyond it. CR|LF is a natural stop, making inifiles simple and elegant to parse.

The main logic of the TQTXInifile parser is fairly straight forward and muc smaller than you might expect

You are expected to inherit from TQTXCustomModel and setup whatever data structure you need there. So the parsers that I already implemented, such as TQTXInifileParser, TQTXCommandlineParser, TQTXDFMParser and so on – all have a specific model, context and parser classes.

The context

This is where things become more interesting. When you are dealing with truly complex formats, be it source-code or something of equal complexity, you can write several parsers that deal with a particular chunk of the format. So instead of having one massive parser that is supposed to deal with everything under the sun – you divide the work across several parsers instead.

To make this easy and fast, both the model and the buffer is isolated in the context object. The context object is the only piece that is shared between parser instances. When you create a parser, you must pass a context instance to the constructor. Since the parsers use the same context (and consequently buffer), you can create parsers on demand and continue in your main loop without problems.

The parser

The parser class is where you implement the logic for either an entire format, or a piece of a format. You use the methods of the buffer to work your way through the text, store whatever results you have in the model – and create sub parsers should you need them. Since no data is stored in the parser itself, not even the position in the buffer, they can be recycled easily.

For example, if you were parsing a pascal unit, you would have a parser for the interface section, one for the uses clause, enum parser, record parser, class parser, interface parser, function and procedure parser, var parser, comment parser – and then use them at the correct places.

Recycling parsers this way is why we have a parsing collection available.

Parsing collections (optional)

Sub parsers (as mentioned above) can be held in a dictionary like class, TQTXParserCollection, so you can query if a parser exists for a given name or token.

var lKeyword := context.buffer.ReadWord();
if fParsers.GetParserInstanceFor( lKeyword, lSubParser) then
  lSubParser.Parse( context )
else
  raise EParseError.Create('Syntax Error');
end;

Availability

The latest parser framework will be available in the next update. It has been re-implemented from scratch to work with QTX (first and foremost), Delphi and Freepascal.

As of writing the use of the code outside of QTX is reserved for backers / customers, we might consider making it open source together with the Quartex Native Framework after the release of QTX.

Align: TAlign and PreAlignBounds

One of the coolest features that has been added to Quartex Pascal’s RTL in the past few months, is most likely support for Align: TAlign for TQTXWidget. This greatly simplifies form layout, and also doing layout when writing your own custom controls.

Rules and differences

HTML5 is not the same as WinAPI or GTK, and as such there are sometimes subtle and small differences in how things are done. Aligning widgets is fairly straight forward (from a user’s perspective), but behind the scenes it’s actually a bit more complex than how the VCL or LCL frameworks operates.

The challenge is not technique or anything special we do in our code, but rather how the browser calculates things – and when it does it. For example, a change in content of a widget can trigger what is known as a reflow. This is when the browser decides to completely re-calculate the layout for a branch in the DOM (document object model). The browser can even force a full reflow, meaning that it recalculates the entire layout for the whole DOM. A reflow is not a problem for QTX, but if you are doing a lot of animations, resizing and content changes – non aligned content might jitter while the browser performs its calculations.

To avoid this, TQTXWidget cache’s the bounds-rect it had before the Align property was set. This cache is used to calculate the order in which widgets occupy horizontal and vertical space – and how much space they are allowed to use in the overall layout.

For example: If you have two labels that are both aligned left, which one of them should be placed first? Obviously the one that is closest to the left-edge at the time the widget was aligned. To avoid reflows jittering widgets during a complex layout operation (e.g the user resizing the browser window excessively), even for a millisecond, the RTL cache the original bounds.

This way, regardless of what the browser might do – widget layout remains consistent.

Adjusting pre align bounds

Alignment is very important when writing your own custom controls

If you try to change the width of a widget that is aligned left or right at runtime, you will notice that nothing happens. This is because altering the width property does not alter the cache’d boundsrect values. It’s the cache that has to be adjusted, because that’s what TQTXWidget uses as the basis for it’s calculations.

In other words, you have to alter the properties inside the TQTXWidget.PreAlignBounds property if you want to change size proportions or order of aligned widgets.

In the present build this can only be done from within a TQTXWidget (it can only adjust itself). However that is about to change. In the next build, TQTXBounds exposes SetLeft(), SetTop(), SetWidth, SetHeight and SetBounds() methods. This greatly simplifies altering proportions at runtime.

So the correct approach would be something like this:

Procedure TMyControl.Resize(Orientation: TQTXOrientation);
begin
  if fChild.Align <> alNone then
    fChild.PreAlignBounds.SetWidth( round(Height * 1.5) )
  else
    fChild.Width := fDefaultSize;

  // Allow the RTL to do it's resize calculations
  inherited Resize(Orientation);
end;

March update and beyond

It has been a little while since we issued an update, so last week we decided to ship another “snapshot” build for the initial backers. There have been a lot of changes so listing them all would cover several A4 pages, needless to say – we are hitting the last tickets at record speed.

Ragnarok protocol designer

While visual component features such as Align: TAlign and linear constructors were much needed additions (earlier constructors were asynchronous), the main focus of the latest update was to finish the Ragnarok protocol designer. Adding support for enum types and collection types in the process.

One of the first things you will notice when writing client-server applications regardless of what transport methodology you use (REST, UDP, TCP or WebSocket), is that you need uniformity. If you are writing a complex server that deals with 20, 30 or more messages (read: commands), you must have standards with regards to message structure, field datatypes and how data is serialized. You also need to differentiate between client messages (sent to a server) and response-messages (returned to the client as a response).

Ragnarok protocol designer is built into the IDE, you can edit as many protocols as you like

Ragnarok solves all of this. It builds on a message-envelope (read: JSON structure) that is tried and tested. Adding to this is the visual designer that takes the grunt out of defining complex message structures. You can define your own complex structures, collection types, enumeration types – which can all be used side by side with intrinsic types when constructing your message objects. There is also clear differentiation between client messages and response messages.

Adding a protocol is as simple as clicking “New File” and selecting “Protocol”.

Multiple protocols

Protocol definition files have the extension *.RRP and can be added to your project at any time. You can also have as many such protocol definition files as you like in a single project.

When the compiler notices such a file in your project folder, it automatically generates ready-to-use object pascal files that you can use from your code. You can define what should be generated within the protocol editor. A typical client-server project would consist of a node.js websocket server and a HTML5 websocket client, but you can also generate client code for node.js – which is handy when creating services that communicate in a cluster.

You can check what code should be generated for the protocol-file. Protocol definition files are JSON

Ragnarok can also be used between a HTML5 application and running web-workers (threading). The uniformity of the generated protocol classes simplify communication regardless of endpoint types (networking, message channels, named pipes et-al).

Easily extended

Since we are aiming for our first launch of the product shortly, support for Ragnarok will be limited to Quartex Pascal. However, the generated pascal code – and consequently the JSON emitted by the message objects when serialized, is standard JSON. Meaning that these messages can easily be supported by other languages.

Once we start working on version 1.1 of Quartex Pascal, support for generating code for JavaScript or TypeScript, as well as Delphi / Lazarus and C# is a natural next step. There is no black-magic involved, just a well crafted message-envelope scheme and code-generators that saves you days of monotonous boilerplate coding.

Switching to Lazarus

The first version of Quartex Pascal is expected to be out this summer (hopefully before). The initial version is written completely in Delphi. However, starting from version 1.1 we will have switched wholesale to Lazarus and Freepascal.

This will have a huge impact on our demographic, and will allow us to target more exotic platforms such as Raspberry PI 4 and 5, ODroid N2+, Rock Pi 5b and other popular single board computers. Raspberry PI is especially interesting since it’s very popular for school and education. We feel Quartex Pascal is a perfect fit for education. The language is easy to learn, students get instant visual feedback, and they can target a HTML5 and work with internet technology directly. Something which for kids growing up today is second nature.

We already have a working builds for MacOS x86 and ARM, Linux x86, Windows x86 – both 32bit and 64bit builds. These builds use TrollTech QT as their UI framework, including their exciting WebView engine.

The transition from Delphi to Lazarus / Freepascal will take some time though, since Quartex Pascal is a large and complex piece of software that covers a lot of different technologies. We have few dependencies, but it’s imperative that the IDE and compiler behaves identical across platforms.

We will keep you posted on our progress!

Changes, fixes and debugger

These are exciting days and Quartex Pascal is being polished, refactored and optimized on a daily basis. For the past week or more my focus has been on the RTL, while Kjell is busy with the IDE and making our toolchain more platform independent.

I could probably list a wide range of tickets we have closed, but I want to focus on the RTL changes since that is at the heart of things. To sum up the most important changes in the RTL this week:

  1. Align: TAlign now works brilliantly
  2. Bugs with regards to async constructors are gone, the source for the strange inconsistensies was simply that people (myself included) had tried to achieve too much inside callback mechanisms, causing synchronization and timing issues. It is very tempting to adopt some of the flamboyance of JavaScript when working with QTX, but the framework defines rules that are there for a reason.
  3. Moved execution of the constructor callback to AFTER the widget has been injected into the DOM, but before ObjectReady() is invoked. This is actually a massive change, since it radically changes what you can do in the constructor callback. It also affecs nested constructors and when they finish (read: when widgets are available in the DOM and can be accessed).
  4. By getting rid of the issues above, Javascript JIT kicks in and is able to optimize the code far better than before. I was also able to remove bloat and fine-tune things now that ready-state is no longer a factor.
  5. TQTXSyncWidget will be depricated since there is no longer any difference between TQTXWidget and TQTXSyncWidget.
  6. Sourcemaps and debugging!

As you might remember from previous articles and posts, the difference between Delphi or FPC’s component constructor and our’s, is that we have a second, optional parameter. An anonymous procedure (a.k.a callback) that allows you to daisy-chain constructors and together – and thus initialize properties immediately, as a part of the constructor itself, rather than afterwards.

The benefit of this technique is that you can “recursively nest” work together, forcing JavaScript to create the entire UI in a single swoop. This technique is something that JSVM (JavaScript virtual machine) excels at and agressively JIT optimize -which is ultimately why QTX code is blistering fast. Even for large and complex UI designs containing hundreds, if not thousands of widgets.

The downside has been that you have been limited to two things:

  1. Create child widgets or objects
  2. Initialize properties

If you tried to do anything else, like accessing a widget further up the chain (which might not be ready yet due to the reverse nature of daisy chained constructors) you would run into access violations.

To make this more clear, imagine you daisy-chain three constructors together to create three widgets as quickly as possible:

  1. First widget
    • Second widget
      • Third widget

In the above sequence, the third widget would in fact be available first, since that is at the end of the recursive sequence. This means that if you tried to access the first or second widget from the third, you would cause an access violation. A widget is by nature only ready to be accessed after it’s callback had finished.

Making it easier

Since this clearly has been counter intuitive we decided to move where we invoke the callback. We have moved it to a spot after the widget is ready to be used, but before ObjectReady() is invoked. This means that child elements will already be in the DOM when the callback executes, and that elements further up in a nested sequence will be available.

Code such as this will then be perfectly valid:

var fHeader:= TQTXHeader.Create(self, procedure (Header: TQTXHeader)
begin
?Header.ButtonLeft.Caption := "Back";
?Header.ButtonRight.Caption := "Forward";
end);
In the new RTL timing is more natural and intuitive

The above code failed in previous versions of the RTL, because there was no guarantee that TQTXHeader had in fact created it’s child buttons. The ButtonLeft and ButtonRight child objects would have been nil.

Since we know the callback and it’s limitations has been confusing and counter intuitive, especially the limitations — we decided it was worth the effort to change it. Most people will immediately assume that they can access widgets and objects there, so it makes sense to meet that expectation as best we can.

We do still suggest you override the InitializeObject() and FinalizeObject() methods rather than the constructor, but these methods are just time-savers since you dont have to type the entire constructor syntax and callback type.

Consequences

The consequences of this is surprisingly little and only affects the RTL and component writers. In 99% of the cases I have encountered so far, the only consequence is that creation-order is flipped. Like outlined above, the last constructor would finish first in the old RTL, but now they finish first-to-last (in the order you have nested them).

The only UI change is (so far) with regards to zIndex. I am sure there will be edge cases, but updating code written especially for the older RTL is very simple. We are talking one-liners or, at most, re-arranging the sequence you create child elements. You can still use WhenReady() to line up work that needs to happen when ObjectReady() is invoked, just like before. So the change is abysmall.

Since Align: TAlign is now supported out of the box for all TQTXWidget’s, you dont need to override Resize() to manually deal with layout. So all in all writing your own complex widgets is now easier than ever.

Sourcemaps and debugging

We still have some way to go before Chrome’s debugger protocol is fully integrated with the IDE, but we are making excellent headway. The compiler now emits the source-maps, and the IDE clones out the pascal code (which is needed by the browser debugger if you wish to use Chrome’s tools directly) into it’s own debug folder.

Once we get more tickets done the goal is to talk directly with Chrome (or Edge) so you can set breakpoints directly in the pascal editor, inspect variables and parameters, and basically work with the code like you do in Delphi or Lazarus. The protocol is already wrapped and ready, so this will be awesome!

Support for Linux, MacOS and ARM

We have already gotten the compiler to spin on Linux, MacOS and Windows from the same codebase, and likewise for ARM Linux (suitable for Raspberry PI, ODroid, Rock Pi 5 and similar SBC [single board computers]) so that is exciting!

I want to add Risc-V to the list of platforms, but we have to research what that entails or how well Freepascal supports the platform. From what we understand so far, support is fairly complete and it should be possible to compile for Linux on Risc-V once our codebase is 100% platform independent.

The goal ofcourse is to make both the IDE and compiler 100% platform independent (the compiler already running on said targets and chipsets), so that it compiles both under Delphi and Freepascal. This will require a substancial refactor of our codebase, but it’s something we want to do. This will have significant benefits for us both in terms of product demographic and adoption, but also for schools and educational institutions where affordable ARM devices is used for IoT. Being able to work on such devices is definitively a great bonus!

Stay tuned, QTX is coming!

Quartex is now a company

It is with great pleasure that I can inform everyone that Quartex Pascal is now a company with a fully funded three year runway. This means that Quartex Pascal as well as the desktop project will see full-time development the next three years.

As of such, the Patreon project will shut down mid December. Don’t remove your backing just yet, i need time to register each name and the tires so that backers can receive appropriate licenses for having backed the project. I will inform everyone in good time.

Having sacrificed more or less every weekend for the past four years, it feels good to have arrived at a place where i can focus more deeply on the tasks, rather than jumping in and out with a full work-week between sessions. I can barely remember a weekend I did not work on QTX in some capacity. I must admit I had hoped the Patreon model would have seen stronger backing, but at least I gave the alternative routes a fair chance.

I think products like this can only be realized through traditional, commercial mechanisms. That is ultimately the lesson I have learned after four years of alternative paths. C# and Miguel de Icaza’s story is somewhat similar. He barely made any money on donations and backing (and that was in California, at the heart of Silicone Valley) and it was ultimately thanks to Novell investing in C# and asp.net for Linux that Xamarin Studio (commercial mono, much better than VS) came into existence. Xamarin Studio was later bought by Microsoft, and the mobile development framework and compiler in VS is ultimately Xamarin. Without commercial investment, Xamarin would never have been a success.

Anyways, updates will appear on Patreon for December, and after that everything will happen via the official website here. The Facebook group will remain active, and it will become an open user-group like Delphi developer, FPC Developer and C++Builder Developer – which are the developer groups I manage on Facebook.

Salud Compadres!

There will no doubt be some pause in January as I implement the license server so updates etc. can be automated, but news will be posted here starting with the release of version 1.0 of Quartex Pascal.

So there is a lot to be happy for right now! Finally I can work full weeks on QTX and have normal weekends again ??

Now for the third chapter in a very exciting journey ??

Filesystem over Websocket

With the desktop application model in development we are finally nearing the stage where we can bring out the big guns and tie the web-desktop infrastructure together. If you are new to QTX you might be wondering what that means? Well, let’s do a little recap of what we are building here and why!

The QTX Desktop is already capable of running 68k Amiga software courtesy of vAmiga (webassembly). x86 support is coming, as is DOSBox support. But those are just for fun and love of retro coding. The actual desktop services and API for making full and large HTML5 based web applications is what this is all about. Being able to have a desktop ecosystem that you can deploy to and host anywhere.

Before QTX existed I had a similar product called Smart Mobile Studio, and I used that to create a prototype of a web based desktop system. Sadly my work on Smart Mobile Studio came to a halt in late 2018, and it became clear that if we finished the desktop system using that compiler, anyone interested in the web desktop would have to buy Smart Pascal in order to work with the codebase I was creating. That made little sense since I had left the company and held no stocks there (I would effectively be doing their work for free, lining their pockets by proxy). At the same time the Smart Pascal RTL would need so many changes to cope, that it would practically be a full re-implementation.

After much consideration we (the backers and myself) decided it would be better to implement a whole new development tool-chain from scratch, and then continue on the desktop project once QTX had matured.

But why a web desktop you might ask? Why would that even be remotely interesting? Well if you have a NAS (network active storage) or a router in your home (chances are you have at least a router) and look at how those devices are controlled, you will find they are both accessed purely through a browser. It also turns out that writing applications for an established NAS brand, such as Synology, is both costly for the developer (absurd developer license fees) and extremely esoteric.

What does such a NAS consist of? Is it black-magic? Does these devices have some secret sauce that separates it from a run-of-the-mill desktop PC? Turns out there is no secret sauce except for their web desktop system, which allows users to manage and work with their NAS from any webbrowser. What you are paying for in blood when you buy a NAS, is ultimately this web desktop system and the work that has gone into that aspect of the product. Most NAS systems are extremely over-priced considering the hardware you get, and you have ZERO control beyond what Synology (or any of the other vendors) gives you.

The Synology web desktop. It works really well and makes it easy to install programs for your NAS. Everybody knows how to use a desktop so it’s a lot more intuitive than the typical website approach. Just sad it costs a ton of money

This is where the Quartex Media Desktop (codename Amibian.js) comes in. A system written 100% in Quartex Pascal and that needs only node.js and a browser to deliver the goods. The entire system is 100% node.js / HTML5, and can thus be moved between architectures ( x86, ARM, Risc-V, PPC ) without needing to re-compile anything.

$800 for a NAS? The hardware is maybe worth $100

This work stopped in late 2019 when we had more or less exhausted the old development system (SMS was designed for mobile apps, not a full desktop environment and back-end services). Sadly, COVID took center stage for a couple of years and took a huge bite out of our TTM. I have kept on working on QTX, even though the tasks seemed endless sometimes. But finally, finally we are reaching that sweet spot where the underlying code is evolved enough that we can continue.

Which is where we are now!

Let’s get the filesystem going

People often ask me why I mention the Commodore Amiga in the same breath as QTX, and you would be forgiven for thinking that it’s just a bad case of nostalgia. And while I have many fond memories of learning to code on Commodore machines way back in the early 1980s, this is ultimately not why I find that machine and OS so inspiring.

Commodore was way ahead of it’s time, and many of the ideas that went into their operating system, were quite frankly too complex for the hardware to fully realize. You have to keep in mind that a stock Amiga 1200 had a 68020 CPU running at 14 MHz with 2 megabytes of ram. Your wristwatch (if you have a modern smart-watch like Samsung Galaxy or Apple Watch) has 100 times more processing power than that old machine. However, the ideas and solutions they came up with is where my focus is.

Take something simple, like the filesystem. Amiga OS had a driver based filesystem long before Windows was capable of displaying more than 16 colors. They also organized that filesystem and path structure, so that creating virtual drives was intrinsic to the system. You could in fact mount a folder as a drive by assigning a drive identifier to it, using a simple shell command, such as:

Assign MyDrive: MyFiles/MoreFiles

The above would register a “fake” disk called “MyDrive” that would simply be mapped to the local folder MyFiles/MoreFiles. In today’s jargon this would be called a symbolic link, and you can do this with Windows, MacOS and Linux. But out of those three systems, only Linux comes close to the same ease of use and elegance of the original Amiga OS.

An early version of the desktop prototype, with filesystem running for both Dropbox, Server (local) and Ram-disk, used seamlessly between running applications. Each application runs in it’s own frame context and communicate with the desktop API through messages. This is where Ragnarok comes in for those that are wondering about that.

If we take that concept and refactor it to 2023, taking account for cloud storage and the various storage mechanisms popular vendors (such as Amazon, Dropbox or Azure) provide, the path-syntax that Amiga OS operated with makes much more sense than it did running on slow and bulky 1980’s consumer hardware:

Assign MyPictures: Dropbox:pictures
Assign MyMovies: Azure:Movies

The path syntax that Amiga OS operated with is brilliant in it’s simplicity, and the fact that it was driver based both for the physical-media-filesystem and networking filesystems, is one of the reasons Amiga OS is still being used today by thousands of people around the world. Whenever there is a new storage media or protocol, someone writes a driver for it, and the core OS can instantly use the new technology. The path syntax the OS follows is:

[drive|assign|pipe|rexx|samba channel]:folder/folder/file

As you can see from above, the same syntax covers not just files and folders, but also pipes, samba shares (network shared folders) and rexx. While I don’t see any point of rexx in 2023, the syntax is malleable enough to cover local storage devices, yet it can stretch to also cover networking and IPC mechanisms. Commodore took the best from Unix and DOS when they designed this. They also applied some ideas from an earlier system called Minix. The takeaway here should be, that it’s a formula that has stood the test of time (almost 40 years of continuous use).

In order for us to use this system for our desktop, the same path syntax must be understood by both back-end (services) and front-end (web desktop client). Which means we need a path parser that is able to read, parse and translate an Amiga path, into a Windows, Unix or Linux path. Since Linux is my goto OS for embedded hardware I have focused on that first. With the exception of the drive-moniker (e.g “MyDrive:”), the paths are compatible with Linux and Unix out of the box.

If you have a peek in qtx.fileapi.core.pas you will find that we have both path parsing and a abstract storage device system in place already. It must just be deployed and initialized properly. It even has an in-memory storage device, called ram-disk, which is typical for Amiga OS, but which a lot of people also use on Windows and Linux. Puppy Linux actually boots out of RAM-DISK

So right now I am implementing the missing pieces so that we can start using and writing node.js back-end services that makes full use of the filesystem drivers. The old system used a hybrid websocket server, which is a normal HTTP/S server that can also deal with websocket connections. This was something I implemented especially for our desktop using Smart Pascal first, but it needs to be refactored for QTX. Which is what I’m looking at now.

The filesystem service and drivers is also something you can use in your applications. In order for it to work properly it requires a second service, the authentication service. Both use the same hybrid node.js server class – so once that is in place, both these node.js services can be used by your programs. Regardless of you using the desktop ecosystem or not.

I made some kick-ass clusters for the desktop system, QTX is excellent for getting into IOT and hardware

This is what I love about OOP, namely that we focus on widgets (components), packages and classes. And if you write the classes to be as system agnostic as possible – you can recycle the technology in a myriad of different scenarios.

Deploying large web applications

Another use case for the desktop system, is to simplify how you deploy your large web applications written in QTX. How would you go about implementing a full accounting system for web? You would need windowing (preferably), you will need file-access, you will need database access, you will need strong authentication — in short, you would end up creating the exact same technology!

The web desktop does all of this for you. Once complete you will be able to fork the entire desktop, mold it as you see fit, and use it as a ramp for that large, 60 form application that would otherwise be a mess if you implemented it as a vanilla website.

Writing Client & Server software should be fun!

Major RTL update available

It’s been 3 weeks since the last update, but rest assured that those weeks have not been wasted. In fact, I have worked on QTX every single day in from afternoon to bedtime. The changes and fixes in this update are in their hundreds. I will only list those that are of immediate interest for users to know about.

Above: The new desktop application model is coming along nicely. Still a few bugs to iron out before we create application templates for it, but getting there!

Please be advised that this update is very much work in progress, meaning that if you experience any issues then please report those issues so I can get them fixed. The ZIndex support especially is borderline experimental, since it’s one of those absolutely bizarre aspects of HTML that makes no sense from an architecture point of view.

Above: For those of you that grew up in the 80s, the Webassembly Amiga emulator will no doubt bring back memories. If you download some ADF games from the internet, you can click the file-flot and boot up the game or demo. This is just a tiny taste of what is to come, we also have x86 emulation and roughly 3000 90s games to play with (I will not provide the DOXBOX games, those can be found on torrent sites here and there).

To underline: This update contains no new .exe file, purely RTL changes! It is the same exefile as was in the last update (I have to do the RTL changes before I go in and make changes to the exe). For those of you that are waiting for IDE fixes, sit tight, they will be out very soon.

Partial changelog

  • Completely overhauled the theme.css file so it takes height for mobile platforms, make sure you copy this into your existing projects! Also replace the old default.theme.css in the themes folder with the new one!
  • SetLeft, SetTop, SetWidth and SetHeight optimized
  • MoveTo, SetSize, SetBounds, BoundsRect adjusted to call the above methods when doing changes, rather than ad-hoc setting the values
  • GetClientRect fixed, it now uses GetClientWidth() and GetClientHeight() which adjusts for the any border-padding and margins
  •  Added support for Shadow DOM, calling TQTXWidget.AttachShadow() creates a shadow-dom on an element, which can be accessed via the property ShadowRoot: TQTXWidget, or direct access to the shadow handle via ShadowHandle
  • Introduced the concept of “Synchronized widgets”. By setting the Synchronized: boolean property on a widget inside the constructor callback, the widget will wait until all it’s children are created and in the DOM (as well as itself) before invoking ObjectReady(). This is important for widgets that wrap external JS frameworks, or that needs to be 100% sure that the widget is indeed “ready” before it’s manipulated further.
  • Added a new CreateOptions flag, wmSynchronized, which developers can set if they dont want to set the Synchronized property. The function GetCreateOptions() can be overriden and you can add the wmSynchronized flag to the result (datatype is a set)
  • Introduced stacking-context functions to TQTXWidget: GetStackContextFor() and an overloaded version that also takes a filter parameter. This will return an array of each child element on a widget, sorted by their element-index. This can be used to determine how widgets are overlapping
  • Optimized BringToFront() and SendToBack(), there was no need for the RemoveChild() call prior to re-insert or re-append of the element
  • Implemented Tooltips (!) This is now in the unit qtx.dom.tooltip.pas
  • Expanded TQTXWidget with a Hint property, as well as a ShowHint property
  • TQTXDomApplication makes a reference to TQTXTooltipManager, which brings in that part of the RTL automatically. You will need to reference qtx.dom.tooltip.pas in your units if you wish to access the tooltip system directly
  • Added a InstallWindowClass() method to TQTXToolTipManager, you can use this to install your own tooltip widget if you want to style it differently
  • Changed the “body” proxy component property for TQTXDOMApplication from a component, to a full blown widget. If you want to create widgets directly on the HTML body, you can use this as a parent. Changing this to a TQTXWidget also means you can easily access things like background, styles, check the width and height etc.
  • Added a new FormContainer: TQTXWidget property to TQTXDOMApplication, this is a proxy widget that will always represent the container where forms are housed. Different application models represents forms differently, and will have different layout rules for forms. This property simplifies access to the “viewport”, regardless of application model.
  • Added Touch delegates, these delegates can be found in qtx.dom.events.touch.pas (Please note that you dont need to use these unless you explicitly need multi-touch support. Instead use the pointer-events and check the pointerType property to know if a user is operating with a mouse, pen or touch, and then implement your handling code thereafter).
  • Added the Touch delegates to the widgets in the RTL, these now show up the “add delegate” dialog for the form-designer
  • Added touch helper functions to TQTXDOMApplication, as well as the partial TQTXWidget class in various units
  • The qtx.dom.widgets.pas unit was getting rather big. Exported things like borders into it’s own unit (qtx.dom.border.pas)
  • To speed up widget creation, borders and font objects are now created “on demand”, meaning they are created when you first reference them. No point wasting CPU time creating a background object when it’s not used
  • Refactored TQTXToolbar so that it supports vertical layout (!)
  • Rewrote the CSS for the toolbar so it takes height for both horizontal and vertical orientation
  • Added support for ZOrder! Exposing the property ZOrder to TQTXWidget. ZOrder under HTML is a bit strange, since elements initially follow their elementIndex (a new widget is always topmost since it’s appended to the parent, and thus ends up at the end of the child collection). ZOrder overrides this, which to be frank is quite confusing for Delphi developers (myself included). Default value is “auto”, which in QTX means -1. So if no zindex has been assigned to a widget, it will return -1, which simply means “automatic”.
  • Implemented ZOrder functions which simplifies sorting and working with ZIndex: GetZOrderHighestFrom(), GetZOrderBoundsFor(), GetZOrderStackFor(). These are used by the RTL to deal with ZOrder.
  • Re-implemented (experimental) BringToFront() and SendToBack() so they now use ZIndex rather than elementIndex. Please note that this is experimental at this point. I have tested this with the various example apps and it works fine, but there are bounds to be edge-cases where you might have to call BringToFront() in order to remap the zindex of child widgets. Consider this work in progress! You can turn this off by disabling the $IFDEF in qtx.dom.pas, EXP_ZINDEX
  • Added a new InstallViewPort() method to TQTXDisplay, so that an application model can use a different TQTXViewport class if needed
  • Disabled older system to detect resize, which caused excessive calls to Invalidate() and Moved(). If you need automatic Resize() and Moved() calls, simply activate the ResizeObserver or MoveObserver, by calling their Observe() methods. When writing widgets you would call ResizeObserver.Observe() in ObjectReady(). This will then listen for mutation events in the DOM and call Resize() automatically whenever your widget has it’s size changed. The MoveObserver works the exact same way.
  • Expanded TQTXDisplay with methods that performs the layout, StdLayout(). TQTXDisplay will organize it’s children vertically, subtracting the height of any widgets you might have created there directly, and resize the viewport to cover the reminder. By isolating this logic in it’s own procedure, different TQTXViewport than the default class (see InstallViewPort() above) can perform changes to the default layout regime if needed.
  • Implemented a new widget, TQTXDOMIFrame, which creates an IFrame element. Please note that this widget still needs work. When finished it will also expose a message channel for communication between “apps” or third party websites.
  • TQTXWindowElement, the baseclass for all TQTXWindow parts, now inherits from TQTXSyncWidgetContainer
  • TQTXWindow is now synchronized, since that works better when hosting external websites as the content
  • Added a new Window type, TQTXFrameWindow, which contains a TQTXDOMIFrame widget
  • Implemented a new application model, this is still being worked on, but you can find it in the test project “Windowing app”, which is ultimately the desktop project I have been showing lately. This project also has an Amiga webassembly emulator — make sure you open this project in the browser, as miniblink does not support WASM.
  • .. And much, much more!

I could literally continue for at least 2 more A4 pages of changes and fixes, but the above list represents the biggest changes to the RTL so far.

What to look out for?

Since I have disabled the old resize system, which ultimately tied into BeginUpdate(), EndUpdate() and logging of changes between those two methods, some of your existing projects might be affected by this. The solution is to activate the ResizeObserver object in your widget ( a simple ResizeObserver.Observe(); will do).

The ZIndex change is quite huge, in that our codebase has so far only dealt with ElementIndex (creation index) with regards to BringToFront() and SendToBack(). But so far I have not seen any dire consequences for this change. If your project is affected, you can turn off the ZOrder support by disabling the EXP_ZINDEX $IFDEF on top of the qtx.dom.widgets.pas unit.

I have completely overhauled the theme.css file so it takes height for mobile platforms, make sure you copy this into your existing projects! Also replace the old default.theme.css in the themes folder with the new one!

Download

You can download the RTL update from www.patreon.com/quartexNow as usual!

How to use TQTXColumnPanel

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.

Above: A TQTXColumnPanel with 3 TQTXDOMDiv widgets, each with a displaymode of cdBlock and positionmode set to cpInitial

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.

Above: Miniblink does not support the column attributes. Run the code in the system browser instead.

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.

Above: If you have multiple browsers installed you can tell the IDE which one to use in the preferences dialog

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.

Above: Running your project that uses TQTXColumnPanel in a full browser renders the output correctly

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:

Above: Sometimes it’s just easier to work with HTML from code, although it does mean a bit more typing

You could also have simplified the whole process even further and dropped the TQTXDOMDiv widget’s completely, by writing this:

Above: You can insert raw HTML into any widget and the browser will create the elements for you. However, this approach means you wont have pascal widgets to play with that easily. But for static content, this is by far the easiest way

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.