Native look and feel

We know that many Qt users want controls styled with a native look-and-feel. But offering that on platforms with no public styling API, is hard. A classic approach is to take snapshots of the native controls, tweak them, and use them as foreground or background in our own controls. Which is somewhat OK for static appearances. But when animations and transitions are involved, static pixmaps will only take you half the way. And since an OS can change style from one update to the next, taking snapshots runtime is risky. Using pre-grabbed snapshots is also something we don’t do because of legal considerations.

Since December last year, the controls team has been researching a bit small scale on a new project to offer controls with true native look-and-feel. The aim is to do the alternative to the above standing, and explore how feasible it would be to wrap actual native controls into a cross platform Qt API. Such ideas is nothing new of course, and have been discussed many times, at least internally. One of the problems is that different platforms can vary quite much API-wise, especially for more complex controls like tab-, and split views, with some having a rich API, while others are more limited. And unifying that into a common useable Qt API is a challenge.

Currently we have prototyped a set of controls, together with a plugin-based framework and a few backends for testing (uikit, appkit, android). The API is small and strict so that it can be realized on all supported (and future) platforms. As such, the controls become rather black box, since we cannot make assumptions on how they are implemented by the backends. Still, there will be times when you need to detail how your application should work on a specific platform. For that reason we plan to open up, and factor out, the various backends into separate libraries that can be accessed directly. Those libraries will wrap parts of the native APIs more closely, and offer building blocks specific to the platform, or controls with a broader set of functions and properties. They will be more open than the common controls, giving access to the native controls they wrap, to let you dig into to native development whenever you need to fill gaps that falls outside our own scope. In the end, it should be easy and straightforward to mix cross-platform controls, platform-specific controls, and native code in your application.

I’ve mentioned “APIs” a couple of times. This of course includes QML. But we also plan to offer direct access to the underlying C++ libraries, making it easy to extend the API, and write applications using C++ only. That way you can choose whether you want to use QML, C++, or a combination.

Here is a snippet showing how things look so far. The app mixes a couple of cross-platform controls (common controls) together with a control directly from the AppKit backend.

import QtQml 2.0
import Qt.CommonControls 1.0 as Common
import Qt.AppKitControls 1.0 as AppKit

Window { id: window visible: true

Common.Button { id: button x: 20 y: 20 text: "Click me" onClicked: text ="You clicked me!" }

Common.TextField { id: textField placeholderText: "TextField" x: 20 y: button.bottom + 8 }

AppKit.SearchField { placeholderText: "Search" x: 20 y: textField.bottom + 8 } }

The next snippet shows the same app, but this time in C++

int main(int argc, char *argv[])
    QGuiApplication app(argc, argv);
    QCommonWindow window;

QCommonButton commonButton(QStringLiteral("Click me"), &window); commonButton.move(20, 20); QObject::connect(&commonButton, &QCommonButton::clicked, [&commonButton](){ commonButton.setText(QStringLiteral("Clicked!")); });

QCommonTextField textField(&window); textField.setPlaceholderText(QStringLiteral("TextField")); textField.move(20, commonButton.bottom() + 8);

#if defined(Q_OS_MACOS) QAppKitSearchField searchField; searchField.setParent(&window); searchField.move(20, textField.bottom() + 8); NSSearchField *nsSearchField = searchField.nsSearchFieldHandle(); nsSearchField.placeholderString = @"Search..."; #endif

window.showFullScreen(); return app.exec(); }

As mentioned, we plan to keep the API rather strict. The common controls will only contain the basics, with few, or no possibilities for tweaking the style, catching internal events, or overriding default behaviour. You basically get native controls with native look, feel and behaviour. If you, on the other hand, need custom styling, or your own custom controls, Qt offer solutions like QtQuickControls2, or Widgets, for that already. Our current thinking is that this module will be something for Qt6.

Blog Topics: