A couple months ago, I watch the video replay "The fundamental secrets of good UI design" done by Ian Barker. Around the 33:15 mark, Barker talks about supporting "dark mode", have a listen:
Transcript:
"One of the things you really should do at a very bare minimum if you do nothing else please support dark mode. And this is kind of a personal beg from me. As I said before I do get these floaters in my eyes, I can see perfectly well you know, I'm not blind or anything like that. I'm shortsighted which is why I have glasses but if you support dark mode you will make my life a lot easier. And other people that have visual issues as well some people get migraines and things like that dark mode helps."
Until I watched this video, I never considered supporting dark mode in the current rewrite of my Zilch software. So I began looking into how to incorporate FMX styles. It was confusing at first. Then I got the hang of it. I decided to go with the "Air.Style" that comes with Delphi.
There were a couple of tweaks I had to make. The Air.Style is a vector style which means it does not use the bitmap designer. It is similar to how Cascading Style Sheets are used in web development. The graphic elements are drawn using SVG paths.
FMX Main Menu component can't be styled
The biggest surprise was the TMainMenu component. It turns out the FMX Main Menu component can't be styled. The Embarcadero FMX Style Viewer was showing a Main Menu that was styled how come my Main Menu wasn't styled?
FMX Style Viewer - Air.Style
I didn't want my application to have some stuff styled and some stuff not styled. Then I discovered FMX includes a MenuBar component which does take styling. And that is what is used by the FMX Style Viewer. So, I slowly and methodically swapped out the MainMenu component for the MenuBar component.
Always use the MenuBar for FMX desktop applications
FMX MainMenu vs MenuBar
My recommendation is to skip using the MainMenu component when developing FMX desktop applications and ALWAYS use the MenuBar component.
The upgrade of my flagship product from Delphi 5 VCL to Delphi 11.3 FMX is finally at the presentation stage. I've spent the last six weeks thinking about and learning about data visualization.
I've never used FastReport before. My current Delphi 5 VCL product uses QuickReports. I did find a great FastReport video available through Embarcadero Academy called Getting Started with FastReport by Cary Jensen. Jensen mainly focuses on the VCL version of FastReport.
I've never used FastReport before
I was surprised at the huge difference between the VCL and FMX versions of the FastReport Embarcadero Editions. That's probably why Jensen focused on the VCL version in his video. These are the palette entries for Delphi 11.3 Professional. (VCL on the left, FMX on the right)
I'm interested in the FMX version of FastReport because I'm developing a desktop software product which targets Windows and macOS users. Because I'm new to FastReport, I want to see how far I can take the "Embarcadero" version of FastReport before I commit to purchasing the full version.
Unfortunately, the Embarcadero Edition of FMX FastReport has no export capability. See the Palette image above.
I was surprised at the huge difference between the VCL and FMX versions
Because I'm targeting both Windows and macOS users I need to know if there are any stark differences between those platforms. I created a comparison chart which highlights these differences. This is the same data from the Fast Reports website sorted by features with missing capabilities highlighted.
Here is the same comparison which filters out any features that are unavailable across all platforms.
As you can see, the Embarcadero Edition of FastReports 2.0 FMX is very limited.
The Bottom Line: Because my application is cross-platform for both Windows and macOS, it looks like I will have to purchase the full retail version of FastReport 2.0 FMX in order to give my customers the experience they expect.
Working with Date data can be very tricky. I recently encountered an "Invalid argument to date encode", error while trying to update a SQLite database table.
This placed a value of 0000-00-00 into the date field of my SQLite table.
The getter function GetOneOffDateAsDate passes in a TDate which doesn't play nicely with FireDAC. Fortunately, the fix is quite simple. I found a fantastic explanation for this error on stackoverflow which states FireDAC expects DATE data type values to be a string in the fixed format of YYYY-MM-DD.
FireDAC Expects DATE data types to be strings formatted as YYYY-MM-DD
So I created another getter function to format the date data as a YYYY-MM-DD string. Problem solved!
I'm currently updating an old Delphi 5 Desktop VCL application to to Delphi 11.3 FMX. And one of the capabilities I want to provide is the ability to launch several webpages from within the application. I want to place a link in the main menu to my YouTube channel so customers can easily get to product videos. And there's also a link to my website in the Help > About box.
It was fairly straightforward the last time I did this using VCL because all I had to worry about was the Windows side of things. However, because I want this application to run on both Windows and macOS it presented a challenge.
The Delphi IDE won't recognize the Macapi namespace unless the target is set to MacOS 64-bit
Harry Stahl covers the COCOA API on pages 98-99 of his book Cross-Platform Development with Delphi. He also gives an example of how to use the NSWorkspace object of Macapi.Appkit. However, he doesn't show how to setup the uses clause.
I also found a fantastic reference on stackoverflow by David Heffernan that was written in 2015. However, there are two issues with Heffernan's if you are looking for a complete answer:
There is a reference to a blog post by Malcolm Groves called Opening files and URLs in default applications in OS X which is no longer available or accessible.
The example doesn't tell you you need to target the MacOS 64-bit platform before the IDE will recognize Macapi namespace..
The Delphi IDE won't recognize the Macapi namespace unless the target is set to MacOS 64-bit. Shame on me for not reading up on the Embarcadero docs. Wrapping my head around how to use the {$IFDEF MSWindows} and the {$IFDEF MACOS} was a little tricky But I eventually caught on.
After a couple hours of going back and forth with code that worked for Windows but didn't work for macOS. And code that worked for macOS but didn't work for Windows, I finally got Heffernan's example to work.
The next step was to extract the code out of the main form and place it into it's own unit. And that is the code I'm sharing with you today. I hope you find this helpful.
I'm in the process of converting an old Delphi 5 VCL application to Delphi 11 FMX. Yeah, I have Delphi 12 but I'm waiting for the first bug release before I start using it. Well, this morning I learned a valuable lesson. A lesson which stopped me dead in my tracks.
There's nothing stopping you from creating an already existing type!
I was making some refactoring changes to my code. I didn't realize I was about to make a huge mistake. I went plodding ahead pleased with the progress I was making and admiring the beautiful, refactored code I was creating. When boom... I renamed a type that stepped on a type already inside the System.UITypes unit.
There's nothing to stop you from creating an already existing type. Of course I didn't use the preview changes function. I just hit go!
I had defined my own enumerated type called TMyImageIndex. I decided to just simply call it TImageIndex. So, that's what I did using Refactor > Rename type TMyImageIndexCTRL+SHIFT+E
I knew I messed up when I saw way too much code show up in the results panel.
How dare Embarcadero define a type called TImageIndex
WTF. If it already existed the IDE should have prevented me or at least warned me. How dare Embarcadero define a type called TImageIndex.
I am a big fan of Harry Stahl's book "Cross-Platform Development with Delphi 10.2 & FireMonkey for Windows, MAC OS X (macOS) & Linux". I purchased this book in May 2021 and it has helped me several times as I'm porting my Zilch application from Delphi 5 VCL to Delphi 11 FMX.
I like to maintain RSS feeds of my favorite Delphi blogs. So, I went looking to see if Stahl has a blog I could add to my Outlook RSS Feeds. I found a couple of Stahl's websites. He does have a few blog posts but they are not easily converted into an RSS feed.
When it comes to Delphi, it's amazing how much I don't know, once knew but forgot, or just plain skipped over because it was too mind-boggling at the time. I took a break from reading Alister Christie's new book "Code Better in Delphi", which I purchased last night, to write this blog post.
While reading the bit on "refactoring", I realized how little refactoring I actually put into practice. Yes, I have Fowler's book, the second edition. And no, I haven't read it. I've thumbed through it a little in the past. I even tried looking for the "Introduce Explaining Variable" refactor Christie discusses on page 53 of his book. It turns out Fowler now calls it "Extract Variable". Keeping Fowlers "Refactoring" book on my desk, and referring to it often, is one way I am going to code better in Delphi.
It's amazing how much I don't know or once knew but forgot
I just started reading "The Observer Pattern" in Christie's book and had to stop. I bought "Head First Design Patterns" by O'Reilly, quite a few years ago. I have the 10th Anniversary edition with code examples in Java 8. My intent was to install Java on my machine and force myself to learn these patterns by following the code in the book. It didn't go so well. The authors do a great job using story to make learning fun. I just couldn't wrap my head around Java.
Christie has managed to reignite my desire to learn these patterns. I downloaded the Java source code. I plan to work my way through "Head First Design Patterns" pasting the Java source code into ChatGPT, and asking Chat to convert it to Delphi.
I will navigate through each pattern using this method. I will then revisit the same pattern in Primoz Gabrijelcic's book "Hand-On Design Patterns with Delphi". This is another way I'm going to learn to code better in Delphi.
Another way is to revisit the Model-View-Controller method of code separation. Here is a the MVC song from 2007 I just learned about today.
And I also want to implement "Interfaces".
Here's my list of how I will code better in Delphi:
Finish reading Christie's book
Practice refactoring often
Learn and implement design patterns
Discover and utilize ways to separate code concerns such as MVC or MVVM
Overcome my stumbling block of Interfaces
Use the comments below to share how you will code better in Delphi.