Last month, I discovered the software application I wrote atop FLTK would not render text at the proper size on 4K resolution monitors. The version of FLTK I used was designed for 1080p monitors and earlier. During the last 4 years, I looked at using C++ code libraries to build GUI applications. I came across numerous code libraries that would provide a means to produce a GUI in C++, but I had to try them out first to see if they would be a good fit. Two of the most popular ones, Qt and wxWidgets, I studied in-depth before I decided not to use them at all.
Several libraries seemed promising. I started with Win32 but that was not cross-platform. I looked at Allegro but decided it was not productive enough. SFML was a little more productive but had too many dependencies. Eventually, I settled on FLTK. The great thing about FLTK was it was nimble, compact, and fast to set up and deploy and required very little to get up and running. I built an application using FLTK and was satisfied with the result until I ran it on a system with a 4K monitor.
Reconsidering the results of using FLTK meant going back to the drawing board. Years passed since I last reviewed a multitude of code libraries and frameworks. I knew that I could get a thorough implementation of the software using Qt or wxWidgets as they were mature and feature complete solutions. I am just a tad on the bleeding edge in terms of core compilers, C++ language version, and depth of control I would prefer in the software and those toolkits are not designed to keep pace with that. I have other reasons not to prefer those toolkits. I began to consider Allegro again.
One thing I wanted that FLTK delivered was productive, high-level widgets that I could declare in code and just use. FLTK already has things like buttons, scroll bars, list boxes and such that I could just use and link to data. Using pre-built widgets such as you have in frameworks like .NET, Java, and HTML seemed like a productive way to go. I would gain the use of code I didn’t have to write. That point of view had a flaw.
As of 2016, code libraries such as Allegro and SFML do not have widgets. When using them as your primary graphics definition tool, you have to create any widgets you need from scratch. It was my hope over the last 4 years to avoid that and reap the benefits of productivity. I understood that if I made my own widgets, I would have more control and a greater ability to adapt to changes in technology and hardware. I also knew in the back of my mind that if I was wrong about using a high-level, productive framework like FLTK or something similar, that I would probably have to write the application-level interactive graphics solution from scratch.
October 2016 came around and it quickly became apparent that to support the latest compilers, C++ standards, display resolutions, and other aspects of an operating environment writing from scratch using libraries limits you less than assembling widgets using third-party frameworks. I still recommend widget frameworks for IT departments, but for true world software, even for personal use, I recommend going with code from scratch. Even with the hassles as the issues will be far less than waiting 6-months to a year or more for a framework to get updated.
C++ GUI using Allegro 5 Graphics, dlib Geometry, and GCC 6
One of the most promising aspects of the Allegro 5 Graphics Library is are high-level drawing routines. The great thing about that is that is very productive if you are comfortable with geometry. Since that was one of my favorite areas of mathematics, I was pleased to see the presence of such high level routines in Allegro. It sped up my definition of lines for the widgets I needed to create.
Drawing the squares and other angles needed was fairly straightforward. What was unclear was the interaction needed to achieve the appearance of movement across a geometric plane. My concern was the speed of the operations to detect mouse movements within a certain area. I needed fast algorithms to achieve this goal. That is where the dlib library becomes useful.
Before I decided to use dlib, I had defined my own geometric primitives. As I got further into the new solution, I realized I was writing too much code and was going too in-depth into areas that had less to do with the application and more with the mechanics of geometric translation. I decided to replace the geometric primitives I had defined with those from the dlib library. Once I did that, it actually became easier to visualize the high-level models I needed to create to produce a more algorithmic process to convert from mouse interactions to movement of lines of text on the screen.
In the past, I’ve used both GCC and Clang at the same time to make sure I was qualifying the code from the perspective of two compilers. This time, I decided to stay with GCC to speed up my code iterations a bit. I plan on exercising the code through Clang at some point, but for now, the code has been vetted only through GCC. I am pleased with the results so far with an executable that is 5.7 MB in size with a debug build.
C++ GUI at 4K Resolution
The series of screen shots that follow show the example application output to a monitor at 4K resolution. Even though I am on Linux and using open source technology, I have to thank Microsoft for the font scaling algorithm. They have a web page where they describe their method for font scaling. The web page does not show actual code for font scaling, but I translated the textual description to a formula. After a few revisions, the formula results in font scaling that appears to work well at different display resolutions.
Screen shot 1 shows the full window with example, auto-generated text. I auto-generated 100 lines of text. I wanted enough lines of text that I knew would not fit into any of the graphical regions I setup. I have 3 graphical regions and with truncated text, the conditions are such that I could examine both the size of lines of text and see how well scroll bar functionality operated.
Screen shot 2 shows the same window manually resized by mouse down to 1920 x 1080. The screen resolution is still 4K but this resized window shows the graphics layout automatically adjusted to the new window dimensions.
Screen shot 3 is where I clicked the mouse within the vertical bars on the right-side of each of the 3 graphical regions. The text scrolled in response. The final touch was to have so when I dragged the mouse down the vertical lane, the text scrolled continuously to correspond to the scrolling. The scroll functionality works, but one additional step is to put in logic so that when the visual scroll button reaches the end, scrolling continues bring text into view that is still beyond the upper bounds of the visual display region. However, the functionality that exist is an excellent start in that direction.
One question you may have is why Orange, Red, and Sea Green? The colors were chosen, not for any aesthetic reasons, but to create sharp contrasts between graphical regions so it was clear when things were working or not working. That way, when all the issues are ironed out, the visual design of those regions can simply be swapped out. When I eventually use this code in a real application, it will have a more refined and polished appearance. Today, however, the focus is on getting the foundation right so that any polish can occur in a context in which you are not troubleshooting both the form and the function.
C++ Code and GNU Makefile
You will notice that the code uses very few directly declared pointers. I was reading Kurt Guntheroth’s great book, Optimized C++ and I am near the end of the book at the 3rd chapter from the end. His insights into memory allocation has been influential in the decisions I’ve made in how I’ve designed the code. I’ve leaned more in a practical direction than I’ve been inclined in past versions of similar solutions.
The Makefile is how I compile and link the software. The following is the Makefile that I put together. You will notice that the libraries I use are several levels up from where I compile and link. If you try to compile the example software, it doesn’t matter where you put the libraries as long as they are reachable from where you compile and link.
The next two code files are the header and implementation for PrimaryDisplaySurfaceWindow. That is code that contains the main display window provided through Allegro in which changes to window size, mouse position, and other changes to the window are made available to the software application.
Next, we have the application-level solution. Others would call some of this the business logic. The C++ header followed by the C++ code. Whatever you call it, it is the merger of the graphics system (mouse clicks, window resizing, drag and drop) with the application (data in/data out, high-level rules, interaction response). This is the part most relevant to the end-user.
Finally, the main method from which the process starts when the application is launched.
This is the final example in this series and would be a great starting point for a GUI in C++ using Allegro and the geometry functions in the dlib library. I plan on moving forward with this code in rebuilding the 2015 version of the Gautier RSS Reader. Now, we have a GUI defined using video game technology in which there is great granular control over most if not all visual aspects of the interaction and rendering.
Dec. 14, 2016 Updates
See the next article.