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 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.
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.
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:
Align: TAlign now works brilliantly
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.
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).
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.
TQTXSyncWidget will be depricated since there is no longer any difference between TQTXWidget and TQTXSyncWidget.
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:
Create child widgets or objects
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:
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!
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.
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
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!
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.
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.
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.
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:
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:
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.
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.
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.
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.
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.
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!
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:
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:
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.
One of the major benefits HTML5 / JavaScript holds over native applications is the power of CSS (cascading style-sheets). CSS is a simple way of describing the visual attributes of something, from the size and color of the font to the thickness of borders and even animations. You can even position elements in 3D space if you like using nothing but CSS.
A CSS file contains the definitions of such styles called style-rules, and once defined you can change everything about your UI with barely any effort. A style rule simply defines a list of attributes and the values you want them to have. Here is an example of a style-rule:
Note: While the technical term for such a definition is style-rule, most people simply say style. So when I refer to a style in this article, unless otherwise stated I am referring to a single style-rule definition.
A HTML application can load and use any number of CSS files, which makes it easier for developers to organize and manage styling for their applications. You can have animations in one file, widget styles in another and color definitions in a third. There are some very good CSS visual editors online as well, that you can use to generate complex CSS. Then you can copy and paste the result into a CSS file and use it in your QTX projects.
CSS is vast and covers a plethora of attributes, but for sake of brevity we group the attributes involved into four distinct categories:
Behavioral attributes: width, height, padding, margin, clipping and layout
Animation attributes: rotating, flipping or moving an element using hardware acceleration, or moving an element in 3D space
Typeface attributes: font names, families, their weight and orientation (note that I did not list color, since that is a visual attribute)
There is much more to CSS than these four categories, but if you are unfamiliar with CSS – these four gives you a rough overview of the terrain. Every inch of a HTML element can be altered in some way through CSS, the question really comes down to how do we organize this in a system that makes sense.
This is where the QTX theme ( or scheme ) comes in.
QTX Style Guidelines
In order to bring uniformity and structure to how component writers work, the IDE operates with a few basic laws or guidelines for using or creating your own themes. These laws are simple and non-intrusive.
If you want to deal with the styling yourself and ignore how QTX does things, that is not a problem! It is very important that QTX don’t get in the way of whatever styling developers write themselves, or collide with existing JavaScript frameworks and their styling schemes. But let’s look at how the RTL is rigged to work with theme-files, which is just a normal CSS file with a set of pre-defined, named style-rules.
First, these are the guidelines for those that wish to create their own theme files. These guidelines must be followed since the RTL and IDE expects this scheme to be in place.
The theme-files divides the styling of a widget into 4 parts: border, background, font and behavior. We dont have styles that mix the attribute groups I mentioned earlier. A border style should just affect the border, a background style should just affect the background. In the RTL we apply multiple styles to a single widget, this is how you can change the border without affecting anything else (!).
Border styles must only affect how a border is visually represented
Background styles must only affect the background
Font styles only affect the font size, weight and pitch. You can set the name of the font if you wish, but the theme-file defines a font for the entire theme, which the rules inherit from. But you are free to override this if you wish.
Behavior, size and layout should exclusively be defined in a style that match the Pascal class-name of a widget. TQTXToolbar, TQTXButton and so on all have corresponding style-rules in the themefile.
All TQTXWidget based controls will add their own pascal class name to the style-collection of it’s HTML element (all widgets create and maintain a HTML element, in most cases this is a DIV) during initialization of the widget.
Widget writers should provide a CSS file in their package that contains style-rules for their Pascal widget class-names. This ensures that element + basic behavior is established instantaneously on creation.
The last 3 items of these guidelines will be confusing to anyone unfamiliar with CSS, they mostly affect advanced developers that write their own packages
Ad-Hoc example
Let us have a look at a direct example of the style mapping mentioned above. First, let us write the smallest possible TQTXWidget we can think of:
You can ignore the [RegisterWidget] and [RegisterInfo] pascal attributes, those just ensure that the IDE registers the widget on the component palette in the IDE when the package loads. Just focus on the class definition for now. And yes, this is a fully working widget, albeit a bit useless.
With the pascal code in place, we need a matching CSS style-rule. Like mentioned already, the widget will add it’s own Pascal name to the list of styles it uses – so all we need now is to have a CSS rule with the same name (note: user styles are prefixed by a dot [.], this is not custom for QTX but a part of the CSS standard).
The above CSS is a fair starting point, and like the guidelines explains, automatically mapped styles should only define behavior essential to the widget. Attributes like padding, margin, minimum size, anything that helps setup the structure of the widget rather than it’s visual attributes.
With our pascal widget and CSS setup, let’s provide a default border and background for the widget. This is done by overriding the StyleObject() method and setting the ThemeBorder and ThemeBackground properties there:
Widget writers who deliver packages with new and cool widgets for developers to use, just have to provide a CSS file inside the package. When a project using that package compiles, the data-files are copied into the current project automatically by the IDE. The user just have to add the styles to his index.html document. This is literally a one liner operation.
This is the beauty of the QTX scheme. The extreme ease of use, letting the browser deal with as much composing as possible, and try to match things up through logic rather than brute force.
The new qtx.dom.theme unit
The theme system operates with a set of named styles that all widgets must follow. Sure, you can roll your own and ignore the system, that is perfectly fine, but the RTL based widgets are all rigged to use the QTX scheme.
A fair bit of thought has gone into this system, and the borders, backgrounds and colors are made to compliment each other. Developers making their own CSS theme files obviously should ensure they pick colors that go well with each other. For example, the border TThemeBorder.tbButton is made to work well with TThemeBackground.bgButton background type.
In the upcoming release a new unit has been added to the QTX.DOM namespace, namely qtx.dom.theme.pas, which contains the enum types used by the theme files and IDE scheme.
Most importantly is that TQTXWidget has been expanded with two new properties:
ThemeBorder: TThemeBorder
ThemeBackground: TThemeBackground
These enum types are straight forward in what they represent, and they are simply used as keys into a series of lookup tables in the unit dealing with name-to-type conversion.
You don’t really have to deal with this unit directly unless you want to. The new TQTXWidget properties are available from the form designer and widget inspector.
From the form designer and widget inspector, it’s straight forward. You can change the border and background of any widget.
Windowing is special
The windowing UI that QTX ships with requires a bit more than just border and background styling, so that is somewhat bolted into the theme-file. However, a window is clearly defined in parts (header, footer, content region etc.) so new theme files will definitively contain new look and feel moving windows too.
I am considering separating the window styling into a separate file, so that it’s not subject to whatever color preferences has been set by a single theme. This will require a bit of contemplation before I change it since the window structure is far more complex than a simple, composite widget.
Well, I hope this was informative! More info will be available shortly!
This article was published on our Patreon site and was available to backers in May of 2022. It is re-published here as part of the drip from what backers have access to and what non-backers have access to.
I dont often write about hardware on this page, but as we near code-freeze to the IDE and development tools- my thoughts return back to our original project, namely the Quartex Media Desktop (codename: Amibian.js).
Just to underline: You dont need a SBC (single board computer) to work with Quartex Pascal. JavaScript runs anywhere, which is the point of making a development toolchain like this to begin with. This article is about working with IoT and embedded devices, which is completely optional.
Since I have written about Quartex Media Desktop before, hopefully everyone understands the value and power that brings. So instead of going into a pitch about that, let’s instead define a use-case where getting a dedicated IoT board makes sense. (Note: If you are not familiar with the Quartex Media Desktop, below is a video that demonstrates the prototype).
The ticket booth scenario
Let us for sake of argument say that you get hired by a large company to deliver a ticket system. The company provides you with a touch display and casing (booth) to house whatever hardware you request to finish this project. Your job is to sort out operating system, back-end technology, commercial dependencies like software licenses -and further, to implement the ecosystem that the customer will see and interact with.
This is where things start to get interesting, because such a ticket system has to scale. There wont just be one such device in use, but rather hundreds – if not thousands nation wide. As a growing business the company hopes to take the product internationally, which could result in hundreds of thousands of devices being deployed around the world.
There are many factors to take into account here. In a distributed system that needs to scale, most of the pressure will be on the back-end (server) side of things. I would not attempt to simply “roll my own” back-end for a project of this magnitude, but instead make full use of Docker and Kubernetes to deal with the scaling aspect, while I would write the QTX server to make full use of available CPU cores and process clustering.
Define your products life-cycle
While the back-end will by far be the most complex piece to engineer, it ultimately holds less surprises. Like i mentioned above, technologies like Docker and Kubernetes pretty much solves everything with regards to scaling and meeting demand. What is more important for our ticket booth system, is to make sure we pick an SBC that performs well, and that will continue to perform well as our software grows in complexity.
This period of time is simply called the a products life cycle, with the EOL (end of life) marking the date where a company must upgrade their hardware. What duration we end up with ultimately boils down to growth in complexity, the amount of data that must be represented, and underlying non-visual complexity. For sake of argument, let us define a lifecycle of 3 years. Personally I would try to find devices that can deliver 5 years, but the difference in cost can be very high between the two.
Right out of the gates the Raspberry PI falls short. The first thing on my mind would be to find an SBC that can handle temperatures well, especially cold weather. If you plan to deploy the device to a region with cold winters then you can forget about the Raspberry Pi. You will need insulation in your booth, but for devices not designed for -30 degrees celsius, you also need an expensive regulator and heating elements. It will be more affordable to pick an SBC that can operate at -35c degrees celsius, and instead make sure you have good insulation. With active cooling the board will make sure the insulation doesn’t cause over heating in warm weather.
But for performance, which is ultimately where your code comes in, the Raspberry Pi is not quite up for the job. Sure, it will run Amibian.js just fine, but the performance will always lack “bite” to deal with demanding tasks, especially graphics intensive applications. A ticket booth system will obviously not be “intensive” compared to say, running Quake in the browser – but you dont want something as simple as a few video elements to potentially ruin the customer experience.
Define your low-end mark
Running Quartex Media Desktop (read: amibian.js) doesn’t require that much CPU power for the core system. The system is based on 5 node.js services where only 3 of them are critical for operation, the other 2 are optional. These services can either be installed separately on their own SBC (hardware cluster) or collectively on a single SBC. The node.js services use exceptionally little memory and they are just as fast as any service written in native languages like Delphi (or .Net C# for that matter).
When running a kiosk system with Amibian.js, you adapt the Linux setup to only run your software. You dont actually boot into the Linux desktop, instead you boot into Chrome in it’s own X-window session. Chrome will then run fullscreen in Kiosk mode and load the desktop from localhost. Once loaded into the browser the desktop connects back to localhost via websocket, and use Ragnarok messages to invoke API functions, perform file operations and run programs.
So the boot sequence for our SBC will look something like this:
Bootstrap loads kernel
Kernel loads Linux drivers, services and initialize
PM2 loads our node.js services and starts them
Our Node.js services use ZConfig to find each other
Each of our services register with the Amibian.js core service
Chrome is started in Kiosk mode with URL set to localhost where the Amibian.js core service hosts a webserver supporting websocket
Desktop connects back via websocket. All API calls happens via Ragnarok messages over websocket. The core service automatically routes API messages to whatever service is registered to deal with them, and likewise routes the response back to the desktop instance that issues them. In many ways the services act as distributed library files.
So if you install Amibian.js on a single SBC, the experience is indistinguishable from booting a normal, native Linux desktop.
The thing you want to factor in, is the graphical performance of the SBC. While you can run the services on any cheap board, the one hosting the desktop display must by necessity have enough muscle to render the display without issues. This means first and foremost a beefy GPU, but also a processor and RAM enough to delegate everything. A browser is perhaps the single most demanding piece of software in existence today.
As a bare minimum I would recommend:
4 GB of memory
ARM v7 (Quad core A73 at 2.4 GHz)
SATA interface and boot support
eMMc boot support
RF / Bluetooth support
The Raspberry PI v4 does hit these marks, or at least it can easily be expanded to cover this list. But keep in mind that this defines our lowest point. This is the absolute minimum that I would recommend for using Amibian.js as your software delivery platform.
As of writing the prices for SBC’s have gone through the roof, and even a Raspberry PI v4 with 4Gb of ram retails for over $100. Which is ridiculous, it’s not worth that by any means unless you have some very narrow educational criteria hanging over you.
A much better buy, one that I picked out two years ago, is the ODroid N2. Not only does it check all the criteria, it actually has 2 extra CPU cores (based on the little-big architecture, where you have 4 powerful CPU cores, and 2 weaker cores). Not long ago the ODroid N2 was replaced with the ODroid N2+ which is roughly 30% faster than the original N2 yet retails at the same price. You can pick up this device for $83 at Hardkernel. And it’s worth every penny for general purpose development. It also acts as one hell of a games system if you want to give your kids a system to play Playstation 1, Sega Saturn and Gameboy Advance type retro games on.
But would I pick the N2+ for a ticket booth with a 5 year lifecycle?
The new breed of SBC’s
Most SBC’s are based on tech created for the mobile phone marked. Meaning that mobile phones drive the development of these SoC’s (system on a chip). The companies that deliver these SoCs want to capitalize on the production infrastructure they have setup, so after the contract for a phone expires, they usually offer the same SoC to embedded and IoT companies – which builds an SBC around them.
There is usually a 5 year delay between a SoC being used in a phone, and the time it becomes available as a fancy new SBC in the IoT sphere. A good example of this is the Snapdragon 880 series which powers expensive phones like Samsung Galaxy Note 9. The 880 series delivers performance comparable to an Intel i5 (first generation), which is incredible value for money.
The coolest board right now is the newly released Firefly SBC. This is not based on the Snapdragon, but rather a brand new RockChip CPU design – but it delivers performance better than Snapdragon 880. It is fitted with a powerful GPU that can handle 8k video and display modes, and you can buy a model with up to 32Gb of RAM (!) – which is a first for SBC’s I believe. I have never seen an IoT SBC with more than 8Gb.
Since manufacturing parts are scarce right now, I was not surprised to see that it retails at well over $200. But when you consider that a Snapdragon 880 development board sells for $2000 – I wouldn’t think twice about giving the Firefly a testdrive. And yes, it ticks all the boxes in our list (it’s way ahead of our minimum). First of all It’s based on ARM v8 (a whole generation ahead of our lowest mark), it has NPU chip which is used to run A.I models, it’s data transfer speeds is astronomical compared to our lowest specs – and you can fit it to an ATX motherboard and use standard PC cases if needed.
So if I was to pick a board right now for our 5 year lifecycle, I would go for the Firefly. If we lower the lifecycle to 3 years, the ODroid N2+ is just impossible to beat. I should underline that GPU support for X has been an issue for the ODroid N2, but that was solved via the Bifrost driver update a while back. But I would make absolutely sure and test it early if you plan on using it.
An alternative to the Firefly board, is Radxa: Rock 5b. This is based on the same RockChip RK3588 SoC that the Firefly uses. Price is significantly lower, especially if you pre-order, but to stay below the $200 mark you are limited to the 16Gb model.
So right now we are in that sweet 5 year spot where the previous generation of SoC’s are being repurposed for IoT devices. Although the RockChip 3588 is the exception rather than the rule. It is either way miles ahead of all the other board out there – and I would not buy anything below this is performance, unless your menu system will only see moderate improvements in the years to come.
Never buy boards for today, always try to think 5 years into the future.
Why not use X86?
You are probably wondering why ARM is the preferred platform in my articles, why not opt for a cheap and affordable x86 board? Intel in particular has dropped it’s prices and there is a lot of good SBC’s that are powered by overclocked Atom CPU’s out there.
Well, there is nothing in the way of using x86, at least at first glance.
A while back there was a movement in the US called “the right to repair” movement. They actually took Apple to court because Apple has consistently gone out of their way to stop people repairing their own machines. Apple even went as far as to fill machines with glue, just to make absolutely sure that nobody could repair, optimize or alter their own devices.
During that trial, which Apple lost, a lot of information was presented about ordinary x86 hardware, information that shocked a lot of people (myself included). Where everything from the firmware to the actual, physical hardware is rigged in in their favor. Microsoft especially has made it damn near impossible to buy a PC without Windows. There are some that allow you to install Ubuntu and pick between the two, but the whole marked is so heavily dominated by third parties that I dont see this changing any time soon.
When you add to this the facts that surfaced about Intel’s SoC’s, namely that they have several back-doors in the SoC design, bolted into the very hardware, that allows them to access your computer remotely – typically in concert with government agencies, suddenly x86 becomes a security risk because these back-doors were first discovered by hackers.
My reluctance for x86 has nothing to do with my personal activities, my digital life hardly warrants interest enough for any remote system incursion, be it the government or Intel. The point here is the principle of the matter, what people do with their computers is nobody’s business. And companies using “financial incentives” to dominate a marked that should be based on fair competition, is not something I will support of condone. Tese tech giants have even gone so far as to have legislation changed to suit their criteria. You dont have to be a rocket scientist to see where this is going, and what that means for consumers.
I should add, this is the real reason why Apple decided to create their own hardware. They have sold their new SoC as being about performance, but it’s actually about getting away from the legislation surrounding x86, parts and repair laws (especially here in Europe). By bolting as much as possible into a single part (SoC) they can ensure that customers are 100% dependent on them, and have to buy a new device. After all, If there are no parts to buy, you cant argue for any rights to repair either.
That’s the kind of people you are helping to fund when you buy their products. I haven’t bought an Apple device in over a decade, and I never will. Nor will i ever buy an Intel PC.
Bricked x86 SBCs
A good example of how controlled the the x86 marked has become – is to have a look at the x86 based UP board. Out of the box this is a good value for money board with the same form-factor and ports as a Raspberry PI. It used to sell for around $120 and is more than capable or running Amibian.js and tick all our boxes. It even has an on-board eMMc drive.
The problem is that their Firmware is bricked to only run two operating systems, namely Windows 10 or Ubuntu. When I asked them how I could boot other operating systems, such as Aros, Android or Haiku (a modern, open source re-implementation of BeOS), they simply dismissed me and referred to their documentation. There is nothing in the way technically for the board to run these alternatives, they would even run exceptionally well – but it boils down to financial incentives: they get paid by Microsoft for each license installed.
So to be perfectly honest: Companies dictating what type of OS you are “allowed” to run on your own device are persona non grata in my book. I simply ignore them and never buy anything from them.
Besides, with so much power and freedom in ARM these days – why on earth would you want to use an x86? The power consumption alone should be enough to say no thank you!
Next article
In this article we went through some of the boards available out there, and had a peek at thinking in life-cycles. I am going to continue writing about this scenario, and in my next article we will have a look at PM2, the process manager we use to run our node.js services in the background when your device starts!
This build contains fixes for bugs that has been lurking around the RTL and IDE for some time. Each revision unveils less and less items to fix, and I believe we are only a couple of builds away to mark the binary as a release candidate! It was a bit sad that the latest DWScript that was going to ship with this build was unstable, but rest assured that we will fork the latest and greatest once the DWScript codebase has been stabilized again. Eric is not one to leave bugs hanging for long.
Fixed a problem with TQTXDynamicApplication model where the ShowForm() failed due to mistaken reintroduction
Fixed a problem with TQTXBoxedApplication model where a similar reintroduce caused issue. Both of these further affected the “ShowForm” method, so it was very fortunate that we fixed these now
The codegen omitted width when emitting code for TQTXBoxedApplication forms, which it should — this has now been fixed
Gave all widgets in the RTL icons. The default green puzzle piece is now reserved for clean HTML definition components (e.g 1:1 wrappers over direct HTML tags, like <Table> or <option> style elements). It also remains as a default glyph in case someone register a widget without a glyph
All widgets have been given a [DefaultName(‘<name here>’)] attribute, so that when you add a widget it is not named “widget1” or “Widget2” but rather “button1” or “listbox1” — which is what you expect and have in both Delphi and Lazarus.
Added a clean TQTXLeafletMap example, which demonstrates RT’s leaflet map package. Also gave that widget a google maps glyph (not sure it actually uses Google maps, that that is easy to fix).
Fixed a problem where WWR (web worker units) was not properly parsed and thus had no unit overview
Went over the code that resolves unit-file names, so it’s faster and more maintainable
.. and much, much more!
Future changes
With the TQTXBoxedApplicationModel finished, it is time to add that model to the projects menu. So in the next build I hope to have the new project template in place. With that however, the application object’s constructor (which is currently exposed in app.entrypoint.pas) will be consumed by the {$I “app::form.init”} code generator insertion point. Why? Because the application model for a project should not be changed by code, but rather in the build-config. If you change from say, a boxed model to a dynamic model, the only way the IDE can alter the constructor of the application object – is if it controls that snippet of code.
This means that examples have to be gone over, and I also need to adjust the project templates. It is an annoying thing to do in the 11th hour but will do a lot to the user-friendliness of the product, and also leave less confusion as to how to switch model in an already existing project.
Download
Backers can download the latest binary from Patreon