Chapter 1: A Brief History of GUI Toolkits and Cross-Platform Development
This book is aimed at exploring how to easily build robust and beautiful graphical applications that will work well across all operating systems and devices. Before we start looking at the details of how this is accomplished, it is important to consider the history of these devices and the landscape of graphical toolkits throughout the last 50 years. We start with a reminder of where GUI-based applications started and how far they have come.
In this chapter, you will be reintroduced to the Graphical User Interface (GUI), along with learning about toolkits that support app development and how they offer different approaches to cross-platform development. We will explore the benefits of coding a native GUI for responsive user experience and platform integration. Upon completion of this chapter, you should be familiar with the origins and challenges of graphical toolkits and the different approaches that have been taken during this journey.
In this chapter, we'll cover the following topics to provide a short history of GUI toolkits and cross-platform development:
- Where GUI toolkits came from
- How they have adapted (or stayed the same) over time
- Historical approaches to cross-platform development
Understanding the history of the graphical user interface
In 1973, the Palo Alto Research Center (Xerox PARC) completed the Alto computer, the first commercial example of a graphical desktop computer. In most contemporary histories, this was the first example of what we understand as the GUI. While the screen orientation and lack of colors make it a little peculiar to the modern eye, it's clearly recognizable and includes many key components as well as a mouse and keyboard for interaction. While it took another 7 years to become generally available to the public in 1981 as the Xerox Star, it was clear that this was a dramatic step forward:
This development was a huge leap forward for the usability of computers. Up to this time, all interaction was through text-mode computer screens and keyboard or other text input devices. The graphical interface is much easier to learn for a novice looking to get started, and allows the quicker discovery of advanced features. While the command-line interface remains popular with programmers and other pro users, the GUI is the largest contributing factor to the rise of the desktop computer.
Popularity of the desktop computer
The introduction of a user-friendly graphical environment brought about a significant growth in the use of desktop computers. Around the time of the Alto computer, there were an estimated 48,000 desktop computers around the world. By 2001, this number had increased dramatically to over 125,000,000 personal computers shipped (https://en.wikipedia.org/wiki/History_of_personal_computers#Market_size). In 2002, the industry celebrated a billion computers shipped (http://news.bbc.co.uk/1/hi/sci/tech/2077986.stm), though numbers have declined more recently (see the Smartphones and mobile apps section later in this chapter) and fewer than 300 million were reported shipped in 2018 (https://venturebeat.com/2019/01/10/gartner-and-idc-hp-and-lenovo-shipped-the-most-pcs-in-2018-but-total-numbers-fell/).
As these devices reached the hands of consumers, and hardware became more capable, we started to see a focus on creating attractive user interfaces as well as a trend to establish or match fashion trends. The following are some important versions of Microsoft's Windows operating system:
As you can see in the previous screenshots, each major revision of the desktop environment brought new styles for the buttons, fonts, and other user interface elements. This is all controlled by the toolkit and represents an evolution in usability and style choices that we'll explore later in this chapter.
Whilst Microsoft were progressing with their GUIs, there were also many competitors, some of which may appear familiar and others with their own distinct styles; for example, the following popular systems:
As you can see from the preceding desktop screenshots for various operating systems from 1985 to 2015, there have been dramatic shifts in the look and feel, whilst maintaining a certain familiarity. These desktop systems are all designed for running multiple application windows typically centered around document editing, file management, and utility apps. Additional software, such as games, photo management, and music players, appeared over the years, but the most ubiquitous, the web browser, was not commonplace until the late 1990s. The addition of internet access started a shift to a new era of computing.
Moving to the web
With the increasing availability of reliable internet connections, we started to see an increase in the amount of information being accessed from servers on the World Wide Web (WWW). Providing a good user web browser experience became of paramount importance and the fierce competition saw operating system manufacturers up against independent software developers (search
browser wars in your favorite search engine to know more).
The WWW was first proposed by Sir Tim Berners Lee in 1981 and development began within the CERN (https://home.cern) project (codenamed ENQUIRE). The early web was made available to the public in 1993. As a distributed system that anyone is able to add to, innovation in design was even more rapid than in the desktop operating systems we saw earlier. Trends in design and usability quickly caught up with, overtook, and started to lead traditional software development:
The web started as a project for providing access to data, born out of a frustration of how difficult it could be to access information on a different computer. What started as simple information retrieval quickly became a polished presentation of more complex information, and then began to become a place to submit or manipulate information as well.
A simple data access platform quickly grew into much more and before long, this emerged as a full application platform. In fact, due to the standards-based approach (overseen by the World Wide Web Consortium (W3C)), this was one of the first truly cross-platform development opportunities. A web-based application would be developed and made available to all computers in one go—a big advancement over previous attempts to develop for multiple platforms.
An additional benefit of delivering applications through a web-based solution was that you could support multiple types of application accessing the underlying data or functionality. A web-based API (Application Programming Interface) that had historically powered the user-visible website could be used by other devices as well. This design allowed traditional software to access the same data as the web-based delivery systems and contributed to the development of common-place architectures that support a multitude of different types of software – including the more recent mobile-based applications.
Smartphones and mobile apps
In 2007, Apple's Steve Jobs introduced the iPhone, a fresh new design for the concept of mobile computing. Although portable smartphone devices existed for many years before this event, the introduction of a slick new user interface, touchscreen input, and large screen for displaying video and web content had a significant impact on the market. Competitors (existing and newly created) were now racing to create the best user experience that could fit in the consumers' pockets. Although early devices touted that you could browse any website with ease, developers quickly adapted the content to be better presented on these smaller screens—often focusing on information that mattered on the move.
To satisfy the user demand for a more sophisticated and faster experience on these more limited (by hardware or internet connectivity) devices, the concept of a mobile app was born. These small pieces of software were designed specifically for a certain type of mobile phone (Android, iPhone, and others) and were made available by the platform's store or marketplace. Such software had large benefits over the web-based solutions that came earlier as they could be installed on the device, so they ran faster and were developed specifically for the given hardware, creating a better user experience and allowing access to the more advanced capabilities of each device (such as location detection, thumbprint sensors, and Bluetooth).
These native apps provided the ultimate user experience. The applications could be very fast (as they were installed on the device), adapt to the user (through access to local settings and data), and also interact with the operating system features (such as calendars, voice controls, and cutting-edge hardware sensors), all of which are not really possible when delivered through a web app. However, they came with a disadvantage for developers—not only did each platform look different, meaning that designs may need to be adapted, but they were also distributed separately and typically required different programming languages to develop. Now instead of reaching the whole world with a single application, a software company would need at least three different apps to reach their customers through their favorite devices:
We will come back to the challenges of developing for multiple different hardware platforms later, but first, we will explore the graphical toolkits that underpinned the various technologies we have seen in this section.
Exploring the evolution of GUI toolkits
GUIs must be programmed like any other computer program, and just like libraries are created to provide standard components, a GUI toolkit exists to support building the graphical elements of an application. Many toolkits exist for different reasons—Wikipedia maintains a list of nearly 50 different projects, and growing, at https://en.wikipedia.org/wiki/List_of_widget_toolkits and https://en.wikipedia.org/wiki/List_of_platform-independent_GUI_libraries. To make sense of the huge number of options, we split them into categories, looking first at those built for specific operating systems.
Each graphical operating system or desktop environment has a distinct look and programming style, therefore a graphical toolkit was traditionally created for each platform. Windows had the WinAPI (as well as WinForms and foundation classes), Atari was programmed using GEM, and BeOS used the Be API. Applications developed for Apple products have used various toolkits, but since macOS X it's called Cocoa (with desktops using AppKit and mobile devices using UIKit). Android devices are programmed with their own toolkit and other mobile platforms have explored other options.
The Unix and Linux operating systems have a more complicated story. Although the Motif toolkit was one of the first, the fact that its design offers multiple choices has meant there is no one true look or library. In the 1980s, before Motif was created, there was the OpenLook project that aimed to provide a standard interface look and feel for Unix systems. Although there were many different designs and toolkits to choose from, the main contributors to Unix decided that unification would help it compete with Windows and other desktop platforms. And so, in 1993, they chose Motif for future development.
One of the common features of desktop environment design is that it is frequently updated, as you could see in the screenshots of Microsoft Windows earlier in this chapter. Whether due to changes in fashion or advances in usability, these changes are expected and the Motif system did not adapt to them, and so new projects were created as alternatives. Later in the 90s, the GTK+ and Qt projects were started and provided a more modern, polished-looking user interface. Also, the Java platform launched with AWT (Abstract Widget Toolkit) in 1995, all of which were not platform-specific, opening a new world of cross-platform GUI libraries.
The toolkits mentioned in the earlier subsection were all developed for a specific platform. They evolved along with the operating system design and are often developed using the manufacturer's preferred programming language. These challenges make it difficult (if not impossible) to create a single app that will work on all platforms. For that reason, the move to create a cross-platform toolkit requires taking a different approach and so developers started to design a library that could be written independently of platform specifics in a language that could be compiled for any of the operating systems it supports.
When GTK+ and Qt were created in the mid-1990s, they chose C and C++ (an object-based language derived from C), respectively. Both languages had wide adoption across most operating systems and were in use with some other toolkits already, keeping the barrier of learning low. The Java approach, however, was broader—to create a whole new language that would work across all of these platforms and deliver a graphical toolkit built on top.
Operating system and computer manufacturers have market power to sway technologies, and as new languages became available, they are able to force developers in the same new directions (such as Apple moving to Swift, Microsoft to C#, and Google moving their apps to Dart). However, the large open source communities built around cross-platform technologies are generally loyal to the language it is built with and so don't typically embrace such large changes. Therefore, these projects can be left behind in some senses and can encourage developers to look in new directions, such as web technologies.
As discussed earlier in the chapter, the WWW offered an attractive platform for delivering applications to users on multiple operating systems as well as providing a way to build apps once and run them on any computer. A web browser offers a highly customizable canvas so that, using Cascading Style Sheets (CSS), any HyperText Markup Language (HTML)-based application can be made to look like any design. This benefit brought a lot of popularity to web application development and even influenced some native toolkits to expand their theme capabilities to mimic this.
Despite the benefits of distributing over the web, many companies still want to provide a traditional application that is downloaded and installed (the reasons are explored in the next section). To balance the speed of development and other benefits of web applications with the standard application package that developers are familiar with, a new breed of applications was created, nicknamed hybrid apps. These new apps are loaded in a standard container that loads the custom web application in a regular window like any other application on the system. Electron, Ionic, and React Native are all projects working in this space, offering a web-based app framework with varying levels of access to the system they are running on.
Alongside the evolution of graphical toolkits for cross-platform development, we cannot ignore the ubiquity of the web browser. Although it has the benefit of being present on most modern computers, it may not be the right solution for building your product – let's take a look at how these approaches differ.
Comparing native graphical apps to web UIs
Despite the benefits that web-based applications can deliver, every technology choice means making a trade-off in some area, so let's look at a few of the common issues that might influence your decision of whether to build a native app or web-based hybrid.
Development speed versus delivery
One of the main reasons to pick a web technology to build your application is the speed of development. The very nature of developing in this way means that you can live-preview your work in a web browser. The availability of browser-based editors also means that a design team can tweak the user interface without much code experience. Large portions of your web app could also be used in your hybrid application (or the other way round) to provide a high level of reuse and minimal additional work to support desktop and mobile delivery.
The actual speed of execution may also be noticeably slower for applications built on web technologies. Due to the layers of abstraction, a web-based app will typically take more time and CPU cycles to perform the same operations than a compiled native application (though technologies such as WebGL and WASM (short for Web Assembly) are attempting to improve this). Therefore, if your application is likely to be CPU-intensive, or have lots of animated graphics, you may wish to benchmark different approaches to determine which platforms are capable of meeting your requirements for app responsiveness.
Another consideration may be automatic updates—would you like your apps to always be running the latest version? Some web-based toolkits offer the functionality to download application updates and dynamically load the new version without the user having to worry. This can be a large benefit, but could also be frustrating if your customers expect the software to work exactly the same every day until they opt to update it. Some people are also concerned about how applications of this nature may appear to phone home—that is, reporting back to a central server about how the apps are being used, and where, as part of the update process.
Another main decision point for choosing web technology-based app development may be the power of the presentation layer (CSS). It is possible, using a combination of image assets and style-sheet code, to create almost any visual style desired. For developers (or indeed, the designers on their team) that desire a completely bespoke look to their application, this could be a great fit. It is worth considering how your users will use the application, however, and whether a completely custom look will inhibit the usability in any way.
This benefit of complete customization can become challenging if the application is intended to match the user interface style of the current system. As the rendering is infinitely flexible, developers can of course add tweaks to a style that make it look subtly (or substantially) different when running on certain systems. This type of adjustment can end up taking a surprising amount of additional effort—as each platform can have a different style over time. A GUI that attempts to match the system style but does not quite manage this is far more off-putting than the one that is clearly following its own style guide.
Therefore, it is probably best to avoid hybrid apps if you desire to blend in with the other apps on a system. Web technologies do offer a fast-to-develop, adaptable platform for cross-platform applications, but there are constraints to this approach that should be considered as well.
In comparison to this, other approaches to cross-platform development typically provide an abstraction over all supported operating systems so that an app can be built just once, but where support is missing, they provide a way to get direct access to the underlying features. This is often in the form of a language bridge or a way to load system libraries from the higher-level language. This can involve needing to build with different programming languages, as discussed earlier, but with cross-platform technologies outside of the web sandbox, it does not normally increase the distribution complexity as much. Additionally, it is far rarer to find devices not supported by a native cross-platform toolkit in comparison to one that is based in an embedded web browser.
Options for cross-platform native toolkits
As indicated earlier in this chapter, the idea of a cross-platform toolkit is not new—in fact, they date back to the mid-1990s, barely 10 years into the history of GUI development. It is important to understand that even among native cross-platform toolkits there are distinct approaches with different benefits and drawbacks.
Cross-platform toolkits can be divided into two distinct visual approaches—the desire to match the look and feel of the system at runtime versus the delivery of a distinct look that will be consistent across all the environments. The Qt and GTK+ toolkits began with their own visual style that added the ability to be controlled by visual themes. Over time, they developed operating system-specific themes that allowed them to match the design of other applications on a system. In contrast, the Java AWT library was created as a code-level abstraction, meaning that programs would use operating system widgets to render despite the application being written for no specific platform. Interestingly, in 1998, Sun (the creators of Java) introduced the Swing toolkit, which offered a whole new look and feel that would be consistent across all platforms. This replacement user interface library slowly gained popularity as AWT was phased out. In an interesting twist, Sun introduced operating system lookalike themes that a developer could choose to enable in their app (unlike in GTK+ and Qt, this was not a user preference).
Compiled versus interpreted
The other factor that commonly defines a toolkit is the choice of programming language—these can be split between those that are compiled into an application binary versus those distributed as source code that is interpreted at runtime. This distinction commonly correlates with statically typed languages compared to dynamically typed languages, which impacts programming styles and the design of associated APIs. In a compiled language that uses static typing, all variables are defined and checked at compile time. Their types (that is, the sort of content they contain or refer to) are set in the definition and never change. This approach typically catches programming errors early and can lead to robust code, but can be criticized as leading to slower application development. A compiled application will usually work directly on the computer it is built for, meaning that it does not require the installation of supporting technologies alongside the application. To achieve this, however, the apps will typically need to be compiled for each supported platform—again leading to longer development times required, this time for distribution tasks.
In comparison, an interpreted language is typically held up as supporting faster development and quicker delivery. By allowing variables to be used for different types of content, the source code of an application can be shorter with fewer complications during the programming phase. Conversely, the reduced rigor means that it is usually required to have a more solid test infrastructure (through unit tests or automated user interface testing) that can ensure the correctness of the software. Distributing apps in this way will require a runtime environment to be installed so that the code can be executed (like the embedded web browser we saw when discussing hybrid apps). For some operating systems, there may be interpreters installed already, and for others, they may have to be installed by the user. Interestingly, there are some compilers available for interpreted languages that allow them to be distributed like any other binary application, though with the preceding trade-off that applications must be built for each target platform individually.
Due to the popularity of building GUIs, each of the main interpreted languages has its preferred toolkit. The Java runtime includes its own graphics routines, which means it can include a bespoke user interface, namely Swing. More recently, the JavaFX library has been built on top of the same graphical code. As mentioned earlier, Java also includes the AWT library, which delegates to system components.
Other popular programming language runtimes do not ship with the same graphical components and so they typically rely on an underlying library. TkInter is the standard GUI library for Python, based on the Tk library, while the Ruby language does not recommend a standard library. Language bindings, which allow programmers to create applications using an interpreted language while using an underlying existing widget toolkit, such as Tk, GTK+, or Qt, are very popular. Using this method, there are too many options available to list, but they usually have the same drawback of not being designed for the language and so can be less intuitive to program than options built specifically for the language being used.
Compiled options (C-based)
As shown earlier in this chapter, GUI development has a long history and the most popular toolkits were initially designed many years ago. GTK+ and Qt are both exceptionally popular, but they were designed for the C and C++ languages, respectively. While this does not stop them from being effective choices, it can make them seem a little outdated to the modern programmer. Possibly due in part to this reason, these GUI frameworks have seen language bindings made available for almost every programming language that exists. However, this does mean that you need to know a little about how the underlying system works to be most effective in development. For example, memory management in a C library is a complex and manual task, and not one that most developers want to worry about. Additionally, a C++-based library will have a different threading model beneath the surface that may not interact well with the higher-level language methodologies.
An additional consideration for toolkits that were designed decades ago is that they may not be best suited for the modern landscape of graphical computing devices. Screens, whether on a desktop, laptop, or mobile device, now come in a wide variety of sizes and pixel densities. This can bring challenges if code was built assuming that a pixel is a certain size, as used to be true. 96 dots per inch (DPI) was a common assumption, meaning that something 96 pixels high would measure about an inch when displayed. However, based on current devices, it could be anywhere from an inch down to 3/16ths of an inch (only 20% of the intended size), so it is important to understand how this will impact your application design. Older toolkits are typically based on pixel measurements, where the display is simplified to be a certain number of pixels (1, 2, 3, or 4) to represent each source pixel's size (which would be 1, 4, 9, or 16 pixels to display a square). Using a toolkit that scales in this manner can result in pixelated output if the application does not adapt carefully to the device.
Compiled options (other languages)
In part due to the legacy of the older C/C++-based GUI toolkits, but also in part because new programming languages bring benefits over the old ones, most of the newer compiled languages also have a toolkit built alongside. These more modern toolkits typically handle today's variety of devices much more effectively due to their more recent design. The challenges with pixel- or bitmap-based design is being overcome by moving to a vector image-based design. Vector images can be displayed at a higher quality output on screens of any pixel density. They do this by defining image elements such as lines, rectangles, and curves independently of output pixels; they are only drawn after the number of pixels available has been determined, resulting in higher quality on most output devices.
As we saw earlier, an operating system manufacturer often dictates the programming language used, and can push for new versions or programming languages when required. We can see this with Apple's recent move to Swift for the latest version of their UIKit and AppKit frameworks (this language is intended primarily for Apple devices but the trend is still noteworthy). Microsoft's development platform is currently geared toward C#, having previously used C, C++, and Visual Basic. Google is putting its energy into the Dart programming language and the Flutter toolkit built on top.
Other languages, such as Go, do not have an official GUI toolkit or widget library. In these situations, we can see various projects emerge with different ways of addressing the gap. In Go, the most active projects are andlabs UI, Fyne, and Gio. The andlabs project aims to use the current system style—in fact, it wraps the code to display them with a simple Go API—much like Java AWT discussed earlier. Gio is an immediate-mode GUI toolkit that aims to provide as much control as possible to the application developer, requiring the app to manage the rendering and event processing. The Fyne project aims for an API that is simple to learn and extend, without needing to worry about the rendering process—this is commonly known as retained mode as the widget state is managed by the library.
In this chapter, we explored the history of graphical applications and the toolkits that power them. We saw that over the last half-century, many things have changed, and yet a lot of aspects have stayed the same. Through an illustration of trends in graphical design and technical capability, it became clear that these technologies, while adapting and improving in the eyes of the end user, can be slow to embrace the speed of improvement that developers expect. We saw that there are many different approaches to supporting the creation of GUIs that work across multiple platforms, but also that they can have drawbacks as well.
In the next chapter, we will learn more about the Fyne toolkit's vision and design, and why the project team believes this provides the easiest way to build robust and performant graphical apps for any platform.