This tip provides instructions for successfully installing Code::Blocks, Simple and Fast Multimedia Library (SFML) and Texus' Graphical User Interface (TGUI) on Linux to create and run a first little TGUI sample program in C++. The sample program shows some things that are not currently covered by example in the TGUI tutorial: [a] Creating a ribbon-like UI. [b] Working with the internal file dialog. [c] Setting the widget font.
This is how the result should look (after loading a small PNG file):
Table of Contents
Introduction
The Texus' Graphical User Interface (TGUI) characterizes itself as - a cross-platform modern C++ GUI library. TGUI focuses on the core tasks of a GUI library and relies on either Simple and Fast Multimedia Library (SFML) - based on OpenGL / GLES, or Simple DirectMedia Layer (SDL) - based on OpenGL / DirectX or Graphics Library Framework (GLFW) - based on OpenGL / GLES / Vulcan to render the widgets and to interact with the platform UI (window, pointer, keyboard, controllers, ...).
TGUI could therefore (because it's rendering via OpenGL / GLES) be classified as immediate mode GUI (IMGUI). So, if you are going for TGUI, you should be aware of the advantages and disadvantages of an IMGUI.
The next section is a little bit about my personal background - you can just skip this. I can't explain why, but GUIs have just always been of particular interest to me. Maybe it's because I learned what object-oriented programming really means from my first own extensions to the Athena Widget Set (Xaw) written in K&R C (yes, object-orientation in C without ++) and later how much manual work C++ takes away from the developer compared to object orientation in C. Or because I learned with Motif what a well structured API is and that the better is the enemy of the good. Or because I learned with MS Windows UI that it doesn't have to cost thousands of dollars to be allowed to use a UI library (which Motif later learned with OpenMotif by the pressure of LessTif as well). Or about the fact that I then walked the rocky road with Microsoft from the Win32 API through Microsoft Foundation Classes (MFC) and Windows Template Library (WTL) to Windows Forms, which finally provided a full-featured, well-structured and easy-to-learn API for the Microsoft Windows UI. Completely indisputably, Windows Presentation Foundation (WPF) was a cool approach - but was then cannibalized by WinUI and more recently MAUI (by cannibalized, I mean Microsoft released new technologies before WPF fully caught on).
Now I just read (August 2022) that Microsoft has completely reworked the Windows Forms library and (after its supposed death) now makes it available again for .NET 5.0 and .NET 6.0. This was the trigger for me to look for GUIs that can be used cross-platform with Windows and Linux after a longer break. During one of my recent researches to stay updated regarding modern GUIs, I stumbled across Texus' Graphical User Interface (TGUI). Since I still have an old love for C++ in addition to my current love for C#, I wasn't afraid that TGUI is written in C++ C14 and was pleased to see that there is a C# binding for it as well.
Background
I chose SFML as the backend for my first TGUI programming experiments. I am aware that SFML is currently struggling to support successor technologies of OpenGL (e.g., Vulkan). This topic is - driven by the decreasing quality of OpenGL support on Windows (keyword DirectX) and OSX (keyword Metal) - indeed discussed in the SFML community (here and here and here and here ...).
The next section tells the story of why this article is about TGUI and SFML - you can also just skip this. Since my professional interest is Microsoft Windows and my private interest is Linux, these two platforms are important to me if I think of cross-platform GUI toolkits. Unfortunately, MAUI falls out of this category. Also, almost all other known GUI toolkits for Microsoft Windows are not available cross-platform for Linux. Therefore, I would like to approach this topic from the Linux side.
I already have some experience with classic Linux GUIs (classic in this case means rendering in the CPU) for X11 (like Athena Widget Set, Motif, GTK2) and also wrote one myself (Roma Widget Set). With the general availability of OpenGL and DirectX, the demands on the appearance of GUIs have increased significantly. I too find modern GUIs (modern in this case means rendering in the GPU) more exciting at the moment. I also made first steps with GPU rendering - namely OpenGL (since DirectX will never be fully supported on Linux, the relevant cross-platform graphics/multimedia libraries are limited to OpenGL, Vulcan, Cairo, SDL2 and Skia).
I think Vulkan is the most interesting option - but since I regularly deal with older hardware, I don't really dare to use Vulkan yet.
Cairo and Pango I know well and appreciate their reliability very much - so Cairo does not awake any particular curiosity in me right now.
SDL2 is a very feature-rich library and a real heavyweight. Since I don't plan to get into photorealistic rendering or ray tracing, it's a bit too heavy for me.
On the Internet, the opinion seems to be gaining ground that Skia is more modern than Cairo. I once tried the SkiaSharp based AvaloniaUI, but the complexity of a Skia installation on Linux and the list of supported IDEs made me lose interest.
Finally I decided for the Methuselah: OpenGL. But as much as I am fascinated by the possibilities of OpenGL (see e.g. article Getting started with OpenGL/OpenTK in MONO/.NET for serious applications) - rendering text with OpenGL is a nightmare. Even GLUT, GLFW and OpenTK (see e.g. article Abstract of the text rendering with OpenGL/OpenTK in MONO/.NET) do not change much.
TGUI can use Simple and Fast Multimedia Library (SFML), Simple DirectMedia Layer (SDL) and Graphics Library Framework (GLFW) as it's rendering backend. And since I don't know it until now, I am also very curious about SFML, which is written in C++ and for which there is also a C# binding. SFML looks like very clean C++ and seems worth to be tested. Consequently, I found the question of whether there are better cross-platform graphics/multimedia libraries like Cairo, SDL2 or Skia only secondary.
Create the Prerequisites
At the end of this tutorial, I want to have a simple TGUI sample program compiled and running on Linux.
One of the IDEs directly supported by TGUI is Code::Blocks. In addition, Code::Blocks is available on MS Windows and Linux, is very feature-rich and is regularly among the top places in the "best of C/C++ IDEs" lists. Even if the UI looks a bit antiquated, the interface concept and functionality is very similar to an older version of Visual Studio and you will quickly find your way around.
That's why I decided to use Code::Blocks.
My Favorites - openSUSE and Debian
I like the open source software approach, like to promote it by my actions and work professionally with Microsoft Windows all day - what could be more natural than relax with Linux in my spare time?
The next section tells the story of why I'm focusing on openSUSE and Debian here - you can also just skip this. Once upon a time, Debian 8 (the leading Linux distribution at the time, along with RedHad) introduced and pushed a GTK 3 desktop that made me feel like I was sitting in front of an oversized smartphone. Only, instead of a fat finger, I had a high-precision mouse pointer available as a typing device - whose precision was not used. That was not for me.
And so I switched to SUSE and later openSUSE. I learned to appreciate the intuitive handling of the much-maligned configuration tool YaST and worked with MonoDevelop on XFCE for a long time.
But in the meantime, a lot has changed in Debian: XFCE is very easy to set up as primary desktop, Code::Blocks and MonoDevelop are easy to install. Consequently, Debian has moved into my focus again.
Providing the Minimum Environment on openSUSE
I start with a fresh default installation of openSUSE Leap 15.4 "Desktop with XFCE".
Mesa 21.2.4 (system for rendering 3-D graphics, that utilizes the OpenGL command syntax and state machine) is already installed.
GNU C++ 11 (or higher)
Code::Blocks requires at least GNU C++ 7, but there is nothing against a GNU C++ 11 (or higher) installation. I have installed the following packages:
- cpp11 (The GCC Preprocessor)
- gcc11-c++ (The GNU C++ Compiler)
- libX11-devel (Development files for the Core X11 protocol library)
- libXt-devel (Development files for the X Toolkit Intrinsics library)
I installed the libX11-devel and libXt-devel to write a small X11 test program with Code::Blocks and because they will be needed later for compiling anyway.
Code::Blocks
Via the "Welcome" dialog (and its button "Get Software"), the openSUSE Software Archive can be invoked. A search for "codeblocks" reveals the community packages for openSUSE Leap 15.4.
Unfortunately, none of the 1 Click install offers work (probably because some of the required packages have to be loaded from unsupported repositories[#]), so I opted for home:regataos Expert Download.
Even if this installation will succeed at the end, the exchange of some standard packages (in my case: 10+ packages) must be accepted.
But luckily all required packages can be installed. After Installation, the unsupported repositories[#] can be removed again ...
- https://download.opensuse.org/repositories/SUSE:/SLE-15-SP1:/GA/pool/
- https://download.opensuse.org/repositories/SUSE:/SLE-15-SP2:/GA/pool/
- https://download.opensuse.org/repositories/SUSE:/SLE-15-SP3:/GA/pool/
- https://download.opensuse.org/repositories/SUSE:/SLE-15-SP4:/GA/pool/
In the meantime, my system has installed updates and GNU C++ 12 became available. So I quickly install C++ 12 before I set up Code::Blocks.
It is also important that the gcc-c++ package is installed (it updates the references to gcc12-c++).
Now Code::Blocks 20.03 can finally be started - and it automatically configures C++ correctly.
SFML
YaST offers SFML 2.5.1 - this is the latest stable version now. I install the packages:
libsfml2-2_5
libsfml2-devel
make
The openSUS default installation already comes with make version 4.2.1-7.3.2.
Cmake
YaST offers Cmake 3.20.4. I install the packages:
cmake 3.20.4
cmake-full 3.20.4
cmake-gui 3.20.4
cmale-man 3.20.4
TGUI
I've downloaded the source code of current development branch 0.10 and extracted it to ~/Projects/CodeBlocks/TGUI-0.10-Oct-2022. I found it stable enough to base my tests on this version. There is a good tutorial how to compile TGUI using cmake.
The tutorial is based on Visual Studio 2019 on Windows and also describes Code::Blocks on Windows. Here are the details for Code::Blocks on openSUSE...
- The environment should be configured like this:
- After the first [Configure] run (note: Red rows in the Cmake table area are new rows, not error rows. Red lines in Cmake log output area are not necessarily errors, often a new [Configure] run can solve the problem.), I changed the
CMAKE_BUILD_TYPE
to "Debug
" - but this is my personal preference. The TGUI_BACKEND
default value SFML_GRAPHICS
is a really suitable choice, that involves the following qualities:
- Pro: It uses OpenGL as rendering backend (just like
SFML_OPENGL3
). - Pro: It requires just OpenGL version 1.1, not OpenGL version 3.3 (like
SFML_OPENGL3
). - Pro: It provides more (convenience) functionality (compared to
SFML_OPENGL3
). - Pro: For heavy OpenGL usage, a higher OpenGL version can be requested as well.
- Con: Multi window / multi thread applications are slightly more complex to implement (compared to
SFML_OPENGL3
).
- After the second [Configure] run, I fixed these errors:
X11_xcb_icccm_INCLUDE_PATH-NOTFOUND
can be fixed with: /usr/include/xcb -
X11_xcb_icccm_LIB-NOTFOUND
can be fixed with: /usr/lib64
-
X11_xcb_util_INCLUDE_PATH-NOTFOUND
can be fixed with: /usr/include/xcb
-
X11_xcb_util_LIB-NOTFOUND
can be fixed with: /usr/lib64
Why (Update 1): Depending on the configured rendering backend (for SDL or SFML < 2.6 but not for GLFW or SFML >= 2.6) TGUI requires some X11 calls to get proper mouse cursors when resizing a child window, and X11 is identified by the find_package(X11)
call. However, it is difficult to impossible to suppress this call if it is not needed for the current rendering backend. So these errors could also be ignored.
After the Code::Blocks project file and the make file generation, the sub-folder /build contains all intermediate results as well as the Code::Blocks project file TGUI.cbp and the make file. The creation of the library works flawlessly in both ways (via Code::Blocks GUI and via make command line call).
After the library generation, the cmake-gui
can be started again, configuration can be changed (e.g. "Release
" instead of "Debug
") and [Configure], [Generate] and compilation can be repeated. The result can look like this:
The final preparation step is to copy the libraries to /usr/lib and the include folder TGUI to /usr/include.
Providing the Minimum Environment on Debian
I wanted to start with a fresh installation of Debian 11, but ...
First Pitfall
Coming from openSUSE, I didn't realize that on Debian, I would end up in a system without WLAN drivers with the default network installation CD. It took some internet research before I realized that I needed an unofficial non-free installation CD for the network installation to fully use all my hardware (an ordinary Intel N1000 WLAN adapter).
After solving this, installation recognizes my WLAN adapter and establishes connection to my WLAN.
Second Pitfall
Coming from openSUSE, I assumed that I can set the system language (I'd like to use English) and the keyboard layout (I need to use German) separately during installation - but that doesn't work with Debian. Adding a second language afterwards is a nightmare of command line calls (as far as I have researched so far) - at least I couldn't manage it so far.
So I apologize if German texts or screenshots are included in this description. Maybe I'll fight my way through this nightmare later and replace them.
libglapi-mesa 20.3.5 (Free implementation of the GL-API - runtime library) is already installed.
GNU C++ 10 (or higher)
Code::Blocks requires at least GNU C++ 7. There is already a GNU C 10 installed. I leave it unchanged and install:
- g++-10 (The GNU C++ compiler)
in addition to that.
Code::Blocks
We reach the first advantage of Debian over openSUSE: Code::Blocks 20.03 can be installed from the Synaptic package manager. I install the package:
- codeblocks (Integrated Development Environment (IDE) Code::Blocks)
and all its auto-dependencies.
Code::Blocks 20.03 can be started now - and it automatically configures C++ correctly.
SFML
Synaptic offers SFML 2.5.1 - this is the latest stable version now. I install the package:
libsfml-dev 2.5.1
(Simple and Fast Multimedia Library - Development Files)
and all it's auto-dependencies.
make
Debian default installation already comes with make version 4.3-4.1.
Cmake
Synaptic offers Cmake 3.20.4. I install the packages
cmake 3.20.4
(Cross-platform, open source make system) cmake-qt-gui 3.20.4
(Qt based user interface for CMake (cmake-gui))
and all their auto-dependencies.
TGUI
I've downloaded the source code of current development branch 0.10 and extracted it to ~/Projects/CodeBlocks/TGUI-0.10-Oct-2022. I found it stable enough to base my tests on this version. There is a good tutorial how to compile TGUI using cmake.
The tutorial is based on Visual Studio 2019 on Windows and also describes Code::Blocks on Windows. Here are the details for Code::Blocks on Debian...
- The environment should be configured like this:
- After the first [Configure] run (note: Red rows in the Cmake table area are new rows, not error rows. Red lines in Cmake log output area are not necessarily errors, often a new [Configure] run can solve the problem.), I changed the
CMAKE_BUILD_TYPE
to "Debug
" - but this is my personal preference. The TGUI_CXX_STANDARD
is already 14. The TGUI_BACKEND
default value SFML_GRAPHICS
is a really suitable choice, that involves the following qualities:
- Pro: It uses OpenGL as rendering backend (just like
SFML_OPENGL3
). - Pro: It requires just OpenGL version 1.1, not OpenGL version 3.3 (like
SFML_OPENGL3
). - Pro: It provides more (convenience) functionality (compared to
SFML_OPENGL3
). - Pro: For heavy OpenGL usage, a higher OpenGL version can be requested as well.
- Con: Multi window / multi thread applications are slightly more complex to implement (compared to
SFML_OPENGL3
).
- The second [Configure] run ends without any error and [Generate] can be started.
After the Code::Blocks project file and the make file generation, the sub-folder /build contains all intermediate results as well as the Code::Blocks project file TGUI.cbp and the make file. The creation of the library works flawlessly in both ways (via Code::Blocks GUI and via make command line call).
After the library generation, the cmake-gui can be started again, configuration can be changed (e.g. "Release
" instead of "Debug
") and [Configure], [Generate] and compilation can be repeated. The result can look like this:
The final preparation step is to copy the libraries to /usr/lib and the include folder TGUI to /usr/include.
Providing the Minimum Environment on Manjaro (Update 1)
I got this tip from texus - the developer of TGUI - with my preferences to take a look at Manjaro (and it will turn out Manjaro is a good choice). I start with a fresh default installation of Manjaro 21.3.7 "Desktop with XFCE".
Mesa 21.2.1 (An open source implementation of the OpenGL specification) is already installed.
GNU C++ 11 (or higher)
Code::Blocks requires at least GNU C++ 7, but there is nothing against a GNU C++ 11 (or higher) installation. Manjaro default installation comes already with GNU C++ version 12.
Code::Blocks
Manjaro default installation already comes with Code::Blocks version 20.3.
SFML
The Manjaro software installer offers SFML 2.5.1 - this is the latest stable version now. I install the package:
sfml 2.5.1-3
(A simple, fast, cross-platform, and object-oriented multimedia API)
and all it's auto-dependencies.
make
Make isn't part of the default installation, but can be installed with the Manjaro software installer. I install the package
make 4.3.3
(GNU make utility to maintain groups of programs)
and all it's auto-dependencies.
Cmake
The Manjaro software installer offers Cmake 3.24.2. I install the packages
cmake 3.24.2
(Cross-platform build system) cmake-qt-gui 3.18.4
(Qt based user interface for CMake (cmake-gui))
and all their auto-dependencies.
TGUI
I've downloaded the source code of current development branch 0.10 and extracted it to ~/Projects/CodeBlocks/TGUI-0.10-Oct-2022. I found it stable enough to base my tests on this version. There is a good tutorial how to compile TGUI using cmake.
The tutorial is based on Visual Studio 2019 on Windows and also describes Code::Blocks on Windows. Here are the details for Code::Blocks on Manjora...
- The environment should be configured like this:
- After the first [Configure] run (note: Red rows in the Cmake table area are new rows, not error rows. Red lines in Cmake log output area are not necessarily errors, often a new [Configure] run can solve the problem.), I changed the
CMAKE_BUILD_TYPE
to "Debug
" - but this is my personal preference. The TGUI_CXX_STANDARD
is already 17 - no need to change it. The TGUI_BACKEND
default value SFML_GRAPHICS
is a really suitable choice, that involves the following qualities:
- Pro: It uses OpenGL as rendering backend (just like
SFML_OPENGL3
). - Pro: It requires just OpenGL version 1.1, not OpenGL version 3.3 (like
SFML_OPENGL3
). - Pro: It provides more (convenience) functionality (compared to
SFML_OPENGL3
). - Pro: For heavy OpenGL usage, a higher OpenGL version can be requested as well.
- Con: Multi window / multi thread applications are slightly more complex to implement (compared to
SFML_OPENGL3
).
- The second [Configure] run ends without any error and [Generate] can be started.
After the Code::Blocks project file and the make file generation, the sub-folder /build contains all intermediate results as well as the Code::Blocks project file TGUI.cbp and the make file. The creation of the library works flawlessly in both ways (via Code::Blocks GUI and via make command line call).
After the library generation, the cmake-gui
can be started again, configuration can be changed (e.g. "Release
" instead of "Debug
") and [Configure], [Generate] and compilation can be repeated. The result can look like this:
The final preparation step is to copy the libraries to /usr/lib and the include folder TGUI to /usr/include.
The TGUI Sample Program
The provided source code comes with a project file, that is configured like this...
Project Setup
The next section is about the creation of the project setup from scratch - you can skip this, if you want to start with the sample program directly. I've already created a ~/Projects folder. Based on this, the Code::Blocks GUI can be started and a new SFML project can be created. First of all, the project type must be selected.
The wizard guides through the project creation process...
Even if we have SFML 2.5.1 installed - it is still a member of the SFML 2.x family and the right libraries are assigned to the project configuration.
After the project has been created, the build options can be optimized.
The wizard registers the required SFML libraries sfml-graphics, sfml-window
and sfml-system
to the "Debug
" and "Release
" Linker settings - but there is no difference between "Debug
" and "Release
" and the SFML libraries can be moved to the generic Linker settings.
In addition to the SFML libraries, the TGUI library must be registered to the Linker settings. As described above in the "Providing the minimum environment on openSUSE" TGUI and "Providing the minimum environment on Debian" TGUI chapters, I have created the debug libtgui-d
(~29MiB) and release libtgui
(~5MiB) versions of the TGUI library and now I can register the debug version to the "Debug
" Linker settings as well as the release version to the "Release
" Linker settings.
TGUI requires to be compiled with the C++ standard 14 at least. Since GCC >= 6 uses C++14 by default and GCC >= 11 uses C++17 by default, typically there is no need to specify the C++ version unless a specific C++ version is needed for the application. When errors like this arise ...
<font font-size:-2="">/usr/include/TGUI/String.hpp:87:18: error: ‘is_same_v’ is not a member of ‘std’; did you mean ‘is_same’?</font>
... then the selected C++ version does not match the C++ installation. Typically C++ version >= 11 is then installed, but the flag -std=c++14
is set. This can be fixed in the *.cbp file:
<Compiler>
<Add option="-Wall" />
<Add option="-std=c17" />
<Add option="-std=c++17" />
<Add directory="/usr/include" />
</Compiler>
I also add my preferred warning settings to the generic Compiler settings.
Finally, I check the build targets within the project properties.
The application Type should be Console application for "Debug" build. The background is, that
std::cerr << "...\n";
can write to the console window and that might help to debug errors.
The application Type should be GUI application for "Release" build.
Using the Code
Effects of the Decision for a Specific TGUI_BACKEND
As described above in the "Providing the minimum environment on openSUSE" TGUI and "Providing the minimum environment on Debian" TGUI chapters, I have kept the TGUI_BACKEND
default value SFML_GRAPHICS
.
This will set constants in the TGUI/Config.hpp header file according to this selection, which will ensure that the matching backend classes are enabled and the non-matching backend classes are disabled. You have to imagine it like this:
The constants in the TGUI/Config.hpp header file, that control this behavior, look like this:
#define TGUI_HAS_WINDOW_BACKEND_SFML 1
#define TGUI_HAS_WINDOW_BACKEND_SDL 0
#define TGUI_HAS_WINDOW_BACKEND_GLFW 0
#define TGUI_HAS_RENDERER_BACKEND_SFML_GRAPHICS 1
#define TGUI_HAS_RENDERER_BACKEND_SDL_RENDERER 0
#define TGUI_HAS_RENDERER_BACKEND_OPENGL3 0
#define TGUI_HAS_RENDERER_BACKEND_GLES2 0
#define TGUI_HAS_FONT_BACKEND_SFML_GRAPHICS 1
#define TGUI_HAS_FONT_BACKEND_SDL_TTF 0
#define TGUI_HAS_FONT_BACKEND_FREETYPE 0
#define TGUI_HAS_BACKEND_SFML_GRAPHICS 1
#define TGUI_HAS_BACKEND_SFML_OPENGL3 0
#define TGUI_HAS_BACKEND_SDL_RENDERER 0
#define TGUI_HAS_BACKEND_SDL_OPENGL3 0
#define TGUI_HAS_BACKEND_SDL_GLES2 0
#define TGUI_HAS_BACKEND_SDL_TTF_OPENGL3 0
#define TGUI_HAS_BACKEND_SDL_TTF_GLES2 0
#define TGUI_HAS_BACKEND_GLFW_OPENGL3 0
#define TGUI_HAS_BACKEND_GLFW_GLES2
Since I have kept the TGUI_BACKEND
default value SFML_GRAPHICS
, there are four constants set to 1 and all other set to 0.
TGUI_HAS_WINDOW_BACKEND_SFML
TGUI_HAS_RENDERER_BACKEND_SFML_GRAPHICS
TGUI_HAS_FONT_BACKEND_SFML_GRAPHICS
TGUI_HAS_BACKEND_SFML_GRAPHICS
Typically, it is not necessary to include TGUI/Config.hpp header file - but there are exceptions.
Extensions
Since this small TGUI sample program is intended to be a classic GUI application (and not a game), it is useful to prepare some application infrastructure that can be reused for similar cases (to be found in the folder TGUI-0.10-Extensions).
MainForm
The MainForm
class provides a minimal infrastructure for the main window of an application. It is not dependent on the selected backend (SFML, SDL, GLFW).
GuiEx
The GuiEx
class provides an overridden mainLoop()
method. It depends on the selected backend (SFML, SDL, GLFW) and is currently implemented for SFML_GRAPHICS
only. With this class, there is the possibility to intervene in the main loop, and e.g., integrate a "Do you really want to close the application?" dialog. However, this simple TGUI sample program does not make use of it.
ImageBytes
The ImageBytes
class provides the resource for images. It is not dependent on the selected backend (SFML, SDL, GLFW).
RibbonButton
The RibbonButton
class provides a convenient wrapper around the BitmapButton
class that significantly reduces the effort required to create ribbon buttons. It is not dependent on the selected backend (SFML, SDL, GLFW).
PortableFileDialogs
The PortableFileDialogs
header-only library provides a cross-platform way to call system-native dialogs. PortableFileDialogsWrapper
is available for convenient embedding of this functionality into a TGUI program. It is not dependent on the selected backend (SFML, SDL, GLFW). However, this simple TGUI sample program does not make use of it.
The TGUI Sample Program
Let's look at the main()
method first:
int main(int argc, char** argv)
{
#ifdef TGUI_HAS_BACKEND_SFML_GRAPHICS
sf::RenderWindow window(sf::VideoMode(980, 600), "TGUI window",
sf::Style::Default);
#else
sf::ContextSettings requestedettings;
requestedettings.attributeFlags = sf::ContextSettings::Attribute::Core;
requestedettings.majorVersion = 3;
requestedettings.minorVersion = 3;
sf::Window window(sf::VideoMode(980, 600), "TGUI window", sf::Style::Default,
requestedettings);
#endif
auto realizedSettings = window.getSettings();
std::cout << "SUCCESS creating main frame window with OpenGL "
<< realizedSettings.majorVersion << "."
<< realizedSettings.minorVersion << " context.\n";
tgui::Gui gui(window);
const char* themeFilePath = "./themes/BabyBlue.txt";
std::ifstream themeFile(themeFilePath);
if (!themeFile.is_open())
std::cerr << "ERROR: Unable to load theme.\n";
else
tgui::Theme::setDefault(themeFilePath);
auto container = gui.getContainer();
Bin2HeaderMainForm mainForm(window, gui);
if (!mainForm.createFrameContent())
{
std::cerr << "ERROR: Unable to create window content.\n";
return EXIT_FAILURE;
}
std::cout << "SUCCESS creating window content.\n";
gui.mainLoop(mainForm.getClearColor());
return EXIT_SUCCESS;
}
The only thing I want to go into here is that Section 1 is already prepared for the fact that TGUI_BACKEND
may have been compiled for SFML_GRAPHICS
as well as for SFML_OPENGL3
.
GUI Creation
Next, let's take a look at the creation of the GUI with createFrameContent()
:
bool createFrameContent()
{
const float groupWidth = 100.0f;
m_menuTabContainer = tgui::TabContainer::create();
m_menuTabContainer->setPosition(0.0f, 0.0f);
m_menuTabContainer->setSize("100%", "84");
m_menuTabContainer->setTabFixedSize(m_tabFixedSize);
m_menuTabContainer->setTabAlignment(tgui::TabContainer::TabAlign::Bottom);
auto tabPanel1 = m_menuTabContainer->addTab(U"Start", true);
auto tabPanel2 = m_menuTabContainer->addTab(U"Edit", false);
this->getGui().add(m_menuTabContainer, U"MenuTabContainer");
auto systemLogOutButton = tgui::RibbonButton::create(
tangoIconTheme::Icons28::Action_SystemLogOut.getBytes(),
tangoIconTheme::Icons28::Action_SystemLogOut.getByteCount());
systemLogOutButton->setPosition(m_ribbonWidgetSpacing.x,
m_ribbonWidgetSpacing.y);
systemLogOutButton->setSize(groupWidth - m_ribbonWidgetSpacing.x * 2, 28.0f);
systemLogOutButton->setText(U"LogOut");
tabPanel1->add(systemLogOutButton, U"RibbonFile_LogOutButton");
systemLogOutButton->onMouseRelease(
&Bin2HeaderMainForm::buttonFileLogOutCallBack, this);
auto separator1 = tgui::SeparatorLine::create();
separator1->setPosition(groupWidth, 0.0f);
separator1->setSize(2.0f, "100%");
tabPanel1->add(separator1, U"RibbonFile_Separator1");
auto separator1Renderer = separator1->getRenderer();
separator1Renderer->setColor(tgui::Color(192, 192, 192, 255));
auto fileOpenButton = tgui::RibbonButton::create(
tangoIconTheme::Icons28::Action_DocumentOpen.getBytes(),
tangoIconTheme::Icons28::Action_DocumentOpen.getByteCount());
fileOpenButton->setPosition(groupWidth + m_ribbonWidgetSpacing.x,
m_ribbonWidgetSpacing.y);
fileOpenButton->setSize(groupWidth - m_ribbonWidgetSpacing.x * 2, 28.0f);
fileOpenButton->setText(U"Open");
tabPanel1->add(fileOpenButton, U"RibbonFile_OpenButton");
fileOpenButton->onMouseRelease(
&Bin2HeaderMainForm::buttonFileOpenCallBack, this);
auto separator2 = tgui::SeparatorLine::create();
separator2->setPosition(2 * groupWidth, 0.0f);
separator2->setSize(2.0f, "100%");
tabPanel1->add(separator2, U"RibbonFile_Separator2");
auto separator2Renderer = separator2->getRenderer();
separator2Renderer->setColor(tgui::Color(192, 192, 192, 255));
auto infoBrowserButton = tgui::RibbonButton::create(
tangoIconTheme::Icons28::Apps_InfoBrowser.getBytes(),
tangoIconTheme::Icons28::Apps_InfoBrowser.getByteCount());
infoBrowserButton->setPosition(groupWidth * 2 + m_ribbonWidgetSpacing.x,
m_ribbonWidgetSpacing.y);
infoBrowserButton->setSize(groupWidth - + m_ribbonWidgetSpacing.x * 2, 28.0f);
infoBrowserButton->setText(U"Info");
tabPanel1->add(infoBrowserButton, U"RibbonFile_InfoButton");
infoBrowserButton->onMouseRelease(
&Bin2HeaderMainForm::buttonInfoCallBack, this);
auto editCopyButton = tgui::RibbonButton::create(
tangoIconTheme::Icons28::Action_EditCopy.getBytes(),
tangoIconTheme::Icons28::Action_EditCopy.getByteCount());
editCopyButton->setPosition(m_ribbonWidgetSpacing.x, m_ribbonWidgetSpacing.y);
editCopyButton->setSize(groupWidth, 28.0f);
editCopyButton->setText(U"Copy");
tabPanel2->add(editCopyButton, U"RibbonCopyButton");
auto editPasteButton = tgui::RibbonButton::create(
tangoIconTheme::Icons28::Action_EditPaste.getBytes(),
tangoIconTheme::Icons28::Action_EditPaste.getByteCount());
editPasteButton->setPosition(m_ribbonWidgetSpacing.x, 32.0f);
editPasteButton->setSize(groupWidth, 28.0f);
editPasteButton->setText(U"Paste");
tabPanel2->add(editPasteButton, U"RibbonPasteButton");
m_textArea = tgui::TextArea::create();
m_textArea->setPosition("3%", "100");
tgui::Layout hightLayout(tgui::Layout::Operation::Minus,
std::make_unique<tgui::Layout>("100%"),
std::make_unique<tgui::Layout>("140"));
m_textArea->setSize("94%", hightLayout);
std::string fontPath = "/usr/share/fonts/truetype/Hack-Regular.ttf";
std::ifstream themeFile(fontPath);
if (!themeFile.is_open())
std::cerr << "ERROR: Unable to load mono-space font.\n";
else
{
auto textAreaRenderer = m_textArea->getRenderer();
#ifdef TGUI_HAS_BACKEND_SFML_GRAPHICS
std::shared_ptr<tgui::BackendFont> monospaceBackendFont(
new tgui::BackendFontSFML());
#else
std::shared_ptr<tgui::BackendFont> monospaceBackendFont(
new tgui::BackendFontFreetype());
#endif
monospaceBackendFont->loadFromFile(fontPath);
tgui::Font monospaceFont(monospaceBackendFont, U"Hack-Regular");
textAreaRenderer->setFont(monospaceFont);
}
m_textArea->setText(U"Hello reader,\n\nthis is a multiline text editor.");
this->getGui().add(m_textArea, "TextArea");
this->setStatusText(L"Hello, I am a state text!");
return true;
}
Callbacks
The first three ribbon buttons have callbacks (non-static class method) registered:
systemLogOutButton->onMouseRelease(
&Bin2HeaderMainForm::buttonFileLogOutCallBack, this);
fileOpenButton->onMouseRelease(
&Bin2HeaderMainForm::buttonFileOpenCallBack, this);
infoBrowserButton->onMouseRelease(
&Bin2HeaderMainForm::buttonInfoCallBack, this);
Let's have a deeper look at these callbacks:
buttonFileLogOutCallBack
void buttonFileLogOutCallBack()
{
getWindow().close();
}
Here is an excerpt from the SFML documentation regarding void sf::Window::close():
Quote:
Close the window and destroy all the attached resources.
After calling this function, the sf::Window instance remains valid and you can call create() to recreate the window. All other functions such as pollEvent() or display() will still work (i.e. you don't have to test isOpen() every time), and will have no effect on closed windows.
So, calling close()
to exit the application is not elegant, but okay.
buttonFileOpenCallBack
void buttonFileOpenCallBack()
{
std::vector<tgui::String> imageFileExtensions;
if (m_fileOpenDialog == nullptr)
{
imageFileExtensions.push_back(U"*.png");
imageFileExtensions.push_back(U"*.jpg");
std::vector<std::pair<tgui::String, std::vector<tgui::String>>> filters;
filters.push_back(std::pair<tgui::String,
std::vector<tgui::String>>(U"Images (png, jpg)", imageFileExtensions));
filters.push_back(std::pair<tgui::String,
std::vector<tgui::String>>(U"All files",
std::vector<tgui::String>({U"*.*"})));
m_fileOpenDialog = tgui::FileDialog::create(U"Open file", U"Open");
m_fileOpenDialog->onClosing(
&Bin2HeaderMainForm::onClosingFileOpenCallBack, this);
m_fileOpenDialog->setFileTypeFilters(filters, 0);
}
m_fileOpenDialog->setPosition("5%", "5%");
m_fileOpenDialog->setSize("90%", "90%");
this->getGui().add(m_fileOpenDialog, U"FileOpenDialog");
return;
}
The m_fileOpenDialog
is a Bin2HeaderMainForm
class member field of type tgui::FileDialog::Ptr
.
There is another callback (non-static class method) registered:
m_fileOpenDialog->onClosing(
&Bin2HeaderMainForm::onClosingFileOpenCallBack, this);
This callback cares for the processing of the selected file. Since it adds no value to understand TGU, I'll leave it out.
buttonInfoCallBack
void buttonInfoCallBack()
{
if (m_infoMessageBox == nullptr)
{
tgui::String message(
U"This is 'Bin2HeaderTGUI', a TGUI sample application version 0.1.\n\n");
message.append(U"It shows some basic TGUI capabilities like:\n");
message.append(U"- TabContainer and RibbonButton with icon,\n");
message.append(U"- internal FileDialog to open a file including filter,\n");
message.append(U"- internal MessageBox and\n");
message.append(U"- mono-space Font assignment to the TextArea.\n");
std::vector<tgui::String> buttons = {U"OK"};
m_infoMessageBox = tgui::MessageBox::create(
U"Bin2HeaderTGUI 0.1", message, buttons);
m_infoMessageBox->onButtonPress(
&Bin2HeaderMainForm::buttonInfoMessageBoxCallBack, this);
}
m_infoMessageBox->setPosition("25%", "25%");
m_infoMessageBox->setSize("50%", "50%");
this->getGui().add(m_infoMessageBox, U"InfoMessageBox");
return;
}
The m_unfoMessageBox
is a Bin2HeaderMainForm
class member field of type tgui::MessageBox::Ptr
.
There is another callback (non-static class method) registered:
m_infoMessageBox->onButtonPress(
&Bin2HeaderMainForm::buttonInfoMessageBoxCallBack, this);
This callback cares for the closing of the message box.
bool buttonInfoMessageBoxCallBack()
{
return this->getGui().remove(m_infoMessageBox);
}
That's it so far.
The download of the project is located at the beginning of the chapter.
Have fun!
Points of Interest
Even if TGUI leaves out a lot of what other GUI libraries offer - in combination with SFML, a first simple example program looks very neat.
With the combination of TGUI and SFML, the development in C++ is really fun - current features of the STL are used abundantly.
I will definitely stay on the ball and dive deeper into TGUI and SFML.
History
- 15th October, 2022 Initial version
- 29th October, 2022 Fixes 1: Some minor fixes to the "Project Setup" chapter
- 6th November, 2022 Update 1: Added description for Manjaro Linux as a target system and explanations regarding the
X11_xcb_...
errors on openSUSE