<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by KDAB on Medium]]></title>
        <description><![CDATA[Stories by KDAB on Medium]]></description>
        <link>https://medium.com/@kdab?source=rss-78eb9e34b73f------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*vLn0j29LcPYL4ukXCRVq4w.png</url>
            <title>Stories by KDAB on Medium</title>
            <link>https://medium.com/@kdab?source=rss-78eb9e34b73f------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 30 May 2026 02:26:27 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kdab/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Bringing Flutter to RISC-V]]></title>
            <link>https://medium.com/industrial-flutter/bringing-flutter-to-risc-v-f612aa9d020e?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/f612aa9d020e</guid>
            <category><![CDATA[flutter-development]]></category>
            <category><![CDATA[flutter-developer]]></category>
            <category><![CDATA[embedded-systems]]></category>
            <category><![CDATA[risc-v]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Thu, 28 May 2026 08:05:30 GMT</pubDate>
            <atom:updated>2026-05-28T08:05:30.306Z</atom:updated>
            <content:encoded><![CDATA[<p>From Engine Patches to Running Apps on RISC-V Devices</p><p><em>This blog is written by Hannes Winkler, originally posted on </em><a href="https://www.industrialflutter.com/blogs/bringing-flutter-to-risc-v/"><em>industrialflutter.com</em></a></p><p>Flutter works on many platforms: from Web and Mobile, to Desktop and Embedded. When it comes to embedded, however, support is still constrained by the CPU architecture used. <a href="https://www.industrialflutter.com/blogs/flutter-on-armv6/">In a previous blog post</a>, we made Flutter &amp; Dart work on ARMv6 as a Proof of Concept. Now, we’re going to look at something more modern: The RISC-V architecture is an upcoming, free, open-source CPU architecture, showing great potential specifically for embedded use-cases.</p><p>In cooperation with <a href="https://cloud-v.co/">Cloud-V</a>, we made it possible to run Flutter apps on RISC-V hardware. This involves a few parts: the largest one is RISC-V support in the Dart programming language. Dart has its own compiler backend, which compiles Dart code into architecture-specific native code. So naturally, it is the component that contains the most architecture-specific parts. Fortunately, adding a RISC-V backend to the Dart SDK was already done by the Dart team in 2022: <a href="https://dart-review.googlesource.com/c/sdk/+/217289">https://dart-review.googlesource.com/c/sdk/+/217289</a></p><p>Additionally, changes to the Flutter Engine (specifically the build scripts) are required to enable building for RISC-V. Lastly, the part that brings it all together — and that actually allows building and running Apps on RISC-V devices — is the Flutter Tool. We are working on PRs for the official Flutter Tool &amp; Engine, but in the meantime, that functionality can be used via <a href="https://pub.dev/packages/flutterpi_tool">flutterpi_tool</a>.</p><h3>Engine</h3><p>First, the Flutter engine has to be built for RISC-V. Normally, this involves <a href="https://github.com/flutter/flutter/blob/master/engine/src/flutter/docs/contributing/Setting-up-the-Engine-development-environment.md">checking out the engine sources</a> and then using the engines gn script to generate the build files.</p><p>As an example, building for a Linux ARM64 machine roughly looks like this:</p><pre>$ gclient sync<br>...<br><br>$ pushd engine/src &amp;&amp; ./flutter/tools/gn \<br> --linux --linux-cpu arm64 \<br> --runtime-mode release \<br> --embedder-for-target --disable-desktop-embeddings \<br> --no-enable-unittests --no-build-glfw-shell \<br> --no-build-embedder-examples<br>Generating GN files in: out/linux_release_arm64<br>Generating compile_commands took 77ms<br>Done. Made 1178 targets from 345 files in 433ms<br><br>$ ninja -C out/linux_release_arm64</pre><p>The gn script that is being invoked there doesn&#39;t actually do that much; it&#39;s just a convenience script that converts the given command-line arguments into a different form, a args.gn file. This is then used by <a href="https://gn.googlesource.com/gn/">the actual</a> gn<a href="https://gn.googlesource.com/gn/"> tool</a> to generate the build files. So technically, it&#39;s not strictly necessary to modify it; it would also be possible to skip the gn script and craft a special args.gn file for RISC-V manually. In this case, we modified it to allow passing --linux-cpu riscv64, for development convenience.</p><h3>Setting up RISC-V cross-compilation</h3><p>Of course, if you now try running a build like that, you’ll undoubtedly run into errors. One of the first errors that’ll appear is that the build scripts try to use the wrong sysroot for building the RISC-V 64 binaries.</p><p>The reason for that can be found in the sysroot.gni<a href="https://github.com/flutter/flutter/blob/aef4718b39569939ec0052c44819cd01176234ca/engine/src/build/config/sysroot.gni#L24-L37">^1</a>, which is a gn &quot;header&quot;. This header has the job of resolving the correct sysroot to be used for cross-compilation. By default, the Flutter build will download &amp; use its own custom sysroot<a href="https://github.com/flutter/flutter/blob/master/engine/src/build/linux/sysroot_scripts/install-sysroot.py">^2</a> from a Google storage bucket. For platforms like linux-armv7, which are also not officially supported by the Flutter build, it&#39;s enough to just manually invoke those scripts to download the required sysroot. For RISC-V, however, this unfortunately does not work because there&#39;s no RISC-V sysroot present that could be downloaded from the Google storage bucket.</p><p>There are multiple ways to go forward here. You could, for example, build your own custom sysroot and use that. That could be done on a native RISC-V machine, in a VM, or in a Docker container. However, there’s an easier way: installing a RISC-V gcc will also install a minimal RISC-V “sysroot” that can be used to build simple binaries. Fortunately, the Flutter engine intentionally doesn’t have a lot of shared library decencies, which is ideal because we are using a minimal sysroot that might not have them. This approach was attempted and proved to work well enough for our purposes.</p><p>In practice, that means compilation is done without an explicit sysroot and clang auto-selects the RISC-V libc headers.</p><pre>--- a/engine/src/build/config/sysroot.gni<br>+++ b/engine/src/build/config/sysroot.gni<br>@@ -21,7 +21,16 @@ if (current_toolchain == default_toolchain &amp;&amp; target_sysroot != &quot;&quot;) {diff<br>   import(&quot;//build/config/android/config.gni&quot;)<br>   sysroot = rebase_path(&quot;$android_toolchain_root/sysroot&quot;, root_build_dir)<br> } else if (is_linux &amp;&amp; !is_chromeos) {<br>-  if (use_default_linux_sysroot &amp;&amp; !is_fuchsia) {<br>+  # install-sysroot.py doesn&#39;t provide a RISC-V 64 sysroot, which means<br>+  # we can&#39;t provide a default sysroot.<br>+  # <br>+  # If we make this fail here, when use_default_linux_sysroot == true and <br>+  # current_cpu == &quot;riscv64&quot;, the sysroot choosing for arm/arm64/x64<br>+  # will fail as well, so we wouldn&#39;t be able to cross-compile the gen_snapshots<br>+  # and build the libflutter_engine.so in one build anymore.<br>+  #<br>+  # Instead we just silently use no sysroot for riscv64 at all.<br>+  if (use_default_linux_sysroot &amp;&amp; !is_fuchsia &amp;&amp; current_cpu != &quot;riscv64&quot;) {<br>     if (current_cpu == &quot;x64&quot;) {<br>       sysroot =<br>           rebase_path(&quot;//build/linux/debian_sid_amd64-sysroot&quot;, root_build_dir)</pre><p>One more thing we have to do is actually make the compiler target riscv64-linux-gnu. There&#39;s a specific gn file that does the compiler setup. The RISC-V target handling can just be added there:</p><pre>--- a/engine/src/build/config/compiler/BUILD.gn<br>+++ b/engine/src/build/config/compiler/BUILD.gn<br>@@ -288,6 +288,11 @@ config(&quot;compiler&quot;) {<br>       if (arm_tune != &quot;&quot;) {<br>         cflags += [ &quot;-mtune=$arm_tune&quot; ]<br>       }<br>+    } else if (current_cpu == &quot;riscv64&quot;) {<br>+      assert(is_clang)<br>+<br>+      cflags += [ &quot;--target=riscv64-linux-gnu&quot; ]<br>+      ldflags += [ &quot;--target=riscv64-linux-gnu&quot; ]<br>     }<br> <br>     if (!is_android) {</pre><h4>Dependency issues</h4><p>Attempting to build the engine now will result in an error while resolving the freetype2 headers via pkg-config. However, as far as I can tell, the engine checks out &amp; builds its own copy of freetype2, so it seems like this resolved dependency isn&#39;t actually used. Simply removing the offending code fixes the issue:</p><pre>--- a/engine/src/build/config/linux/BUILD.gn<br>+++ b/engine/src/build/config/linux/BUILD.gn<br>@@ -27,9 +27,9 @@ config(&quot;fontconfig&quot;) {<br>   libs = [ &quot;fontconfig&quot; ]<br> }<br> <br>-pkg_config(&quot;freetype2&quot;) {<br>-  packages = [ &quot;freetype2&quot; ]<br>-}<br>+#pkg_config(&quot;freetype2&quot;) {<br>+#  packages = [ &quot;freetype2&quot; ]<br>+#}<br> <br> config(&quot;x11&quot;) {<br>   libs = [</pre><h4>Compatibility Fixes</h4><p>In addition to that, some engine headers do architecture detection (using preprocessor defines), and RISC-V detection was added to them:</p><pre>--- a/engine/src/flutter/assets/native_assets.cc<br>+++ b/engine/src/flutter/assets/native_assets.cc<br>@@ -17,6 +17,8 @@ namespace flutter {<br> #define kTargetArchitectureName &quot;ia32&quot;<br> #elif defined(FML_ARCH_CPU_X86_64)<br> #define kTargetArchitectureName &quot;x64&quot;<br>+#elif defined(FML_ARCH_CPU_RISCV64)<br>+#define kTargetArchitectureName &quot;riscv64&quot;<br> #else<br> #error Target architecture detection failed.<br> #endif</pre><pre>--- a/engine/src/flutter/fml/build_config.h<br>+++ b/engine/src/flutter/fml/build_config.h<br>@@ -96,6 +96,11 @@<br> #elif defined(__pnacl__)<br> #define FML_ARCH_CPU_32_BITS 1<br> #define FML_ARCH_CPU_LITTLE_ENDIAN 1<br>+#elif defined(__riscv) &amp;&amp; __riscv_xlen == 64 &amp;&amp; __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__<br>+#define FML_ARCH_CPU_RISCV_FAMILY 1<br>+#define FML_ARCH_CPU_RISCV64 1<br>+#define FML_ARCH_CPU_64_BITS 1<br>+#define FML_ARCH_CPU_LITTLE_ENDIAN 1<br> #else<br> #error Please add support for your architecture in flutter/fml/build_config.h<br> #endif</pre><pre>--- a/engine/src/flutter/third_party/tonic/common/build_config.h<br>+++ b/engine/src/flutter/third_party/tonic/common/build_config.h<br>@@ -103,6 +103,11 @@<br> #define ARCH_CPU_32_BITS 1<br> #define ARCH_CPU_LITTLE_ENDIAN 1<br> #endif<br>+#elif defined(__riscv) &amp;&amp; __riscv_xlen == 64 &amp;&amp; __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__<br>+#define ARCH_CPU_RISCV_FAMILY 1<br>+#define ARCH_CPU_RISCV64 1<br>+#define ARCH_CPU_64_BITS 1<br>+#define ARCH_CPU_LITTLE_ENDIAN 1<br> #else<br> #error Please add support for your architecture in build/build_config.h<br> #endif</pre><h3>Building the RISC-V Engine in CI</h3><p>All those patches were then pushed to a custom CI pipeline at <a href="https://github.com/ardera/flutter-ci">https://github.com/ardera/flutter-ci</a>. This CI essentially watches for Flutter updates and builds the engine with the applied compatibility changes, for a lot of different architectures:</p><ul><li>It builds the Flutter engine for armv7, aarch64 &amp; x64, with separate debug symbols, partially with CPU-specific tuning (for Pi 3 &amp; 4)</li><li>Furthermore, it builds the Dart cross compiler (gen_snapshot) for a lot of different host &amp; target combinations, including linux (arm/arm64/x64/riscv64), windows &amp; mac targeting all the different linux architectures.</li></ul><p>The built artifacts for all the different architectures are then published as a GitHub release.</p><h3>Running RISC-V Flutter Apps</h3><p>Now that all the necessary artifacts are present, they can be used to assemble Flutter apps. Normally, this is done by the Flutter Tool, which downloads these artifacts from a Google Storage bucket maintained by the Flutter team. In this case, however, the modifications were made to flutterpi_tool.</p><p>In general, there was not much work involved here. Just recognizing riscv64 as a uname output for architecture detection and adding the correct values to some enumerations:<br><a href="https://github.com/ardera/flutterpi_tool/commit/443c8efbfdf884701a1e097726b7a6039a0c19d5">https://github.com/ardera/flutterpi_tool/commit/443c8efbfdf884701a1e097726b7a6039a0c19d5</a></p><h3>Demonstration</h3><p><a href="https://www.kdab.com/">KDAB</a> used a Banana Pi BPI-F3, with free compute provided by <a href="https://cloud-v.co/">Cloud-V</a>. This platform helped us to get Flutter running on RISC-V. You can try it yourself and speed up your own software development by getting a free RISC-V compute from Cloud-V.</p><p>Contact:<br>Cloud V cloud-v@10xengineers.ai<br>KDAB <a href="https://www.kdab.com/contact/">https://www.kdab.com/contact/</a></p><p>Thanks to <a href="https://cloud-v.co/">Cloud-V</a> (a project by <a href="https://10xengineers.ai/">10xEngineers</a>) for demonstrating how to run Flutter on RISC-V hardware.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fvx0VmmmjejA%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dvx0VmmmjejA&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fvx0VmmmjejA%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/44034ee744c669163a4f546c80c6d5fa/href">https://medium.com/media/44034ee744c669163a4f546c80c6d5fa/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f612aa9d020e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/industrial-flutter/bringing-flutter-to-risc-v-f612aa9d020e">Bringing Flutter to RISC-V</a> was originally published in <a href="https://medium.com/industrial-flutter">Industrial Flutter</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Flutter Desktop Single-Instance Applications]]></title>
            <link>https://medium.com/industrial-flutter/flutter-desktop-single-instance-applications-83c0d3e6b243?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/83c0d3e6b243</guid>
            <category><![CDATA[flutter-desktop]]></category>
            <category><![CDATA[flutter-development]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[flutter-application]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Tue, 26 May 2026 10:13:30 GMT</pubDate>
            <atom:updated>2026-05-28T08:08:41.569Z</atom:updated>
            <content:encoded><![CDATA[<p><em>This blog is written by Werner Scholtz, originally posted on </em><a href="https://www.industrialflutter.com/blogs/flutter-desktop-single-instance-applications/"><em>industrialflutter.com</em></a></p><p>Most desktop applications are single-instance, meaning that if the user attempts to open the application again, the existing instance is typically brought to the foreground instead of launching a new one.</p><p>Flutter applications do not behave like this out of the box for all desktop platforms. Desktop Linux and Windows require some extra work.</p><h3>Section 1: Using packages</h3><p>Using packages to achieve single-instance applications in Flutter is possible. Basic functionality to focus on an existing window when it is re-launched is supported. Using packages is a lot simpler than modifying platform-specific code generated by Flutter.</p><p>Recommended packages:</p><ul><li><a href="https://pub.dev/packages/unix_single_instance">unix_single_instance</a>: Prevents multiple instances on Linux/macOS.</li><li><a href="https://pub.dev/packages/windows_single_instance">windows_single_instance</a>: A similar solution for Windows.</li><li><a href="https://pub.dev/packages/window_manager">window_manager</a>: Provides control over window states like showing and focusing.</li></ul><p>Example:</p><ol><li>flutter pub add window_manager</li><li>flutter pub add windows_single_instance</li><li>flutter pub add unix_single_instance</li></ol><pre>void main(List&lt;String&gt; args) async {<br> WidgetsFlutterBinding.ensureInitialized();<br>   // Initialize the window manager to control the app window<br> await windowManager.ensureInitialized();<br><br>  // Enforce single-instance behavior based on the platform<br> if (Platform.isLinux || Platform.isMacOS) {<br>   if (!await unixSingleInstance(args, _onSecondInstance)) exit(0);<br> } else if (Platform.isWindows) {<br>   await WindowsSingleInstance.ensureSingleInstance(<br>    args,<br>    &quot;custom_identifier&quot;,<br>    onSecondWindow: _onSecondInstance,<br>   );<br> }<br><br>  runApp(const MyApp());<br>}<br><br>// Handle what happens when a second instance is launched<br>void _onSecondInstance(List&lt;dynamic&gt; decodedArgs) async {<br>  windowManager.waitUntilReadyToShow(null, () async {<br>    await windowManager.show();<br>    await windowManager.focus();<br>  });<br>}</pre><h3>Section 2: Manual implementation</h3><p>You can achieve the same, and in some cases, more reliable results, by implementing single-instance enforcement in the files generated by Flutter. Doing it this way allows you much finer control over the behavior. It does have some downsides, as you need to maintain the code yourself.</p><p><em>Note:<br>It is possible to enforce single instances using Dart’s</em> <a href="https://api.flutter.dev/flutter/dart-io/RandomAccessFile-class.html"><em>RandomAccessFile</em></a>,<em> as it provides a way to lock files. The first instance would lock a file, the second instance would check if the file is locked, and then not start.</em></p><h3>Linux Desktop</h3><p>Desktop Linux applications built with Flutter use the GTK framework under the hood. By default, a Flutter application on Linux may allow multiple instances to run simultaneously. To enforce single-instance behavior, you can make changes to the linux/my_application.cc file. Below are the steps and explanations for each modification.</p><h3>Step 1: Remove the G_APPLICATION_NON_UNIQUE Flag</h3><p>The G_APPLICATION_NON_UNIQUE flag in GTK allows multiple instances of an application to run. To enforce single-instance behavior, you need to remove this flag from the application initialization code.</p><pre>MyApplication* my_application_new() {<br>  return MY_APPLICATION(<br>	g_object_new(<br>		my_application_get_type(), <br>		&quot;application-id&quot;, APPLICATION_ID,<br>		// &quot;flags&quot;, G_APPLICATION_NON_UNIQUE,<br>    	nullptr)<br>	);<br>}</pre><p>By removing the G_APPLICATION_NON_UNIQUE flag, the application becomes single-instance.</p><h3>Step 2: Check for Existing Windows and Present Them</h3><p>When the application is re-launched, the GApplication::activate signal is triggered. In this signal handler, you can check if a window is already open. If it exists, bring it to the foreground using the gtk_window_present() function. Otherwise, create a new window.</p><pre>// Implements GApplication::activate.<br>static void my_application_activate(GApplication* application) {<br>    MyApplication* self = MY_APPLICATION(application);<br><br>    GList* list = gtk_application_get_windows(<br>GTK_APPLICATION(application));<br><br>    GtkWindow* existing_window = list ? GTK_WINDOW(list-&gt;data) : NULL;<br><br>    if (existing_window) {<br>        gtk_window_present(existing_window);<br>    } else {<br>        // ...<br>  // Generated flutter code<br>  // ...<br>    }<br>}</pre><p><em>Note:<br>Linux distributions running Wayland have strict focus behaviors, and therefore, the above solution is inadequate for focusing the window. Instead, a request to show the window is made, and might appear as a notification.</em></p><h3>Windows</h3><p>By default, Windows desktop applications allow multiple instances to run simultaneously. To enforce single-instance behavior in a Flutter Windows app, you can make modifications to the windows/runner/main.cpp file.</p><h3>Step 1: Create a Named Mutex</h3><p>By creating a named <a href="https://en.wikipedia.org/wiki/Mutual_exclusion">mutex</a> (short for “mutual exclusion”) when starting a Flutter application, we can use it to enforce single-instance behavior.</p><p>Add the following code to the beginning of the wWinMain() function:</p><pre>#define APP_MUTEX_NAME L&quot;Global\\MyUniqueFlutterAppMutex&quot;<br><br>int APIENTRY wWinMain(...) {<br> HANDLE hMutex = CreateMutex(NULL, TRUE, APP_MUTEX_NAME);<br> if (GetLastError() == ERROR_ALREADY_EXISTS) {<br>     // App is already running.<br> }<br><br> ...<br>}</pre><h3>Step 2: Find and Present the Existing Window</h3><p>When the application detects that another instance is running, it brings the existing app window to the foreground instead of launching a new one. If you use something like window_manager to change the name of the window, the single instance will still be enforced; however, focusing on the existing window will not work.</p><p>Add the following code inside the if (GetLastError() == ERROR_ALREADY_EXISTS) block:</p><pre>HWND hwnd = FindWindow(NULL, APP_WINDOW_NAME);<br>if (hwnd) {<br>    ShowWindow(hwnd, SW_RESTORE);<br>    SetForegroundWindow(hwnd);<br>}<br>return 0;</pre><h3>Step 3: Set a Unique Window Name</h3><p>The window title serves as the identifier for FindWindow.</p><p>Update the Create call for the main window to use a unique name defined as APP_WINDOW_NAME:</p><pre>#define APP_WINDOW_NAME L&quot;unique_title&quot;<br><br>if (!window.Create(APP_WINDOW_NAME, origin, size)) {<br>    return EXIT_FAILURE;<br>}</pre><p>When the application exits or crashes, the mutex is automatically destroyed.</p><p>Add this code before the return EXIT_SUCCESS; statement:</p><pre>if (hMutex) {<br>    ReleaseMutex(hMutex);<br>}</pre><h3>macOS</h3><p>Flutter on macOS typically handles single-instance applications quite well on its own. When you launch a second instance of an application, the system usually brings the existing instance to the front. However, you can ensure the behavior by modifying the macos/Runner/AppDelegate.swift file.</p><h3>Step 1: Override the applicationShouldHandleReopen Function</h3><p>This method ensures that if the app is already running and a new instance is opened, the existing window is brought to the foreground.</p><p>Add this function to the AppDelegate class.</p><pre>override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -&gt; Bool {<br>  if !flag {<br>      sender.windows.forEach { $0.makeKeyAndOrderFront(self) }<br>  }<br>  return true<br>}</pre><h3>Step 2: Override the applicationDidFinishLaunching Function</h3><p>This function is called when the app finishes launching. It checks if there are other instances of the app running. If it finds another instance, it will activate the existing one and terminate the new instance.</p><p>Add this function to the AppDelegate class.</p><pre>override func applicationDidFinishLaunching(_ notification: Notification) {<br>  super.applicationDidFinishLaunching(notification)<br><br>  let appBundleIdentifier = Bundle.main.bundleIdentifier!<br><br>  let runningApps = NSRunningApplication.runningApplications(withBundleIdentifier: appBundleIdentifier)<br>  if runningApps.count &gt; 1 {<br>      // App is already running, activate the existing instance<br>      runningApps.first?.activate(options: .activateIgnoringOtherApps)<br>      NSApp.terminate(nil)<br>  }<br>}</pre><h3>Summary</h3><p>By implementing single-instance functionality, you can provide a polished and user-friendly experience for desktop applications. Whether you choose to use existing packages or dive into platform-specific code, Flutter makes it possible to achieve this seamlessly across platforms. The choice ultimately depends on your app’s requirements and your development preferences.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=83c0d3e6b243" width="1" height="1" alt=""><hr><p><a href="https://medium.com/industrial-flutter/flutter-desktop-single-instance-applications-83c0d3e6b243">Flutter Desktop Single-Instance Applications</a> was originally published in <a href="https://medium.com/industrial-flutter">Industrial Flutter</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Display Widget Windows in Qt Quick Application]]></title>
            <link>https://medium.com/kdab-blog/display-widget-windows-in-qt-quick-application-52af033a1d90?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/52af033a1d90</guid>
            <category><![CDATA[qml]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[qt]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Mon, 09 Feb 2026 10:51:11 GMT</pubDate>
            <atom:updated>2026-02-09T10:51:11.960Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Javier Cordero, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><p>Developing an application for desktop or embedded platforms often means choosing between Qt Widgets and Qt Quick to develop the UI. There are pros and cons to each. Qt, being the flexible framework that it is, lets you combine these in various ways. How you should integrate these APIs will depend on what you’re trying to achieve. In this entry I will show you how to display Qt Widget windows on an application written primarily using Qt Quick.</p><h3>Why Show a Qt Widget Window in a Qt Quick App</h3><p>Qt Quick is great for software that puts emphasis on visual language. A graphics pipeline, based around the Qt Quick Scene Graph, will efficiently render your UI using the GPU. This means UI elements can be drawn, decorated, and animated efficiently as long as you pick the right tools (e.g. <a href="https://doc.qt.io/qt-6/qml-qtquick-shadereffect.html">Shaders</a>, <a href="https://doc.qt.io/qt-6/qml-qtquick-animator.html">Animators</a>, and Qt’s <a href="https://doc.qt.io/qt-6/qtquick-shapes-qmlmodule.html">Shapes API</a> instead of its implementation of <a href="https://doc.qt.io/qt-6/qml-qtquick-canvas.html">HTML’s Canvas</a>).</p><p>From the Scene Graph also stem some of Quick’s weaknesses. UI elements that in other applications would extend outside of the application’s window, such as tool tips and the <a href="https://doc.qt.io/qt-6/qml-qtquick-controls-combobox.html">ComboBox</a>control, can only be rendered inside of Qt Quick windows. When you see other app’s tooltips and dropdowns extend beyond the window, those items are being rendered onto a separate windows; one without window decorations (a.k.a. borderless windows). Rendering everything on the same window helps ensure your app will be compatible with systems that can only display a single window at a time, such as <a href="https://www.kdab.com/software-technologies/platforms/android/">Android</a> and <a href="https://www.kdab.com/software-technologies/platforms/macos-ios/">iOS</a>, but it could result in wasted space if your app targets PC desktop environments.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*F4qZOJokeyyjwL7E.gif" /><figcaption><em>An animation shows a small window with QML’s and Widget’s ComboBoxes opening for comparison purposes</em></figcaption></figure><p>QML ComboBox is confined to the Qt Quick window while the Widgets ComboBox extends beyond the window</p><p>Qt lets us combine Widgets and Quick in a few ways. The most common approach is to embed a Qt Quick view into your Widgets app, using <a href="https://doc.qt.io/qt-6/qquickwidget.html">QQuickWidget</a>. That approach is fitting for applications that primarily use Widgets. Another option is to render Widgets inside a Qt Quick component, by rendering it through a <a href="https://doc.qt.io/qt-6/qquickpainteditem.html">QQuickPaintedItem</a>. However, this component will be limited to the same window confines as the rest of the items in your Quick window and it won’t benefit from Scene Graph rendering optimizations, meaning you get the worst of both worlds.</p><p>A third solution is to open widget windows from your Qt Quick apps. This has none of the aforementioned drawbacks, however, the approach has a couple of drawback of its own. First, the app would need to be run from a multi-window per screen capable environment. Second, widget windows are not parentable to Qt Quick windows; meaning certain window z-stack related features, such as setting window modality to Qt::WindowModal, won’t have effect on the triggering window when a Widget is opened from Qt Quick. You can work around that by setting modality to Qt::ApplicationModal instead, if you’re okay with blocking all other windows for modality.</p><p>Displaying Widget windows in Qt Quick applications has been useful to me in the past, and is something I haven’t seen documented anywhere, hence this tutorial.</p><h3>How to Show a Qt Widget Window in a Qt Quick App</h3><h4>Architecture Big Picture</h4><p>Displaying a Qt Widget window from Qt Quick is simpler than it seems. You’ll need two classes:</p><ol><li>The class that represents the widget window.</li><li>A class to interface from QML, that hosts and instantiates the window.</li></ol><p>You might be tempted to forgo the interface class and instantiate the widget directly. However, this would result in a crash. We’ll display the widget window by running Widget::show from the interface class.</p><h4>Update CMakeLists.txt</h4><p>In addition to those classes, you’ll also need to make sure that your app links to both Qt::Quickand Qt::Widgets libraries. Here&#39;s what that looks like for a CMake project</p><pre>// Locate libraries<br>find_package(Qt6 6.5 REQUIRED COMPONENTS<br>    Quick<br>    Widgets)<br><br>// Link build target to libraries<br>target_link_libraries(${TARGET_NAME} PRIVATE<br>    Qt6::Quick<br>    Qt6::Widgets)<br><br>// Replace ${TARGET_NAME} with the name of your target executable</pre><h4>Update main.cpp</h4><p>In addition to that, in main.cpp you&#39;ll need to use <a href="https://doc.qt.io/qt-6/qapplication.html">QApplication</a> in place of <a href="https://doc.qt.io/qt-6/qguiapplication.html">QGuiApplication</a>.</p><pre>QApplication app(argc, argv);</pre><h3>The Interface Layer</h3><p>Prepare the interface layer as you would any C++ based Quick component. By this I mean: derive from QObject, and use the Q_OBJECT and QML_ELEMENT macros to make your class available from QML.</p><pre>// widgetFormHandler.h<br>#pragma once<br>class WidgetFormHandler : public QObject<br>{<br>    Q_OBJECT<br>    QML_ELEMENT<br><br>public:<br>    explicit WidgetFormHandler(QObject *parent = nullptr);<br>};</pre><pre>// widgetFormHandler.cpp<br><br>WidgetFormHandler::WidgetFormHandler(QObject *parent)<br>    : QObject(parent)<br>{<br>}</pre><h4>Instantiating the Widgets Window</h4><pre>// widgetFormHandler.h<br>#pragma once<br>class WidgetsForm;<br>class WidgetFormHandler : public QObject<br>{<br>    Q_OBJECT<br>    QML_ELEMENT<br><br>public:<br>    explicit WidgetFormHandler(QObject *parent = nullptr);<br>~WidgetFormHandler();<br><br>private:<br>    std::unique_ptr&lt;WidgetsForm&gt; m_window;<br>}</pre><p>Use std::make_unique in the constructor to initialize the unique pointer to m_window.</p><p>Define the instantiating class’ destructor to ensure the pointers are de-alocated, thus preventing memory leaks. If you stick to using smart pointers, C++ will do all the work for you; simply use the default destructor, like I do here. Make sure to define it outside of the class’ header; some compilers have trouble dealing with the destructor when it’s defined inside the header.</p><pre>// widgetFormHandler.cpp<br>#include &quot;widgetFormHandler.h&quot;<br><br>WidgetFormHandler::WidgetFormHandler(QObject *parent)<br>    : QObject(parent)<br>    , m_window(std::make_unique&lt;WidgetsForm&gt;())<br>{<br>    // ...<br>}<br><br>WidgetFormHandler::~WidgetFormHandler() = default;</pre><h4>Make Properties Available to QML</h4><p>Now we want to make properties from the widget available in QML. How we do this will depend on the property and on whether we will manipulate the property’s value from both directions or only from one side only and update on the other.</p><p>Let’s look at a bi-directional example in which we add the ability to control the visible state of the widget window from QML. We’ll add a property called “visible” to the C++ interface so that it matches the visible that we get from Qt Quick windows in QML. Declare the property using Q_PROPERTY. Use READ and WRITE functions to control the window&#39;s state.</p><p>Here’s what that would look like:</p><pre>// widgetFormHandler.h<br>#pragma once<br>class WidgetsForm;<br><br>class WidgetFormHandler : public QObject<br>{<br>    Q_OBJECT<br>    QML_ELEMENT<br>    Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)<br><br>public:<br>    explicit WidgetFormHandler(QObject *parent = nullptr);    <br>~WidgetFormHandler();<br>    const bool isVisible();    <br>    void setVisible(bool);<br><br>signals:<br>    void visibleChanged();<br><br>private:<br>    std::unique_ptr&lt;WidgetsForm&gt; m_window;<br>};</pre><pre>// widgetFormHandler.cpp<br>#include &quot;widgetFormHandler.h&quot;<br><br>#include &quot;widgetForm.h&quot;<br><br>WidgetFormHandler::WidgetFormHandler(QObject *parent)<br>    : QObject(parent)<br>    , m_window(std::make_unique&lt;WidgetsForm&gt;())<br>{<br>    // Hide window by default<br>    m_window-&gt;setVisible(false);<br>}<br>WidgetFormHandler::~WidgetFormHandler() = default;<br>const bool WidgetFormHandler::isVisible()<br>{<br>    return m_window-&gt;isVisible();<br>}<br>void WidgetFormHandler::setVisible(bool visible)<br>{<br>    m_window-&gt;setVisible(visible);<br>    emit visibleChanged();<br>}</pre><p>To make this bi-directional, set NOTIFY to a signal that allows the property to be updated in QML after it being emitted and emit the signal where applicable. We emit it from setVisible in this class, however if QWidget had a signal that emitted when its visible state changed, I would also make a connection between that signal and that of our handler’s visibleChanged. However, that isn’t the case, so we have to make sure to emit it ourselves.</p><h4>Make Signals Available to QML</h4><p>Develop the widget window as you would any other widget. If you use UI forms, go to the header file and create a signal for each action that you wish to relay over to QML.</p><p>In this example we’ll relay a button press from the UI file, so we’ll create a button named pushButton in our ui file:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*r9Aw6n2DKS7rE7C4.png" /><figcaption>Qt Designer shows UI file with a button named pushButton, in camel case.</figcaption></figure><p>Now add a buttonClicked signal to our header:</p><pre>// widgetsForm.h<br>#pragma once<br>#include &lt;QWidget&gt;<br><br>namespace Ui<br>{<br>    class WidgetsForm;<br>}<br><br>class WidgetsForm : public QWidget<br>{<br>    Q_OBJECT<br>public:<br>    explicit WidgetsForm(QWidget *parent = nullptr);<br>    ~WidgetsForm();<br>signals:<br>    void buttonClicked();<br>    // Signal to expose button click from Widgets window<br>private:<br>    std::unique_ptr&lt;Ui::WidgetsForm&gt; ui;<br>};</pre><p>Once again, we use a unique pointer, this time to hold the ui object. This is better than what Qt Creator templates give us because it means C++ handles the memory management for us and we can avoid the need for a delete statement in the destructor.</p><p>In the window’s constructor, we make a connection between the UI’s button’s signal and the one that we’ve created to relay the signal for exposure.</p><pre>// widgetsForm.cpp<br>#include &quot;widgetsform.h&quot;<br>#include &quot;ui_widgetsform.h&quot;<br><br>WidgetsForm::WidgetsForm(QWidget *parent)<br>    : QWidget(parent)<br>    , ui(std::make_unique&lt;Ui::WidgetsForm&gt;())<br>{<br>    ui-&gt;setupUi(this);<br>    // Expose click<br>    connect(ui-&gt;pushButton, &amp;QPushButton::clicked, this, &amp;WidgetsForm::buttonClicked);<br>}<br><br>WidgetsForm::~WidgetsForm() = default;</pre><p>Before we connect the exposed signal to the QML interface, we need another signal on the interface to expose our event over to QML. Here I add qmlSignalEmitter signal for that purpose:</p><pre>// widgetFormHandler.h<br>[..]<br>signals:<br>    void visibleChanged();<br>    void qmlSignalEmitter();  // Signal to relay button press to QML<br>[..]</pre><p>To complete all the connections, go to the interface layer’s constructor and make a connection between your window class’ signal and that of the interface layer. This would look as follows:</p><pre>// widgetFormHandler.cpp<br>[..]<br>WidgetFormHandler::WidgetFormHandler(QObject *parent)<br>    : QObject(parent)<br>    , m_window(std::make_unique&lt;WidgetsForm&gt;())<br>{<br>    QObject::connect(m_window, &amp;WidgetsForm::buttonClicked, this,<br>    &amp;WidgetFormHandler::qmlSignalEmitter);<br>}<br>[..]</pre><p>By connecting one emitter to another emitter we keep each classes’ concerns separate and reduce the amount of boilerplate code, making our code easier to maintain.</p><p>Over at the QML, we connect to qmlSignalEmitter using the on prefix. It would look like this:</p><pre>import NameOfAppQmlModule  // Should match qt_add_qml_module&#39;s URI on CMake<br><br>WidgetFormHandler {<br>    id: fontWidgetsForm<br><br>    visible: true  // Make the Widgets window visible from QML<br><br>    onQmlSignalEmitter: () =&gt; {<br>        console.log(&quot;Button pressed in widgets&quot;)  // Log QPushButton&#39;s click event from QML<br>    }<br>}</pre><h3>Final Product</h3><p>I’ve prepared a demo app where you can see this technique in action. The demo displays text that bounces around the screen like an old DVD player’s logo would. You change the text and font through two identical forms, one implemented in QML and the other done in Widgets. The code presented in this tutorial comes from that demo app.</p><p>Example code: <a href="https://github.com/KDABLabs/kdabtv/tree/master/Blog-projects/Widget-window-in-Qt-Quick-app">https://github.com/KDABLabs/kdabtv/tree/master/Blog-projects/Widget-window-in-Qt-Quick-app</a></p><p><a href="https://youtu.be/IGGBhsFESR4?feature=shared">https://youtu.be/IGGBhsFESR4</a></p><p>The moving text should work on all desktop systems except for Wayland sessions on Linux. That is because I’m animating the window’s absolute position (which is restricted in Wayland for security reasons) rather than the contents inside a window. This has the benefit of not obstructing other applications, since the moving window that contains the text would capture mouse inputs if clicked, preventing those from reaching the application behind it.</p><h3>Real World Use Case</h3><p>The first time I employed this technique was in my FOSS project, QPrompt. I use it there to provide a custom font dialog that doubles as a text preview. Having a custom dialog gives me full control over formatting options presented to users, and for this app we only needed a preview for large text and a combo box to choose among system fonts. QPrompt is also open source, you can find the source code relevant to this technique here: <a href="https://github.com/Cuperino/QPrompt-Teleprompter/blob/main/src/systemfontchooserdialog.h">https://github.com/Cuperino/QPrompt-Teleprompter/blob/main/src/systemfontchooserdialog.h</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*TbAqsk_v7UaIoKvy.png" /></figure><p>Thank you for reading. I hope you’ll find this useful. A big thank you to David Faure for suggesting the use of C++ unique pointers as well as reviewing the code along with Renato and my team.</p><p>If there are other techniques that you’d like for us to try or showcase, let us know.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=52af033a1d90" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/display-widget-windows-in-qt-quick-application-52af033a1d90">Display Widget Windows in Qt Quick Application</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Streamlining Development by Mastering Modern Development Practices]]></title>
            <link>https://medium.com/kdab-blog/streamlining-development-by-mastering-modern-development-practices-8b27d391134e?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/8b27d391134e</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-best-practices]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Sat, 20 Dec 2025 10:14:06 GMT</pubDate>
            <atom:updated>2025-12-20T10:14:06.265Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Milian Wolff, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><p>Modern embedded software development demands precision, adaptability, and efficiency. Projects often span diverse hardware platforms, each with unique needs, and must meet strict real-time requirements. Best practices like containerized environments, streamlined CI pipelines, and incremental refactoring, often provide the edge needed to deliver reliable, high-performance solutions. Let’s look at these practices to help enhance your development processes and overcome some common challenges in embedded workflows.</p><h3>Building in isolation</h3><p>Containers are revolutionizing embedded software development by providing a controlled and consistent environment for builds. This approach prevents conflicts between your local development environment and the build environment, offering much-needed clarity and stability. Three key reasons why containers matter in embedded development:</p><ul><li>Consistency across teams: Developers working on different platforms can rely on identical, preconfigured environments. This eliminates discrepancies caused by local setup variations and ensures reproducibility. For example, you could use a container to encapsulate your development toolchain so that every team member compiles with identical compiler versions and configurations.</li><li>CI/CD integration: Containers enable rapid deployment of isolated build instances within CI pipelines. This means you can spin up a new container for each build and run tests in parallel without interfering with other processes.</li><li>Simplified onboarding: New developers can pull a container image and start building without manually configuring dependencies, saving hours of setup time.</li></ul><p>While containers add overhead during setup, the long-term gains in reliability and scalability make them a great fit for embedded workflows. For a deeper dive on containers, check out the <a href="https://www.kdab.com/the-developers-guide-to-containers/">Developer’s Guide to Containers</a> by my colleague, Andrew Hayzen.</p><h3>Managing dependencies</h3><p>Most embedded projects rely on numerous dependencies, including runtime libraries, open-source components, and developer tools. It’s important to manage these dependencies effectively to keep your builds stable and your CI pipelines smooth.</p><p>One approach is to build all critical dependencies from source. For smaller, fast-moving dependencies, this approach provides flexibility and ensures alignment with project requirements. However, for more stable, less frequently updated dependencies, using package managers or pre-built binaries to distribute precompiled packages can save time and reduce the overall burden on your team. Striking the right balance between the two approaches depends on the complexity and size of your specific stack.</p><h3>CI done right</h3><p>When CI works well, it’s a developer’s best friend. Automated builds, testing, static analysis, and code sanitizers provide fast feedback and improve code quality. However, CI is more challenging in embedded development due to hardware constraints, cross-compilation requirements, and resource limitations. Yet, a well-tuned CI pipeline can transform how teams work. Here are a few ways to optimize your pipeline:</p><ol><li>HIL testing: Integrating physical hardware into your CI pipeline is a great way to test firmware against real-world conditions. While emulators are useful, they can miss hardware-specific bugs like race conditions and timing issues — things you might be able to catch with a cluster of low-cost boards like Raspberry Pis in your test framework.</li><li>Parallelized builds and tests: Leveraging cloud services or dedicated build servers to run parallel jobs, can reduce feedback time. This approach is useful for tasks like testing multiple product configurations by distributing the workload across several nodes.</li><li>Static and dynamic analysis tools: Tools like <a href="https://clang.llvm.org/extra/clang-tidy/">clang-tidy</a> or <a href="https://github.com/google/sanitizers">Google Sanitizers</a> can be integrated into the CI pipeline to catch potential issues early. Static analysis ensures code adheres to best practices, while dynamic analysis detects runtime errors.</li></ol><h3>Iterative improvements without stalling development</h3><p>Refactoring is essential for long-term maintainability of clean, manageable code, but it must be handled carefully to avoid disrupting ongoing development. Instead of halting production for massive rewrites, adopt a trunk-based development approach using ‘atomic commits’ where changes for small, self-contained updates are merged into the main branch <a href="https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development">at least once a day</a>. Atomic commits work because short-lived branches and incremental changes keep the codebase current while avoiding the pitfalls of stale, long-lived branches and minimizing integration headaches. Plus, every commit is tested via CI pipelines and static analysis, catching issues early.</p><p>However, sometimes you can’t avoid working with large-scale changes, particularly in big codebases. Examples include migrating hardware abstraction layers and rewriting critical middleware. In these cases, you may want to create parallel APIs that allow old and new implementations to coexist temporarily. This approach enables incremental migration of refactored modules to the new API while maintaining the legacy API for unaffected components. Both APIs should coexist only for as long as it takes to confidently migrate and validate all dependent modules, Validation should include unit, integration, and hardware tests. Once all refactored modules have been integrated into the new API, the legacy API should be deprecated.</p><p>By refactoring incrementally in this way and maintaining stability, teams can avoid the risk of system-wide disruptions, turning what is often considered a very disruptive process into a seamless part of development.</p><h3>Key takeaways</h3><p>By embracing modern development practices tailored to the unique challenges of embedded systems, teams can achieve faster cleaner, more maintainable codebases without sacrificing productivity. Strategies discussed in this blog include:</p><ul><li>Leveraging containers for consistent build environments</li><li>Building dependencies wisely, balancing flexibility with efficiency</li><li>Optimizing CI pipelines for fast, actionable feedback</li><li>Approaching refactoring incrementally for minimal disruption</li></ul><p>These practices are just the tip of the iceberg in creating a robust development environment. For those looking to delve deeper into optimizing their development workflows, our full whitepaper on <a href="https://www.kdab.com/publications/bestpractices/best-practices-general-development.html#general-development">General Development</a> offers more insights and additional strategies tailored to modern software teams.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8b27d391134e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/streamlining-development-by-mastering-modern-development-practices-8b27d391134e">Streamlining Development by Mastering Modern Development Practices</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Indented Printing with fmt]]></title>
            <link>https://medium.com/kdab-blog/indented-printing-with-fmt-c90518d741f5?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/c90518d741f5</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[formatting-code]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Tue, 16 Dec 2025 15:22:27 GMT</pubDate>
            <atom:updated>2025-12-16T15:22:27.076Z</atom:updated>
            <content:encoded><![CDATA[<h4>Pretty printing of nested structs in C++</h4><p><em>by Sean Harmer, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><h3>Introduction</h3><p>We all need to log out data from time to time whilst developing or even when running in production. Sometimes this will be to stdout, stderr, a logfile, the system logger etc. Using tools such as spdlog and fmt make this a breeze. However, one thing has been causing an itch that I&#39;ve wanted to scratch for a while now. How can we do nicely formatted printing of nested structs and maintaining indentation?</p><p>Given some simple nested structs such as:</p><pre>struct Vec2 {<br>    int x;<br>    int y;<br>};<br><br>struct Size {<br>    int width;<br>    int height;<br>};<br><br>struct Rect {<br>    Vec2 position;<br>    Size size;<br>};<br><br>...<br>Rect rect{ { 0, 20 }, { 800, 600 } };<br>spdlog::info(&quot;rect: {}&quot;, rect);</pre><p>We would like to easily obtain output similar to this:</p><pre>rect: Rect2 {<br>    position: Vec2 {<br>        x: 0<br>        y: 20<br>    }<br>    size: Size2 {<br>        width: 800<br>        height: 600<br>    }<br>}</pre><p>We hope this short blog post can show you a way to do exactly that using fmt in a way that you can easily extend to your own types and customise further. This can then be used with spdlog or similar to make your log files and debug output kinder to humans.</p><h3>General Approach</h3><p>The excellent <a href="https://fmt.dev/11.0/api/#formatting-user-defined-types">fmt documentation</a> explains how to provide support for formatting your own types by providing a specialization of the fmt::formatter template and taking advantage of subclassing or composing an existing fmt::formatter to take advantage of the facilities they provide (e.g. parsing formatting specifications).</p><p>The slight design issue we have here is that fmt is oriented around the concept of formatting independent lines of output. We need to have some state or context that is persistent across multiple lines, namely the amount to indent.</p><p>Examining the above layout of the output we see that we can break the output of a struct down to the following steps:</p><ol><li>Output the struct name and an opening curly brace at a given level of indentation and a newline.</li><li>Increase the indentation by one level</li><li>Iterate over the struct members and for each, output the member label, a colon, and the member value. If the member is itself a struct, go back to 1. If the member is a “scalar”, output a newline.</li><li>Decrease the indentation by one level</li><li>Output a closing curly brace and a newline.</li></ol><p>Our approach to this, is to provide a new class that specializes the fmt::formatter template that manages the above steps. In order to easily allow customisation to output your own types taking advantage of the indenting facilities, we allow inheriting from our custom formatter using the <a href="https://www.geeksforgeeks.org/curiously-recurring-template-pattern-crtp-2/">Curiously Recurring Template Pattern (CRTP)</a>. This nicely allows customisation without any runtime overhead from virtual functions and allows us to provide most of the boilerplate in the base class.</p><p>If you just want to see the final code, you can <a href="https://godbolt.org/z/MqGKMPaej">play with it here</a>.</p><h3>The Indenting Formatter</h3><p>To specialize the fmt::formatter template we must provide two functions:</p><ul><li>constexpr auto parse(fmt::format_parse_context &amp;ctx) -&gt; fmt::format_parse_context::iterator</li><li>auto format(auto v, fmt::format_context &amp;ctx) const -&gt; fmt::format_context::iterator</li></ul><p>We won’t go into the gory details of the former in this article. You can read it for yourself in the <a href="https://godbolt.org/z/MqGKMPaej">final code</a> if you are interested. Suffice it to say it is concerned with extracting the initial indentation level if the user specifies one.</p><h3>The format function</h3><p>The format function in the indenting formatter is responsible for orchestrating the algorithm outlined above. Let’s tackle the easy part first: outputting the type name and the opening curly brace. We require the developer to subclass using the CRTP which we can then make use of to output the custom type’s name via a name() member function on the derived class:</p><pre>auto format(auto v, fmt::format_context &amp;ctx) const<br>        -&gt; fmt::format_context::iterator<br>{<br>    const T &amp;derived = static_cast&lt;const T &amp;&gt;(\*this);<br>    fmt::format_to(ctx.out(), &quot;{} {{\\n&quot;, derived.name());<br><br>// Get indentation level and output members<br>    ...<br><br>// Output the closing curly brace and return the context iterator<br>    ...<br>}</pre><p>The fmt::format_to function works just like the regular fmt::format function except that it outputs to an output iterator which in this case is part of the format_context we are provided with. The remaining arguments are the format specification followed by any variables to be substituted in. For more information, there is an excellent <a href="https://fmt.dev/11.0/syntax/">guide to the fmt syntax</a>.</p><p>To output the type name we obtain a reference to the derived object and call the derived.name() member function. The format specification just says to output the substituted type name, followed by a literal curly brace, followed by a newline.</p><h3>Extracting the indentation level</h3><p>The parse functions provided by formatters are responsible for extracting the formatting instructions and keeping track of explicitly numbered arguments. Using explicitly numbered arguments allows the substitutions to appear in an explicit order rather than the implicit left-to-right ordering in the format specification. Within our format function, we must handle the case of numbered arguments when deciding upon which indentation level to use. To do so, we can lookup argument values from the format specification and operate upon them.</p><pre>auto format(auto v, fmt::format_context &amp;ctx) const<br>        -&gt; fmt::format_context::iterator<br>{<br>    // Output type name<br>    ...<br><br>    // Extract indentation level to use<br>    size_t indentToUse;<br>    if (!hasArgumentIndex) {<br>        // Implicit argument ordering - use indent from parse() function<br>        indentToUse = indent;<br>    } else {<br>        // Numbered argument ordering<br>        auto argValue = ctx.arg(argumentIndex);<br>        auto extractSizeT = \[\]&lt;typename U&gt;(const U &amp;v) -&gt; size_t {<br>            if constexpr (std::is_integral_v&lt;U&gt;) {<br>                return v;<br>            } else {<br>                throw std::format_error(&quot;Wrong argument datatype provided&quot;);<br>                return {};<br>            }<br>        };<br><br>        indentToUse = fmt::visit_format_arg(extractSizeT, argValue);<br>    }<br><br>    // Output members and closing brace<br>    ...<br>}</pre><p>Full credit to my amazing colleague Giuseppe D’Angelo for making this work!</p><h3>Formatting values and structs</h3><p>Armed with our current indentation level we can now increment this and delegate outputting of the members to the derived class:</p><pre>auto format(auto v, fmt::format_context &amp;ctx) const<br>        -&gt; fmt::format_context::iterator<br>{<br>    // Output type name<br>    ...<br><br>    // Extract indentation level to use<br>    ...<br><br>    // Output members and closing brace<br>    memberIndent = indentToUse + 4; // Yes it&#39;s a magic number<br>    derived.format_members(v, ctx);<br><br>    // Output closing curly brace<br>    ...<br>}</pre><p>From the above, we see that we expect a derived class to now also provide another member function called format_members. This function should output the members that you care about. To make life easier for developers, let&#39;s make a couple of functions on our base class that can be used by the derived classes to format a &quot;scalar&quot; value as well as a member that is itself another struct:</p><pre>void format_scalar(std::string_view label, auto v, fmt::format_context &amp;ctx) const<br>{<br>    fmt::format_to(ctx.out(), &quot;{:{}}{}: {}\\n&quot;, &quot;&quot;, memberIndent, label, v);<br>}<br><br>void format_struct(std::string_view label, auto v, fmt::format_context &amp;ctx) const<br>{<br>    fmt::format_to(ctx.out(), &quot;{:{}}{}: {:{}}\\n&quot;, &quot;&quot;, memberIndent, label, v, memberIndent);<br>}</pre><p>Both of these functions make use of a neat feature in fmt that allows us to dynamically specify the format specification. In this case we want to be able to specify the indentation used when a derived class calls one of these helpers.</p><p>Notice the format specifications in both of these functions include some nested substitutions. When dealing with implicitly ordered substitution placeholders, they are ordered from left to right and then from outside in. The inner placeholders are prefixed by a colon which means the value that gets substituted in will be used to specify the indentation level.</p><p>The use of an empty string for the surrounding placeholder just means that fmt will expand that placeholder to be the specified width - i.e. pad it with spaces. The following color-coded diagrams should hopefully illustrate how the various parts of the format specifications map to the substituted values.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*zFo2sk7gYZhFJcxG.png" /></figure><p>Substitutions for format_scalar</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*YdSwh_ZpIFJMdYKn.png" /></figure><p>Substitutions for format_struct</p><p>One thing of note here is that in the format_struct function (see Fig. 2), the purple width specifier is used to provide the current member indentation level as the surrounding blue placeholder recurses into the indenting formatter for the child struct. It is the format() and format_struct() functions that form the recursive pair to properly indent the output with the level of nesting.</p><p>One final piece is just combining the dynamic width trick with outputting a literal closing curly brace to finish off the output for a given struct:</p><pre>auto format(auto v, fmt::format_context &amp;ctx) const<br>        -&gt; fmt::format_context::iterator<br>{<br>    // Output type name<br>    ...<br><br>    // Extract indentation level to use<br>    ...<br><br>    // Output members and closing brace<br>    ...<br><br>    // Output closing curly brace<br>    return fmt::format_to(ctx.out(), &quot;{:{}}}}&quot;, &quot;&quot;, indentToUse);<br>}</pre><h3>Providing a concrete indenting formatter</h3><p>With all of the above elements in place, writing a custom subclass of the indenting formatter for your own type is very easy. Here is the subclass for the Vec2 type used in the motivating example:</p><pre>template&lt;&gt;<br>struct fmt::formatter&lt;Vec2&gt; : indenting_formatter&lt;fmt::formatter&lt;Vec2&gt;&gt; {<br>    auto name() const -&gt; std::string_view { return &quot;Vec2&quot;; }<br><br>    auto format_members(Vec2 v, fmt::format_context &amp;ctx) const<br>            -&gt; fmt::format_context::iterator<br>    {<br>        format_scalar(&quot;x&quot;, v.x, ctx);<br>        format_scalar(&quot;y&quot;, v.y, ctx);<br>        return ctx.out();<br>    }<br>};</pre><p>All we have to do is:</p><ul><li>Inherit using CRTP.</li><li>Provide a name() function to return the type name as we wish to see it.</li><li>Provide a format_members() function that will typically make use of the format_scalar()and format_struct() helpers we built above.</li></ul><h3>Going further</h3><p>There are a few improvements we could make to the above. For example, the indentation is hard-wired to 4 spaces per level but this could be made configurable. Providing another helper on the base class for formatting values wrapped in a std::optional that could print &quot;&lt;Not Set&gt;&quot; when the optional does not contain a value. Adding support for arrays in whatever format you wish to present them. Even nicer would be if we could generate the derived indenting formatter classes automatically using reflection.</p><p>Using the indenting formatter within spdlog is completely transparent since it uses fmt under the hood. If you are using spdlog in your projects, you may also be interested in the <a href="https://github.com/KDAB/KDSPDSetup">KDSpdSetup</a> library that allows you to configure all of your logging from a simple toml file.</p><p>We hope that you found this article an interesting exercise in abusing the fmt api slightly to provide a more human readable output of your types. Feel free to take the code and use it as you wish.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c90518d741f5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/indented-printing-with-fmt-c90518d741f5">Indented Printing with fmt</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Integrate QML Window’s Background with the System’s Color Palette]]></title>
            <link>https://medium.com/kdab-blog/integrate-qml-windows-background-with-the-system-s-color-palette-0939c5972105?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/0939c5972105</guid>
            <category><![CDATA[qt]]></category>
            <category><![CDATA[qml]]></category>
            <category><![CDATA[resize-images]]></category>
            <category><![CDATA[qml-development]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Mon, 08 Dec 2025 09:09:05 GMT</pubDate>
            <atom:updated>2025-12-08T09:09:05.619Z</atom:updated>
            <content:encoded><![CDATA[<h4>For a Correct Background Color When Resizing</h4><p><em>by Javier Cordero, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><p>As a person who enjoys responsive designs, I like to resize apps and see how quickly the UI updates after the resize. If you’re making an app with Qt Quick, you’ll sometimes find that, as the window size increases, the UI will lag behind for one or more frames, revealing a white background on two of the window’s edges, that usually looks out of place on desktop applications.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*YVHJEqfzXGiuKhIz.jpg" /><figcaption>Thumbnail: A window with a white border where there should be a background color.</figcaption></figure><h3>Why is the color background revealed when the window size is increased?</h3><p>This issue stems from the fact that Qt Quick windows will render a background color, white by default, before the position of elements is updated by the scene graph, which lags at least one frame behind the resize because it uses a frame buffer. If the default background is white, that means the QML window itself is not aware of your application’s theme and that is intentional; after all, only desktop apps need to be aware of the background color used by native applications.</p><p>Qt Quick Controls, however, are theme aware, and that may include your platform’s theme. This is why, when you make an app for Windows, if you set a Label against the application’s default white background, it will look just fine. But when that app is moved to macOS or a Linux distribution with a dark theme, the label’s text will be drawn with a light color to match that theme, and that will make it nearly impossible to read against the default white background.</p><p>One solution for that issue is to place these components against a control that extends through a large area, such as a <a href="https://doc.qt.io/qt-6/qml-qtquick-controls-pane.html">Pane</a>. Qt Quick Controls’ theme awareness will ensure the right background color is used. However, controls are only drawn within the window, so when the window is resized, its true background color will seep through — and that’s the issue with which we’re concerned. The correct solution is to set the window’s background to the system’s or the theme’s background color, which will make the frame buffer’s lag far less noticeable during resizing.</p><h3>How to Make Qt Quick Window Backgrounds Aware of Your System’s Theme</h3><p>Setting a background color is as simple as assigning a value to the “color” property of the window, like so:</p><pre>Window {<br>    color: &quot;lightblue&quot;<br>}</pre><p>To have it match the background color of native apps, use QML’s <a href="https://doc.qt.io/qt-6/qml-qtquick-systempalette.html">SystemPallete</a> type, as follows:</p><pre>import QtQuick<br>Window {<br>    color: systemPalette.window<br>    SystemPalette {<br>        id: systemPalette<br>    }<br>}</pre><p>Here you can find the various <a href="https://doc.qt.io/qt-6/qtquickcontrols-styles.html">default styles used by Qt Quick for each supported platform</a>. By making use of SystemPalette, the appropriate background color for the current platform will be chosen.</p><p>It is important to note that, using SystemPalette, the background color will be chosen based on the current platform and not on which QML style has actually been applied, which may differ if your app makes use of a fixed style for all platforms.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/0*3z_adRaSmSPblOrP.gif" /><figcaption><em>Animation of a window resizing, letting the white background seep through a corner.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/878/0*0rFVHSxzND2FVPKM.gif" /><figcaption><em>Animation of a window resizing. The background color is consistent with the theme at all times.</em></figcaption></figure><h3>Platform Style vs. Fixed Style</h3><p>As an alternative to following the platform’s theme, you could provide a uniform experience for all platforms by enforcing one style for all. There are minor performance advantages to having a fixed style for all systems, but these only tend to matter on low-end embedded hardware, where every cycle counts. There you could use <a href="https://doc.qt.io/qt-6/qtquickcontrols-styles.html#compile-time-style-selection">Compile-Time Style Selection</a>, so the <a href="https://doc.qt.io/qt-6/qtqml-qtquick-compiler-tech.html#the-qml-script-compiler">QML compiler</a> knows which style is in use and can generate C++ code for bindings.</p><p>If you are creating a desktop application, your theming choice should be based, not on performance, but on the level of system integration and the user experience you wish to provide. A photography application might benefit from having a dark mode at all times so the picture colors can pop. An e-mail client, on the other hand, might do better integrating with the system’s theme or allowing users to choose between a light theme and a dark theme. Light backgrounds generally provide better contrast, which makes them a good default choice for emails. However, users may prefer the system theme or a dark theme, if it’s easier on their eyes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*voyXrL7iqhNWHQuA.png" /><figcaption>A window from one OS, with a light theme. Its contents can be read just fine.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*-ypKGn_WWHj3tciF.png" /><figcaption>A window from a different OS, with a dark theme. Some of it contents cannot be read due to a lack of contrast.</figcaption></figure><h3>Crafting Seamless and Theme-Aware Qt Quick Applications</h3><p>By thoughtfully managing your Qt Quick application’s window background color, you can significantly improve its responsiveness and aesthetic integration with the host platform. Whether you opt to align with the system’s theme using <a href="https://doc.qt.io/qt-6/qml-qtquick-systempalette.html">SystemPalette</a> or enforce a fixed style for uniformity, your choice should reflect the needs of your application and its users. Balancing system integration, performance, and user experience ensures your app not only looks great across platforms but also feels natural to use, providing a polished and professional result. With these tools and insights, you can create dynamic, theme-aware applications that stand out in usability and design.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0939c5972105" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/integrate-qml-windows-background-with-the-system-s-color-palette-0939c5972105">Integrate QML Window’s Background with the System’s Color Palette</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Delivering Software Updates: The Last Mile of Product Development]]></title>
            <link>https://medium.com/kdab-blog/delivering-software-updates-the-last-mile-of-product-development-30f8e3a7eac9?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/30f8e3a7eac9</guid>
            <category><![CDATA[software-update]]></category>
            <category><![CDATA[embedded-systems]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Thu, 27 Nov 2025 10:00:29 GMT</pubDate>
            <atom:updated>2025-11-27T10:00:29.292Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Nathan Collins, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><p>Shipping your product to customers is where some real challenges in software development begin. Once it lands in customers’ hands, delivering the expected and necessary software updates can be a complicated task.</p><p>Here’s a breakdown of some <strong>key considerations</strong> to keep your product, and your customers, running smoothly, even after your product has shipped.</p><h3>Getting updates to users</h3><p>The way you handle software updates depends on both your device and your users. Some products rely on users to manually update the device firmware. This approach puts more work on the end user but can be an acceptable simple first step if your user base is technically savvy. It eliminates some complexity to have your customers download the firmware directly from your website and install it themselves via USB, onboard storage, or even through a desktop app.</p><p>If you go this route, however, you’ll want to think about implementing safety measures. Corrupt firmware files or interruptions in the update process can brick the device. Consider using dual A/B firmware loads to ensure there’s always a working version available if the update fails.</p><p>The alternative to manually delivered updates is over-the-air (OTA) installation. This has a lot of advantages, from hands-off operation to remote updates. They can also be made more resilient to interruptions since the software can validate the image is on the system before installing it. The biggest downside is the complexity of programming an OTA solution, as well as mandating that the device has access to the internet (although it’s increasingly rare that things don’t).</p><h3>To auto-update or not?</h3><p>Automatic updates sound like a great idea — customers always get the latest features and security patches without having to lift a finger. But it’s important to weigh the pros and cons. Auto-updates can force your device to go offline for maintenance, which might annoy users if it happens at the wrong time. Imagine a smart keyless lock performing an update while someone is locked out of their home or a medical device updating itself during a critical procedure.</p><p>For devices that are constantly in use or have critical uptime requirements, it’s worth considering an opt-in model for updates. Even if auto-updates are the default, make sure the feature can be easily turned off or postponed by the user.</p><h3>Keeping data intact</h3><p>Updates should never wipe out user data. It’s one thing to install fresh code, but if configuration files or user data are lost in the process, you’ve got a problem. To avoid this, you need to manage your file versions carefully and develop a process for migrating data forward in a way that’s backwards-compatible if necessary.</p><p>If your product allows rolling back to earlier versions of firmware, ensure that data migration works both ways. For configuration files, text formats like JSON can be a lifesaver. If you need to roll back, any keys added by newer versions can be ignored. When updating, any missing keys mean that you need to provide a default value. Plus JSON files are human-readable, which is especially useful if you need to debug configuration files directly.</p><h3>Preparing your software for the world</h3><p>When it’s time to release an update, you’ll need a robust process in place. First things first: every update needs a unique version number. This is essential for tracking issues and ensuring that customers are using the correct version of the software.</p><p>To streamline the process, compress your firmware files to save space during download and storage. And don’t forget security: all your update files should be digitally signed to prevent tampering or corruption. Maintaining a well-organized in-house library of all releases, along with tagging customer-facing versions in your source control, will make life easier when you need to dig up old binaries for testing or debugging.</p><p>Even if your product uses containers like <a href="https://www.docker.com/">Docker</a>, the same principles apply. How will customers get the updates? How will the integrity of the files be verified? And, just as important, how can you ensure that the process of getting these files into your device is bulletproof?</p><h3>Testing before shipping</h3><p>Quality assurance is crucial, and there’s no substitute for testing the entire product before it hits customer hands. Unit testing by developers is important, but you need to go beyond that. Whether it’s an in-house team or outsourced specialists, someone needs to test the fully integrated product.</p><p>Once your product is in the field, you’ll also need a plan to recover from potential bugs in the update process. Can your customer reset the device to factory defaults and restore it from a stable firmware version? If so, make sure that reset process covers everything, from the obvious to the less visible, like persistent hardware or file system states that users might not even know exist.</p><h3>Planning for future updates</h3><p>Before your product is in the field, you’ll need to decide how often you want to push bug fixes and security patches. Will updates roll out on a regular schedule, or will you wait until a critical mass of fixes is ready? Some customers may even require their own private branches for high-priority fixes, depending on their contracts or support agreements.</p><p>It’s worth discussing these considerations with your team upfront so that everyone’s aligned on the update strategy. This can help avoid last-minute scrambles and keep your customers happy in the long run.</p><h3>Final thoughts</h3><p>Shipping a product isn’t just about getting it into the customer’s hands. It’s about ensuring that, once it’s there, it can continue to evolve, improve, and remain secure. Whether through manual updates or automated processes, thoughtful planning and thorough testing will make sure your product stays at its best long after it’s left your warehouse.</p><p>By keeping these best practices in mind, you can ensure that your embedded Linux device not only works today but will continue to function — and improve — well into the future.</p><p>For more on related topics, including important up-front decisions that need to be made at the outset of a project, explore our whitepaper, <a href="https://www.kdab.com/embedded-linux-framing-the-development-process/">Designing Your First Embedded Linux Device: Framing the Development Process</a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=30f8e3a7eac9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/delivering-software-updates-the-last-mile-of-product-development-30f8e3a7eac9">Delivering Software Updates: The Last Mile of Product Development</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Integration to Deployment: A CI/CD Primer]]></title>
            <link>https://medium.com/kdab-blog/from-integration-to-deployment-a-ci-cd-primer-0e082aaea895?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/0e082aaea895</guid>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Tue, 25 Nov 2025 09:49:29 GMT</pubDate>
            <atom:updated>2025-11-25T09:49:29.253Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Jan Marker, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><p>Adopting proven practices like continuous integration (CI) and continuous deployment (CD) is a key part of modern software design. These methodologies enhance software quality and team productivity while shortening development cycles — what’s not to love? Arguably, it’s the short-term pain of getting these systems installed and configured for your environment and the long-term pain of maintaining them. But you’ve come to the right place; this blog gives you some practical tips in getting your CI/CD system spun up that avoid common pitfalls and maximize long-term benefits.</p><p>Not sure you want to take the plunge? Here’s why we recommend CI/CD practices.</p><h3>Advantages of CI/CD</h3><p>CI/CD practices have several advantages that that can transform your development process. Here are some key reasons why implementing them is a smart move:</p><ul><li>Comprehensive and efficient testing: CI runs all relevant tests, not just those immediately impacted by recent changes, so it uncovers potential hidden issues. This allows developers to focus on local testing for quick checks but rely on CI for thorough, asynchronous testing that can run on separate systems in the background.</li><li>Automated code reviews: A CI system acts as an additional, automated code reviewer. By building and testing code across all platforms, it can catch issues that might be missed in peer reviews, all without consuming valuable engineering resources. Plus, it allows you to easily incorporate tools for static analysis, linting, and sanitizing to enhance code quality and stability.</li><li>Consistent code style: CI enforces consistent code formatting across all source files, eliminating style discrepancies that can affect readability. This standardization is crucial for large, distributed teams and helps avoid time-consuming (and unproductive) debates during peer reviews.</li><li>No testing needed: It might seem like you need a fully developed set of unit tests to take advantage of CI. You don’t. You can still get a lot of value from cross-compiling on multiple platforms, doing static analysis, and running format checkers. And once your CI system is in place, a small number of effective tests can validate critical items. Don’t be discouraged if you’ve just started creating your test suites — building up a few tests at a time as you find and fix bugs still offers a lot of benefit.</li></ul><h3>Essential components for effective CI/CD setup</h3><p>A successful CI/CD pipeline requires several key components to function smoothly. These elements work together to ensure that your development process is efficient, scalable, and reliable. Here are a few basic requirements to get started:</p><ul><li>Repository: Hopefully this is already a given, but if it’s not, you’ll need a shared repository for all developers.</li><li>CI servers: You need hardware to do the CI builds but it can be on-prem or in-cloud and we’ll discuss the pros and cons of both later. But if you’re considering an on-prem solution, you’ll want at least one dedicated CI server, and possibly multiple CI worker machines based on project size and complexity.</li><li>Team structure: You need people that know in detail how the CI system works. But whether it’s better to have a dedicated specialist or to distribute the necessary CI knowledge and responsibilities throughout the team probably depends on which CI tools you choose. In either case, you probably want to have 2 or 3 people who are part-time focused on CI tasks who can help other team members troubleshoot and act as backup if your dedicated person is unavailable.</li><li>Toolchain: Cloud-based tools like GitHub Actions provide easy, pre-configured setups. But a cloud system isn’t always the best idea. For an on-prem system like Jenkins or Buildbot, expect to invest time in server setup, software installation, worker machine configuration, and ongoing OS maintenance. You also want automation tools like Chef or Ansible to allow systems to run unattended so that the CI/CD system can reset and reboot them automatically.</li></ul><h3>Cloud versus on-premises</h3><p>Deciding whether to host your CI/CD system in the cloud or on-premises is an important decision that impacts cost, scalability, and security. Each option has its advantages and disadvantages, depending on your specific needs and constraints. Here are some key considerations to help guide your choice:</p><p>Cloud pros</p><ul><li>Simplicity and quick setup: Cloud systems are ready to use without hardware acquisition, installation, or configuration.</li><li>Scalability: Easy scalability is an advantage of cloud-based systems, allowing for quick adjustments to resources as development needs change.</li><li>Integration: Cloud hosted CI/CD systems integrate easily with cloud hosting platforms like GitHub and GitLab, especially when you leverage built-in automation tools like GitHub Actions.</li></ul><p>Cloud cons</p><ul><li>Cost: Continuous operation can lead to significant cloud processing and storage fees, especially with frequent multiplatform builds.</li><li>Data privacy and security: Regulations such as GDPR or specific industry requirements may restrict cloud usage, necessitating on-prem solutions for enhanced control over data security and compliance.</li></ul><p>On-prem pros</p><ul><li>Cost control: On-prem can be more cost-effective in the long run, especially if large-scale CI operations are needed frequently.</li><li>Data security: On-prem provides control over data privacy and security, which can be essential for regulation</li></ul><p>On-prem cons</p><ul><li>Maintenance: On-prem systems need to be maintained, repaired, and upgraded, requiring ongoing engineering or IT resources.</li><li>Spin-up: While a cloud system is available instantly, an on-prem equivalent needs to be built and configured, which can take a significant amount of time, especially if it’s the company’s first.</li></ul><h3>Optimizing CI builds</h3><p>CI is only effective when developers use it. And if your CI system is too slow to deliver results, developers will tend to ignore it. How do you achieve a balance of speed <em>and</em> thoroughness?</p><p>Essentially, you want to implement two main CI builds — one that allows a quick per-commit build and one that builds nightly. The quick build is run throughout the day as developers make commits and it should use pre-built dependencies and caches for optimal speed. The nightly build executes a full build from scratch every night with all static checks and installers.</p><p>If developers are in the same time zone, the two builds can be run on the same machine — otherwise, you’ll want a dedicated machine for nightly builds. You may also consider a third CI build for packaging. This can be used for teams that share external updates, such as customer testing or third-party validation services. This build is somewhere between the quick and nightly builds — it can rely on bits of the quick build (including packaging if it’s fast enough), while benefitting from the deeper coverage of the nightly build.</p><h3>Ensuring reliability in your CI workflow</h3><p>In a CI workflow, using developer tools like GitHub, Gerrit, and Bitbucket, CI-gated check-ins are essential. These tools ensure that all changes are validated by the CI system, confirming that commits pass necessary compiler and unit tests before merging into the main branch. Making CI gating optional can lead to the dangerous practice of skipping or bypassing CI builds, which significantly reducing a system’s value.</p><p>That’s why your CI system needs to be rock solid. If it frequently experiences issues — like excessive build times due to network problems — developers might be tempted to bypass CI checks altogether. Continuous maintenance is essential to ensure CI systems function effectively as a non-negotiable part of the development workflow.</p><h3>Where to go next?</h3><p>This short blog just scratches the surface on CI/CD. If you’re looking for more information and details on some of the most commonly used CI/CD tools — and when you might be better off choosing one over the other — check out our <a href="https://www.kdab.com/best-practices-ci-and-cd/">CI/CD best practice guide</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0e082aaea895" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/from-integration-to-deployment-a-ci-cd-primer-0e082aaea895">From Integration to Deployment: A CI/CD Primer</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CLI++: Upgrade Your Command Line]]></title>
            <link>https://medium.com/kdab-blog/cli-upgrade-your-command-line-6be6615a0b43?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/6be6615a0b43</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[cpp]]></category>
            <category><![CDATA[software-tools]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Mon, 24 Nov 2025 08:17:43 GMT</pubDate>
            <atom:updated>2025-11-24T08:17:43.813Z</atom:updated>
            <content:encoded><![CDATA[<h4>With a New Generation of Everyday Tools</h4><p><em>by Leon Matthes and Matt Aber, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*5Po7Ru-4Z4KFO-8J.png" /></figure><p>In a recent email, Leon Matthes from KDAB highlighted some of his go-to command line tools for everyday use on Unix. His recommendations sparked a lively exchange among our colleagues, each sharing their own favorite utilities.</p><p>Many of these tools offer efficient alternatives to standard Unix programs, speeding up the workflow or otherwise enriching the development experience.</p><p>This article aims to serve as a resource for the wider community, encouraging others to explore these tools and upgrade their command line setup for improved productivity.</p><p>The following common Unix tools are listed in this document, with their alternatives specified in their respective sections:</p><ul><li>cd</li><li>ls</li><li>man</li><li>find</li><li>grep</li><li>cat</li><li>diff / git diff</li></ul><p>Additionally, there are some tools here that do not replace common programs, but are extremely useful when working in a shell environment. These are broken into two broad categories:</p><ul><li>prompts &amp; shells</li><li>misc.</li></ul><p>Many of these tools can be considered examples of the “rewrite it in Rust” approach (RIIR), which in this document will be denoted with Ferris the crab: 🦀</p><p>Finally, there is a bonus Rust crate listed at the end that is helpful for writing CLI programs of your own.</p><p>Note: for scripting, stick with standard Unix tools, as they’re more widely distributed and will work in most other Unix environments. The tools in this article are meant to improve quality of life working within your own shell environment on a daily basis.</p><h3>cd</h3><h4><a href="https://github.com/wting/autojump">autojump</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/98/0*mNFsG05YiKglmHmL.png" /></figure><p><a href="https://github.com/wting/autojump">autojump</a> is an alternative to cd, invoked with the command j. It jumps to what it determines to be the best fit for whatever directory name you give it. For example, j cxx-qt will change directory to the cxx-qt directory, even if it’s nested several levels deep from the current working directory.</p><p>If autojump doesn’t prioritize a directory correctly, its priority weight can be increased or decreased. j -i XXX will increase the priority weight of the current working directory by XXX, while j -d XXX will do the opposite and decrease the weight accordingly.</p><p>autojump also supports opening a file browser at a matched location rather than cd&#39;ing into it, by invoking jo rather than j.</p><p>Unfortunately, autojump has not been in active development for the past two years, and can break in some environments. The next tool, zoxide, will be more reliable if autojump breaks for you.</p><h4><a href="https://github.com/ajeetdsouza/zoxide">zoxide 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*kYSwQUYpk2MtHeTx.png" /></figure><p><a href="https://github.com/ajeetdsouza/zoxide">zoxide</a> is a similar program to autojump, actually drawing inspiration from it. Invoked with the command z, it remembers paths that have been visited in the past and matches the best fitting directory. The tool also supports interactive selection of the correct match via the zi command, which uses the tool fzf for fuzzy finding. fzf is listed later in this article.</p><p>zoxide can also be used the same way as plain cd, and can target absolute and relative paths. It can be configured to be invoked from the j and ji commands, or even cd to fully replace the cdcommand.</p><p>Finally, it is implemented in Rust, resulting in better performance than autojump.</p><h3>ls</h3><h4><a href="https://github.com/lsd-rs/lsd">lsd 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*eHQIvW-Br89OIVoq.png" /></figure><p><a href="https://github.com/lsd-rs/lsd">lsd</a> is a more feature-rich version of ls, including colors, icons, tree-view, additional formatting, and more. It is also highly configurable through a yaml config file.</p><h4><a href="https://github.com/eza-community/eza">eza 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*pfI-GtgxXSf4ZM4J.png" /></figure><p><a href="https://github.com/eza-community/eza">eza</a> is a small, fast ls rewrite that knows about symlinks, extended attributes, and git, and has support for hyperlinks. Similar to lsd, it supports colors and icons, which are configurable with a yaml config file.</p><h3>man</h3><h4><a href="https://github.com/tldr-pages/tldr">tldr</a></h4><p><em>This project has several clients written in different languages. The most mature client is an npm package.</em></p><p><a href="https://github.com/tldr-pages/tldr">tldr</a> is an alternative to using man for most common use cases. It is a collection of pages that provides brief descriptions and usage examples for loads of programs, standard Unix and otherwise. It’s much faster if you don’t need to read about every option in detail in order to find the correct way to invoke a command or select the proper option quickly.</p><p>For example, if the man page for tar is too long to read when looking for the right option to do something, simply running tldr tar will show a list of examples that will usually have the options you need.</p><h4><a href="https://github.com/chubin/cheat.sh">cheat.sh / </a><a href="https://github.com/chubin/cheat.sh">cht.sh</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/98/0*-KH5NVlP7JmvWPcP.png" /></figure><p><a href="https://github.com/chubin/cheat.sh">cheat.sh</a> and its associated shellscript cht.sh, provide access to a superpowered aggregation of cheatsheets for programming languages and CLI tools, including tldr pages and other sources.</p><p>It has a great system for querying cheatsheets, and you don’t even need to install a tool (though you can), as the query response can be retrieved by curl. It’s also usable inside editors, for easy referencing while programming.</p><p>The cheatsheet sources also include StackOverflow answers, which allow for flexible usage like so:</p><pre>$ cht.sh js parse json</pre><p>A nice way to use cheat.sh without installing anything is to put the following function in your .bashrc or .zshrc:</p><pre>function cheat() {<br>    if ! curl -s &quot;cheat.sh/${*//\ /+}&quot;; then<br>        echo &quot;Failed to get the cheatsheet&quot; &gt;&amp;2<br>        return 1<br>    fi<br>}</pre><p>Then you can use it like</p><pre>$ cheat tar</pre><p>or</p><pre>$ cheat cpp number to string</pre><h3>find</h3><h4><a href="https://github.com/sharkdp/fd">fd 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*hIpwIuczjl8OrsyW.png" /></figure><p><a href="https://github.com/sharkdp/fd">fd</a> is an alternative for many use cases of find.</p><p>While it is not as powerful as find, it is designed to be faster and more user-friendly for the majority of find&#39;s use cases.</p><p>No more find -name .rs -- just use fd .rs and you’re good to go.</p><p>fd is super fast and will search through your entire filesystem in a matter of seconds (Leon recorded 1.4s on his machine with 177GB of files).</p><p>By default, fd will not include any hidden files that are ignored by .gitignore, which is very useful as it doesn’t give you any random junk in your build directory and speeds up the search even further.</p><p>Use --hidden and --no-ignore (or -H and -I, respectively) to search for everything.</p><h4><a href="https://github.com/junegunn/fzf">fzf</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/63/0*TVw-RVXdpn3EKvNY.png" /></figure><p><a href="https://github.com/junegunn/fzf">fzf</a> is a fuzzy-finder that can match files, command history, processes, hostnames, bookmarks, git commits, and more, with interactive selection. It’s also usable as a vim or neovim plugin.</p><h3>grep</h3><h4><a href="https://github.com/BurntSushi/ripgrep">ripgrep 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*GdlyLq0kC0Fg8DoC.png" /></figure><p><a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> (rg as a command) provides an incredibly fast grep alternative with a simpler interface.</p><p>Did you ever have to wait for grep to finish searching a directory or had to look up the right options for specific usage? Well, no more! Ripgrep will search the contents of large build directories within milliseconds.</p><p>Just running rg Q_PROPERTY will search through the entire current directory if you don’t pipe something into it, which is very convenient and the way it should have been in the first place. The output is also a lot friendlier and easier to read.</p><p>Like fd, use --hidden and --no-ignore to search in hidden and ignored directories/files.</p><h3>cat</h3><h4><a href="https://github.com/sharkdp/bat">bat 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*UG7LOQThF2msJ_CP.png" /></figure><p><a href="https://github.com/sharkdp/bat">bat</a>, written by the creator of fd, is a superpowered Rust rewrite of cat. It provides syntax highlighting, line numbers, support for displaying non-printable characters, support for showing multiple files at once, and pipes to less by default for large output. It can also be integrated with fzf, find, fd, ripgrep (rg), git show, git diff, man, and more. A tool you&#39;ll read about later in this document, delta, uses bat as a dependency to display diffs.</p><h3>diff / git diff</h3><h4><a href="https://github.com/so-fancy/diff-so-fancy">diff-so-fancy</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/77/0*xkaw6a8a_BHpaNks.png" /></figure><p><a href="https://github.com/so-fancy/diff-so-fancy">diff-so-fancy</a> is an alternative to vanilla git diff that improves readability, with better formatting, colors, and highlighting. Configure git to use diffsofancy as its pager, and git diff will provide the more readable diffsofancy output.</p><h4><a href="https://github.com/dandavison/delta">delta 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*Ccxw49fT1FsHh2Gh.png" /></figure><p><a href="https://github.com/dandavison/delta">delta</a> is an alternative to diffsofancy that’s written in Rust. It includes additional features, like line numbers, syntax highlighting, side-by-side, improved navigation between files in large diffs, hyperlinked commit hashes, improvements for display of git blame and merge conflicts, and the ability to syntax-highlight grep output. delta uses bat under the hood and, as such, they can share color themes.</p><h4>prompts &amp; shells</h4><h4><a href="https://github.com/starship/starship">starship 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*IyFC0JblHHay1i2x.png" /></figure><p><a href="https://github.com/starship/starship">starship</a> is a prompt written in Rust, which can be used with a number of popular shells including bash, zsh, fish, and even Windows shells like cmd and PowerShell.</p><p>The prompt is both stylish and informative, providing information like git branch, versioning, and more in the prompt itself. It also indicates the time length of command execution, whenever the run lasts for more than a few seconds — very helpful for gauging the duration of a build. This prompt should be utilized with a NerdFont for icon support.</p><p>Starship is also extremely configurable, so any details can be omitted or added; the look and feel of the prompt can be completely customized, etc.</p><h4>zsh prompts &amp; plugins</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/70/0*pTwPVG9zTKeA_uIv.png" /></figure><p>Some shell users argue that switching from bash is definitely worthwhile, with zsh being the favorite choice for many. It’s highly configurable and has a rich plugin ecosystem.</p><p><a href="https://ohmyz.sh/">oh-my-zsh</a> is a zsh framework that makes completions, searching in history, the prompt, etc., much nicer than the defaults. It can, of course, be tweaked much further from there. If you find oh-my-zsh to be a bit bloated, there are more lightweight alternatives such as ZimFW or Zinit.</p><p>Some zsh plugins can also serve as alternatives to previously mentioned tools. There are quite a few useful plugins listed here (they should work with any plugin manager): <a href="https://github.com/unixorn/awesome-zsh-plugins">https://github.com/unixorn/awesome-zsh-plugins</a>.</p><p>Some that stand out include:</p><ul><li><a href="https://github.com/jeffreytse/zsh-vi-mode">zsh-vi-mode</a></li><li>a proper Vim mode for the terminal, much better than the default one</li><li><a href="https://github.com/zdharma-continuum/fast-syntax-highlighting">fast-syntax-highlighting</a></li><li>nice while-you-type syntax highlighting in the shell</li><li><a href="https://github.com/urbainvaes/fzf-marks">fzf-marks</a></li><li>alternative workflow to zoxide and similar cd improvements</li><li>this can also be used with bash</li><li><a href="https://github.com/Tarrasch/zsh-autoenv">zsh-autoenv</a></li><li>customize environment variables by directory, similar to the tool direnv mentioned later</li></ul><p>Note: If you are interested in switching away from bash but don’t like zsh, consider trying fish.</p><h3>Update Systems</h3><h4><a href="https://github.com/topgrade-rs/topgrade">topgrade 🦀</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*mRiyD5s-t2klBF2r.png" /></figure><p><a href="https://github.com/topgrade-rs/topgrade">topgrade</a> is a tool which conveniently updates several package managers with one command.</p><p>Just run topgrade and every package manager under the sun will be updated, one after the other. This even includes flatpaks and updates to your systems package manager, very convenient.</p><h3>Misc.</h3><h4><a href="https://direnv.net/">direnv</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/63/0*rLO5aw9CmgL3UyqL.png" /></figure><p><a href="https://direnv.net/">direnv</a> is an extension for several common Unix shells, focused on environment management. More specifically, it is intended to unclutter .profile and export different environment variables depending on the current directory.</p><p>On each prompt, it checks for the existence of a .envrc file in the current directory, otherwise looking for .env, and loads or unloads environment variables accordingly. It’s a single executable and very fast.</p><p>direnv is also extensible with bash files in .config.</p><h4><a href="https://github.com/vimpostor/blobdrop">blobdrop</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/72/0*4vKY2dDysh_pBLG6.png" /></figure><p><a href="https://github.com/vimpostor/blobdrop">blobdrop</a> enables drag-n-drop of files from a terminal window, which can be very convenient when using the command line as a file browser. It was written by fellow kdabian <a href="https://github.com/vimpostor">Magnus Gross</a></p><h3>bonus!</h3><h4><a href="https://github.com/clap-rs/clap">clap</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/78/0*2gLo-ue1KKLJwhWD.png" /></figure><p>bonus is not quite a tool, but a Rust crate used frequently for writing CLI tools, called <a href="https://github.com/clap-rs/clap">clap</a>.</p><p><a href="https://github.com/clap-rs/clap">clap</a> is used in the codebases of a majority of the Rust tools in this document, as it’s incredibly useful. It’s the reason most Rust CLI tools have great UX.</p><p>Using the derive feature, you can simply mark up a struct of data you need from your command line arguments and clap will generate a beautiful and convenient CLI interface for you. Just take a look at the <a href="https://docs.rs/clap/latest/clap/_derive/_tutorial/chapter_0/index.html">derive documentation</a> to get an idea of what this looks like.</p><h3>Other Tools</h3><p>We hope these tools can improve your workflow, as they certainly make our lives on the command line far more enjoyable.</p><p>If you have any additions to this list, feel free to discuss them in the comments.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6be6615a0b43" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/cli-upgrade-your-command-line-6be6615a0b43">CLI++: Upgrade Your Command Line</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Model/View Drag and Drop in Qt — Part 3: Moving/copying items onto existing items]]></title>
            <link>https://medium.com/kdab-blog/model-view-drag-and-drop-in-qt-part-3-moving-copying-items-onto-existing-items-f173d56c7f62?source=rss-78eb9e34b73f------2</link>
            <guid isPermaLink="false">https://medium.com/p/f173d56c7f62</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[qt]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[cpp]]></category>
            <dc:creator><![CDATA[KDAB]]></dc:creator>
            <pubDate>Wed, 12 Nov 2025 07:38:07 GMT</pubDate>
            <atom:updated>2025-11-12T07:38:07.847Z</atom:updated>
            <content:encoded><![CDATA[<h3>Model/View Drag and Drop in Qt — Part 3: Moving/copying items onto existing items</h3><p><em>by David Faure, originally published on </em><a href="http://kdab.com/resources"><em>kdab.com/resources</em></a></p><p>In this third blog post of the Model/View Drag and Drop series (<a href="https://www.kdab.com/modelview-drag-and-drop-part-1/">part 1</a> and <a href="https://www.kdab.com/modelview-drag-and-drop-in-qt-part-2/">part 2</a>), the idea is to implement dropping onto items, rather than in between items. QListWidget and QTableWidget have out of the box support for replacing the value of existing items when doing that, but there aren’t many use cases for that. What is much more common is to associate a custom semantic to such a drop. For instance, the examples detailed below show email folders and their contents, and dropping an email onto another folder will move (or copy) the email into that folder.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*RsiQdD30FrloD6r_.png" /><figcaption>Step 1: Initial state, the email is in the inbox</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*gvX0tUYANjm3BXid.png" /><figcaption>Step 2: Dragging the email onto the Customers folder</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*8ud5OrY0x1tuVLJr.png" /><figcaption>Step 3: Dropping the email</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*dLROqL72GPWnZtJE.png" /><figcaption>Step 4: The email is now in the customers folder</figcaption></figure><h3>With Model/View separation</h3><p>Example code can be found <a href="https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/model-view/drop-onto-items-with-model-view.cpp">here</a> for flat models and <a href="https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/treemodel/drop-onto-items-with-treemodel.cpp">here</a> for tree models.</p><h4>Setting up the view on the drag side</h4><p>☑ Call view-&gt;setDragDropMode(QAbstractItemView::DragOnly)<br>unless of course the same view should also support drops. In our example, only emails can be dragged, and only folders allow drops, so the drag and drop sides are distinct.</p><p>☑ Call view-&gt;setDragDropOverwriteMode(...)<br>true if moving should clear cells, false if moving should remove rows.<br>Note that the default is true for QTableView and false for QListView and QTreeView. In our example, we want to remove emails that have been moved elsewhere, so false is correct.</p><p>☑ Call view-&gt;setDefaultDropAction(Qt::MoveAction) so that the drag defaults to a move and not a copy, adjust as needed</p><h4>Setting up the model on the drag side</h4><p>To implement dragging items out of a model, you need to implement the following — this is very similar to the section of the same name in the previous blog post, obviously:</p><pre>class EmailsModel : public QAbstractTableModel<br>{<br>    ~~~<br>    Qt::ItemFlags flags(const QModelIndex &amp;index) const override<br>    {<br>        if (!index.isValid())<br>            return {};<br>        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;<br>    }<br><br>    // the default is &quot;copy only&quot;, change it<br>    Qt::DropActions supportedDragActions() const override { return Qt::MoveAction | Qt::CopyAction; }<br><br>    QMimeData *mimeData(const QModelIndexList &amp;indexes) const override;<br><br>    bool removeRows(int position, int rows, const QModelIndex &amp;parent) override;</pre><p>☑ Reimplement flags() to add Qt::ItemIsDragEnabled in the case of a valid index</p><p>☑ Reimplement supportedDragActions() to return Qt::MoveAction | Qt::CopyAction or whichever you want to support (the default is CopyAction only).</p><p>☑ Reimplement mimeData() to serialize the complete data for the dragged items. If the views are always in the same process, you can get away with serializing only node pointers (if you have that) and application PID (to refuse dropping onto another process). See the previous part of this blog series for more details.</p><p>☑ Reimplement removeRows(), it will be called after a successful drop with MoveAction. An example implementation looks like this:</p><pre>bool EmailsModel::removeRows(int position, int rows, const QModelIndex &amp;parent)<br>{<br>    beginRemoveRows(parent, position, position + rows - 1);<br>    for (int row = 0; row &lt; rows; ++row) {<br>        m_emailFolder-&gt;emails.removeAt(position);<br>    }<br>    endRemoveRows();<br>    return true;<br>}</pre><h4>Setting up the view on the drop side</h4><p>☑ Call view-&gt;setDragDropMode(QAbstractItemView::DropOnly) unless of course it supports dragging too. In our example, we can drop onto email folders but we cannot reorganize the folders, so DropOnly is correct.</p><h4>Setting up the model on the drop side</h4><p>To implement dropping items into a model’s existing items, you need to do the following:</p><pre>class FoldersModel : public QAbstractTableModel<br>{<br>    ~~~    <br>    Qt::ItemFlags flags(const QModelIndex &amp;index) const override<br>    {<br>        CHECK_flags(index);<br>        if (!index.isValid())<br>            return {}; // do not allow dropping between items<br>        if (index.column() &gt; 0)<br>            return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // don&#39;t drop on other columns<br>        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;<br>    }<br><br>    // the default is &quot;copy only&quot;, change it<br>    Qt::DropActions supportedDropActions() const override { return Qt::MoveAction | Qt::CopyAction; }<br>  <br>    QStringList mimeTypes() const override { return {QString::fromLatin1(s_emailsMimeType)}; }<br>  <br>    bool dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &amp;parent) override;<br>};</pre><p>☑ Reimplement flags()<br>For a valid index (and only in that case), add Qt::ItemIsDropEnabled. As you can see, you can also restrict drops to column 0, which can be more sensible when using QTreeView (the user should drop onto the folder name, not onto the folder size).</p><p>☑ Reimplement supportedDropActions() to return Qt::MoveAction | Qt::CopyAction or whichever you want to support (the default is CopyAction only).</p><p>☑ Reimplement mimeTypes() - the list should include the MIME type used by the drag model.</p><p>☑ Reimplement dropMimeData()<br>to deserialize the data and handle the drop.<br>This could mean calling setData() to replace item contents, or anything else that should happen on a drop: in the email example, this is where we copy or move the email into the destination folder. Once you&#39;re done, return true, so that the drag side then deletes the dragged rows by calling removeRows() on its model.</p><pre>bool FoldersModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &amp;parent)<br>{<br>    ~~~  // safety checks, see full example code<br><br>    EmailFolder *destFolder = folderForIndex(parent);<br><br>    const QByteArray encodedData = mimeData-&gt;data(s_emailsMimeType);<br>    QDataStream stream(encodedData);<br>    ~~~ // code to detect and reject dropping onto the folder currently holding those emails<br><br>    while (!stream.atEnd()) {<br>        QString email;<br>        stream &gt;&gt; email;<br>        destFolder-&gt;emails.append(email);<br>    }<br>    emit dataChanged(parent, parent); // update count<br><br>    return true; // let the view handle deletion on the source side by calling removeRows there<br>}</pre><h3>Using item widgets</h3><p>Example code:</p><ul><li><a href="https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qlistwidget/drop-onto-qlistwidgetitems.cpp">QListWidget</a></li><li><a href="https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qtablewidget/drop-onto-qtablewidgetitems.cpp">QTableWidget</a></li><li><a href="https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qtreewidget/drop-onto-qtreewidgetitems.cpp">QTreeWidget</a></li></ul><h4>On the “drag” side</h4><p>☑ Call widget-&gt;setDragDropMode(QAbstractItemView::DragOnly) or DragDrop if it should support both</p><p>☑ Call widget-&gt;setDefaultDropAction(Qt::MoveAction) so that the drag defaults to a move and not a copy, adjust as needed</p><p>☑ Reimplement Widget::mimeData() to serialize the complete data for the dragged items. If the views are always in the same process, you can get away with serializing only item pointers and application PID (to refuse dropping onto another process). In our email folders example we also serialize the pointer to the source folder (where the emails come from) so that we can detect dropping onto the same folder (which should do nothing).</p><p>To serialize pointers in QDataStream, cast them to quintptr, see the <a href="https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qlistwidget/drop-onto-qlistwidgetitems.cpp#L58">example code</a> for details.</p><h4>On the “drop” side</h4><p>☑ Call widget-&gt;setDragDropMode(QAbstractItemView::DropOnly) or DragDrop if it should support both</p><p>☑ Call widget-&gt;setDragDropOverwriteMode(true) for a minor improvement: no forbidden cursor when moving the drag between folders. Instead Qt only computes drop positions which are onto items, as we want here.</p><p>☑ Reimplement Widget::mimeTypes() and return the same name as the one used on the drag side&#39;s mimeData</p><p>☑ Reimplement Widget::dropMimeData() (note that the signature is different between QListWidget, QTableWidget and QTreeWidget) This is where you deserialize the data and handle the drop. In the email example, this is where we copy or move the email into the destination folder.</p><p>Make sure to do all of the following:</p><ul><li>any necessary behind the scenes work (in our case, moving the actual email)</li><li>updating the UI (creating or deleting items as needed)</li></ul><p>This is a case where proper model/view separation is actually much simpler.</p><h3>Improvements to Qt</h3><p>While writing and testing these code examples, I improved the following things in Qt, in addition to those listed in the previous blog posts:</p><ul><li><a href="https://bugreports.qt.io/browse/QTBUG-2553">QTBUG-2553</a> QTreeView with setAutoExpandDelay() collapses items while dragging over it, fixed in Qt 6.8.1</li></ul><h3>Conclusion</h3><p>I hope you enjoyed this blog post series and learned a few things.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f173d56c7f62" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kdab-blog/model-view-drag-and-drop-in-qt-part-3-moving-copying-items-onto-existing-items-f173d56c7f62">Model/View Drag and Drop in Qt — Part 3: Moving/copying items onto existing items</a> was originally published in <a href="https://medium.com/kdab-blog">KDAB Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>