Planet KDE logo

Welcome to Planet KDE

This is a feed aggregator that collects what the contributors to the KDE community are writing on their respective blogs
To have your blog added to this aggregator, please read the instructions
The Re-flow One day I was looking at the MR (Merge Request) and saw Tomaz Canabrava’s sketch showing the terminal re-flow lines while it shrinks, and I just thought it would be great to have it fully working.

Konqi hunting Plasma 5.21 bugs

I am happy to announce that we are going to hold the second Plasma Beta Review Day on the 28th January from 9.00 CET to at least 20.00 CET. We will meet in a Big Blue Button room and you can join via audio, video or text.

Everybody is encouraged to join, regardless of whether you are a user testing the new release, a bug triager, or a developer. We want to hear your impressions about the beta release and want to focus on any regressions or bugs compared to the last release.

Check out the wiki page for more details.

See you on Thursday!

Featured image of post Pikasso, a simple drawing application in QtQuick with Rust

Following my last blog post about using Rust and Lyon to create custom shapes. I’m happy to announce the creation of Pikasso, a very simple drawing program intended to be used on Plasma Mobile.

Pikasso
Pikasso

Pikasso is very basic and only supports drawing with the mouse/finger and adding rectangles and circles to the scene. An undo feature is also available as well as the possibility to export your beautiful artworks to SVGs. As you can see, Pikasso is not intended to be replacements for Krita. If you want a powerful drawing application just use Krita, it’s awesome. The scope of Pikasso is more similar to Kolourpaint or Paint.exe and intended for children to play a bit with it on Plasma Mobile.

Behind the scene

Behind the scene, Pikasso uses Rust and Lyon to do all the drawing. The entire drawing area is just one QQuickItem with many QSGNodes. This makes Pikasso hardware accelerated.

When drawing, Pikasso creates DrawEvents for storing the drawing. The DrawEvent contains a QPainterPath and a few metadata (line width, pen color and if the QPainterPath should be filled or not. This is inspired by the QtWidgets Scribble example and the Digikam sketch widgets. Using a QPainterPath replaces the usage of the std::variant from my last blog post.

Thanks to Milian on IRC for giving me the hint about just using a QPainterPath. This has the advantage of integrating better with the rest of the Qt ecosystem sine QPainterPath is used to store reusable instructions for QPainter but also supports more features like text rendering. Another advantage of using a QPainterPath is that we get an SVG export feature almost for free with a QSvgGenerator. See the MR from Jonah about this feature..

QPainterPath can be natively used with QPainter, but it can also be in a very simple way transformed to a LyonBuilder object.

rust::Box<LyonBuilder> painterPathToBuilder(const QPainterPath &path)
{
    auto lyonBuilder = new_builder();
    for (int i = 0; i < path.elementCount(); i++) {
        const auto element = path.elementAt(i);
        if (element.isLineTo()) {
            lyonBuilder->line_to(LyonPoint { static_cast<float>(element.x), static_cast<float>(element.y) });
        } else if (element.isMoveTo()) {
            lyonBuilder->move_to(LyonPoint { static_cast<float>(element.x), static_cast<float>(element.y) });
        } else if (element.type ==  QPainterPath::ElementType::CurveToElement) {
            // Cubic is encoded with ctrl1 -> CurveToElement, ctrl2 -> CurveToDataElement and to -> CurveToDataElement
            Q_ASSERT(i + 2 < path.elementCount() && "CurveToElement doesn't have data");
            const auto ctrl1 = path.elementAt(i);
            const auto ctrl2 = path.elementAt(i + 1);
            const auto to = path.elementAt(i + 2);
            lyonBuilder->cubic_bezier_to(
                LyonPoint { static_cast<float>(ctrl1.x), static_cast<float>(ctrl1.y) },
                LyonPoint { static_cast<float>(ctrl2.x), static_cast<float>(ctrl2.y) },
                LyonPoint { static_cast<float>(to.x), static_cast<float>(to.y) }
            );
            i += 2; // we analysed tree elements instead of just one
        }
    }

    return lyonBuilder;
}

And once we have a LyonBuilder object we can render our geometries to the screen. This part didn’t change since the last post.

auto node = new QSGGeometryNode;
auto builder = painterPathToBuilder(drawEvent.path);
LyonGeometry lyonGeometry;
if (!drawEvent.fill) {
    lyonGeometry = build_stroke(std::move(builder), drawEvent.penWidth);
} else {
    lyonGeometry = build_fill(std::move(builder));
}
QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
        lyonGeometry.vertices.size(), lyonGeometry.indices.size());

QSGGeometry::Point2D *points = geometry->vertexDataAsPoint2D();
std::size_t i = 0;
for (const auto &vertice: lyonGeometry.vertices) {
    points[i].set(vertice.x, vertice.y);
    i++;
}

quint16* indices = geometry->indexDataAsUShort();
i = 0;
for (const auto indice: lyonGeometry.indices) {
    indices[i] = indice;
    i++;
}

Pikasso, needs to add new nodes for each new DrawEvent but only update the geometry of the last DrawEvent since it’s the only one who could have changed. This makes the painting experience very smooth.

For those interested, this is the code for the updatePaintNodeData method. It’s a bit more complex since Pikasso also handles undo events and needs to cleanup its removed QSGNodes.

Antialiasing

The last problem to solve for Pikasso was antialiasing since the figures on the screen looked pixelized. This was easy to solve by enabling Multisample Antialiasing on my QML DrawingArea. This can be done using the layer.samples property:

DrawingArea {
    anchors.fill: parent
    layer.enabled: true
    layer.samples: 4
}

Future goals

I think Pikasso is already good enough for its intended usage scope. That I might do in the future is to use a similar technique to add annotation supports to KQuickImageEditor, a library I developed for editing images before sending them in NeoChat. This will need a bit of thinking and a lot of refactoring in KQuickImageEditor first.

A lot of stuff happened for Kate and it’s foundation KTextEditor & KSyntaxHighlighting in January 2021 ;)

I will just briefly summarize what is going one feature wise.

Quick Open

Already two weeks ago I pointed out the improvements to our Quick Open dialog.

It not only got some nice visual refresh but a much better fuzzy matching algorithm.

The fuzzy matching algorithm is on its way to be upstream to KCoreAddons to be used by more parts of the KDE universe.

Praise to Waqar Ahmed for implementing this and pushing it to upstream. And thanks to Forrest Smith for allowing us to use his matching algorithm under LGPLv2+!

HUD style command palette

;=) I stole that naming from Nate’s This week in KDE: the Plasma 5.21 beta is here! post.

This dialog provides the same fuzzy matching we have in Quick Open but applies it to all registered actions of the application.

There is some menu items for it? It will find it.

Besides, it even is able to trigger sub menus.

As it works on the KActionCollection of the application without any other dependencies, this might be upstreamed later to e.g. KXMLGui to be usable for other KDE applications, too.

Thanks for this to Waqar Ahmed, too!

Search & Replace

The Search and Replace tool view provided by the corresponding plugin is a central feature of Kate.

I use it a lot myself, especially in combination with the projects plugin. Just type pgrep Kate in your Kate command line (F7) and you get all matches of that in your current active project. Replaced my usual grep -r or git grep inside the Kate terminal tool view years ago.

Both representation and architectural wise this now got some overhaul (and more to come).

We now use the same theme as inside the KTextEditor editor views. This means the matches will look exactly like the text in your editor, same font, same colors.

In addition, the plugin uses now a proper model-view separation and we will try to get multi-threaded search working, too. At the moment still one background thread does the disk search, this will be improved.

Thanks to Kåre Särs & Waqar Ahmed, for working on this.

Semantic Highlighting via LSP

Another nifty feature is the improved semantic highlighting support inside the LSP plugin. This now more properly tries to use the same theme colors as KTextEditor & KSyntaxHighlighting provide, too.

There is still room for improvements, like allowing themes to specify more colors for this. But at least as a start, it already works ok.

See below the a file inside the Kate project without semantic highlighting enabled:

And here with that feature on:

:=) Waqar worked on this, too, the LSP changes were reviewed by Mark Nauwelaerts.

Summary & Outlook

This is not even all that happened in 2021 up to now ;)

As you can see on our team page a lot of new people helped out in the scope of the last year. I hope to see more people showing up there as new contributors. It is a pleasure that Waqar Ahmed & Jan Paul Batrina now have full KDE developer accounts!

Especially Waqar came up with a lot of nifty ideas what could be fixed/improved/added and he did already do a lot of work to actually get these things done!

I actually wanted to write earlier about what cool new stuff is there, but had too much review requests to look after. Great! ;=) No I can read review request instead of light novels in the evening.

In my Kate is 20 years old! post from December 14, 2020 I added summaries of the merge requests we got since we are on our GitLab invent.kde.org instance:

Today, a bit over one month later, these numbers look like:

I would say that is a nice increase ;=)

Naturally, there are small and large merges, but still, KTextEditor’s requests doubled in a bit over one month.

Let’s hope this trend continues!

Thanks to all people that contribute, let’s rock!

Comments?

A matching thread for this can be found here on r/KDE.

Sunday

24 January, 2021

Discovering I never thought I could contribute with Open Source, or even imagined I could change my workspace, in my mind doing it was beyond my programming skills.

Saturday

23 January, 2021

The year 2020 was difficult in many ways, but it also was important for me: I joined KDE e.V. as a consultant in the role of Project Coordinator.

One of the main focuses of mine was supporting the KDE Goals initiative, which resulted in creating a formalized process.

As you might know (or read in the process), the KDE Goals are to be replaced roughly every two years.

This timebox was selected to balance keeping the Goals fresh, and letting the Goal Champions have enough time to work on the topics with the community.

Often, the goals have dedicated Sprints, where community members from all over the world meet up to work together – this not only greatly accelerates the work, but also lets the community create a stronger bond (and is a perfect opportunity to take group photos that the Promo team can use later 🙂 ).

As you might guess, the previous year was hardly an ideal one for those kind of events, and even though Virtual Sprints did happen, they can only be a substitute for the in-person ones.

So as the new year started, discussion about a potential extension of the current Goals period began. An extension would mean that instead of starting the process of selecting new Goals this year, we would do that in 2022 instead.

This has two main benefits:

  1. The current goals will get more time to finish the work that is already ongoing and was somewhat hindered by the pandemic. And if we’re lucky enough, perhaps by this time next year we could even think about having a in-person event.
  2. The new Goal Champions will start their reign in a (hopefully) more predictable world, with all of us having a better understanding if the vaccines will let us travel and meet again. This will help the Champions set the scope of the Goals.

Of course there are some considerations to be made. As mentioned before, the process assumes replacing Goals with new ones, so that they reflect the current focus of the community. Extending the Goals forever would resolve in a discrepancy between what the community wants, and what the KDE e.V. is promoting and helping with. However, I hope you agree with me that the current Goals (Wayland, Consistency, and KDE is All About the Apps) are still very much relevant.

The other thing is, Championing can be a demanding task, so our current Champions might feel exhausted with an extra year in focus. The good news is, after talking with Méven, Niccolo and Jonathan I can confirm that they are ready to extend their roles for an additional year. Additionally, I have confirmed with the KDE e.V. Board that they are OK with this extension as well.

So this brings me to the part, where I can officially announce, that the KDE Goals get an extra year! I hope this will give you more time to engage with the work being done as part of this initiative, and also more time to plan out your Goal proposals for 2022 🙂

PS. Speaking of engaging in ongoing work, there is a Wayland Virtual Sprint currently active!

Well folks, you finally have a chance to test out Plasma 5.21, in beta form! Please do install it and find all the bugs we missed. Bug reports have already started pouring in, and we’ll fix them as fast as we can in the next month. 🙂

But check out what else we did this week:

New Features

Kate now has a searchable HUD-style command palette that lets you trigger menu items with super speed! It’s activated using the Ctrl+Alt+I shortcut, and we’re investigating adding it to other KDE apps as well in the form of a re-usable framework component. (Waqar Ahmed, Kate 21.04):

Bugfixes & Performance Improvements

Spectacle is once again able to take rectangular region screenshots on triple-screen setups (Franz Baumgärtner, Spectacle 20.12.2)

Okular’s Open dialog once again defaults to “All files” in its file type filter when run on non-Plasma desktops (Albert Astals Cid, Okular 20.12.2)

Dolphin now correctly reports the number of files present on other disks not using standard Linux filesystems, such as NTFS (Ilia Kats, Dolphin 20.12.2)

The “Add Network Folder” action is once again visible in Dolphin for people using Frameworks 5.78 or later–albeit it now lives in the toolbar, rather than in the view (Norbert Preining, Dolphin 20.12.2)

When you turn off Konsole’s “Remember window size” feature, that now works again (me: Nate Graham, Konsole 20.12.2)

The System Settings Fonts page no longer shows blank previews for the anti-aliasing styles when using the proprietary Nvidia driver (Yaroslav Sidlovsky, Plasma 5.21)

Discover now handles the situation where you’re installing an add-on that requires you to select a specific file from among a set of options, just like the standalone “get new [thing]” dialog (Dan Leinir Turthra Jensen, Plasma 5.21)

Plasma’s support for the Pulse Connect Secure VPN type now works (David Redondo, Plasma 5.21)

Discover is now a bit faster to launch and uses less memory (still a lot, but less than before at least) (Aleix Pol Gonzalez, Plasma 5.21)

It’s now possible to unset global shortcuts set for activating Plasma applets (David Redondo, Plasma 5.21)

While the “Get new Plasma Widgets” window is open, attempting to open it again now re-focuses the existing one rather than opening a second one (Willyanto, Plasma 5.21)

Fixed an uncommon crash in System Settings’ Shortcuts page (David Redondo, Plasma 5.21)

When waking up a sleeping laptop that has a touchscreen, KWin no longer pretends that a finger is being held down on the touchscreen until you really do touch it at least once yourself (Xaver Hugl, Plasma 5.21)

In the Plasma Wayland session, windows no longer try to snap to OSDs and notifications (Vlad Zahorodnii, Plasma 5.21)

The System Tray’s back button now reverses itself correctly when using an RTL language (Jan Blackquill, Plasma 5.21)

Filenames in the Breeze-themed GTK folder dialog are now readable, especially when using a dark color scheme (Jan Blackquill, Plasma 5.21)

Undoing the deletion of a file or folder can no longer unexpectedly overwrite an existing item that has the same name as the undeleted item (David Faure, Frameworks 5.79)

System Settings no longer crashes when you navigate to the Screen Locking page’s “Appearance” sub-page and then navigate away (Nicolas Fella, Frameworks 5.79)

Okular and potentially other KDE apps too can once again open files accessed from a web browser (i.e. https://www.example.org/my_awesome_file.pdf) using their “Open” dialogs (Albert Astals Cid, Frameworks 5.79)

When you open a file accessed from a web browser in a KDE app and then show the Open dialog again, it no longer tries to show you the file’s parent website (Albert Astals Cid, Frameworks 5.79)

System Tray applets that feature expanding lists of actions no longer sometimes have some of the actions clipped out of the view when using non-default fonts or font sizes (me: Nate Graham, Frameworks 5.79)

User Interface Improvements

Dolphin’s “Copy file path” action has had its shortcut changed to Ctrl+Alt+C so as not to conflict with the “Copy” action in the embedded terminal panel, whose shortcut is Ctrl+Shift+C (Someone going by the pseudonym “The Imp”, Dolphin 20.12.2)

Gwenview’s titlebar now shows the path to the currently-viewed location while in Browse mode (Antonio Prcela, Gwenview 21.04)

When using the systemwide Double-Click mode, you can now rename files on the desktop by clicking on the label of an already-selected item, just like in Dolphin (me: Nate Graham, Plasma 5.21)

Plasma no longer ever sends a pointless notification when you create a link to a file somewhere else (Nicolas Fella, Plasma 5.21)

Rotated widgets are no longer jaggy and aliased (David Edmundson, Plasma 5.21):

The Edit Mode toolbar now includes a link to the System Settings Global Themes page (someone going by the pseudonym “Wael CH”, Plasma 5.21):

System Settings’ Desktop Effects and Background Services pages now support the “Highlight changed settings” feature (Cyril Rossi, Plasma 5.21)

The icons used for screen rotation in System Settings’ Display & Monitor page are now clearer (someone going by the pseudonym “Wael CH”, Plasma 5.21):

Breeze theme monochrome icons now stay monochrome when using scale factors greater than 200% (Kai Uwe Broulik, Frameworks 5.79)

How You Can Help

Have a look at https://community.kde.org/Get_Involved to discover ways to be part of a project that really matters. Each contributor makes a huge difference in KDE; you are not a number or a cog in a machine! You don’t have to already be a programmer, either. I wasn’t when I got started. Try it, you’ll like it! We don’t bite!

Finally, consider making a tax-deductible donation to the KDE e.V. foundation.

Thursday

21 January, 2021

My talk “Partially-Formed Objects For Fun And Profit” from Meeting C++ 2020 is now online here: https://www.youtube.com/watch?v=9OQKZl7ha7g

From the abstract:

Lately, partially-formed states have gained some traction as the natural state of moved-from objects. Alexander Stepanov, in his landmark book Elements of Programming (EoP), defines them as a state in which the only valid operations are assignment and destruction. This state was, and continues to be, essential to high-performance code, and to the C++ guarantee of “don’t pay for what you don’t use”.

We will also show how developers that feel uneasy about the partially-formed state can avoid them at little to no cost, neither in code readability, nor performance, and use these examples to propose a new (or old) paradigm for API design: safe and unsafe functions (in the Sean Parent sense).

A longer version of the talk, in which I also cover the Bonus Slides, for which I didn’t have time in the Meeting C++ version, is available from the MUC++ YouTube channel: https://www.youtube.com/watch?v=HcSAIZafNZU

Wednesday

20 January, 2021

Featured image of post Efficient custom shapes in QtQuick with Rust

One of the advantages of QWidgets when building a Qt application is the ability to build in a simple way custom widgets with the QPainter API. This gives the Qt developer almost total freedom to implement complex geometries for their widgets.

On the other hands, QML contains by default only rectangles. These rectangles can change the radius to create circles and rounded rectangles, but more complex shapes are more complicated.

The current state of custom geometry in QtQuick

Fortunally, the Qt API provides multiple ways to implement custom shapes, that depending on the needs might be enough.

There is the Canvas API using the same API as the canvas API on the web but in QML. It’s easy to use but very slow and I wouldn’t recommend it.

Instead of the Canvas API, from the QML side, there is the QtQuick Shapes module. This module allows creating more complex shapes directly from the QML with a straightforward declarative API. In many cases, this is good enough for the application developer but this module doesn’t offer a public C++ API.

If you need more controls, using C++ will be required to implement custom QQuickItem. Unfortunately drawing on the GPU using QQuickItem is more complex than the QPainter API. You can’t just use commands like drawRect, but will need to convert all your shapes in triangles first. This involves a lot of maths like it can be seen in the example from the official documentation or from the KDAB tutorial (Efficient custom shapes in Qt Quick).

A QPainer way is also available with QQuickPaintedItem, but it is slow because it renders your shape in a textured rectangle in the Scene Graph.

The Rusty way

What if we could transform arbitrary shapes into triangles? We would get a high level API but still get great performance. This process is called tessellation and there are a few libraries that implement it. For example in C++, we have Skia and CGAL. Unfortunatelly, both aren’t easy to use, so I decided to look at the Rust library ecosystem and in particular at Lyon, which was designed with performance and compliance to the SVG standard in mind since the goal is to use it in Servo in the future.

Lyon doesn’t have any C++ bindings but I got inspired by the recent blog post from Jonah and I need to say the experience of writing bindings was a breeze.

The first step was creating wrapper structs around the Lyon primitives. LyonPoint, LyonGeometry and LyonBuilder will later be directly usable from the C++ side.

#[cxx::bridge]
mod ffi {
    pub struct LyonPoint {
        x: f32,
        y: f32,
    }

    pub struct LyonVector {
        x: f32,
        y: f32,
    }

    pub struct LyonGeometry {
        vertices: Vec<LyonPoint>,
        indices: Vec<u16>,
    }

    extern "Rust" {
        type LyonBuilder;
        fn new_builder() -> Box<LyonBuilder>;
        fn move_to(self: &mut LyonBuilder, point: &LyonPoint);
        fn line_to(self: &mut LyonBuilder, point: &LyonPoint);
        fn relative_move_to(self: &mut LyonBuilder, to: LyonVector);
        fn close(self: &mut LyonBuilder);
        fn quadratic_bezier_to(self: &mut LyonBuilder, ctrl: &LyonPoint, to: &LyonPoint);
        fn cubic_bezier_to(self: &mut LyonBuilder, ctrl1: &LyonPoint, ctrl2: &LyonPoint, to: &LyonPoint);
        fn build_fill(builder: Box<LyonBuilder>) -> LyonGeometry;
        fn build_stroke(builder: Box<LyonBuilder>) -> LyonGeometry;
    }
}

We then need to define the methods we declared above. These are all trivial to implement since they are just wrapping the Lyon API.

use ffi:{LyonPoint, LyonVector, LyonGeometry};

// Create a wrapper arround Lyon svg path. This struct is opaque from
// the C++ side so we won't be able to access the internal object, but
// we still can call the methods on it.
pub struct LyonBuilder {
    builder: WithSvg<Builder>,
}

// Implement wrapping methods
impl LyonBuilder {
    fn close(&mut self) {
        self.builder.close();
    }

    fn move_to(&mut self, to: &LyonPoint) {
        self.builder.move_to(point(to.x, to.y));
    }

    fn line_to(&mut self, to: &LyonPoint) {
        self.builder.line_to(point(to.x, to.y));
    }

    fn quadratic_bezier_to(&mut self, ctrl: &LyonPoint, to: &LyonPoint) {
        self.builder.quadratic_bezier_to(point(ctrl.x, ctrl.y), point(to.x, to.y));
    }

    ...
}

// Lyon Builder constructor
pub fn new_builder() -> Box<LyonBuilder> {
    return Box::new(LyonBuilder{
        builder: Path::builder().with_svg()
    })
}

The next step was to add the build_fill that will transform the SVG path instructions into a set of vertices and indices. These vertices and indices will be directly available from the C++ side. This is extremely handy since this can be directly fed into the QSGGeometry painting method.

pub fn build_fill(builder: Box<LyonBuilder>) -> LyonGeometry {
    let mut buffers: VertexBuffers<Point, u16> = VertexBuffers::new();
    {
        let mut vertex_builder = simple_builder(&mut buffers);

        // Create the tessellator.
        let mut tessellator = FillTessellator::new();

        let path = builder.builder.build();

        // Compute the tessellation.
        let result = tessellator.tessellate_path(
            &path,
            &FillOptions::tolerance(0.01),
            &mut vertex_builder
        );
        assert!(result.is_ok());
    }

    LyonGeometry {
        // convert_points transform lyon::point to our LyonPoint wrapper
        vertices: convert_points(buffers.vertices), 
        indices: buffers.indices,
    }
}

And we are almost done with the Rust side, we still need to create the cargo and corrosion configuration, but I won’t go into details in this post. You can look at how it was done in this pet project.

Using the generated bindings

To make it easy to store and manipulate the path, I create a simple abstraction to the various SVG path instructions.

#include <QList>
#include <variant>
#include <tessellation.rs.h>

/// Move to the point without drawing a line.
struct MoveTo
{
    /// The destination.
    LyonPoint to;
};

/// Drawe a line to a specific point.
struct LineTo
{
    /// The destination.
    LyonPoint to;
};

/// Draw a cubic bezier curve to the point.
struct CubicBezierTo
{
    /// First control point.
    LyonPoint ctrl1;
    /// Second control point.
    LyonPoint ctrl2;
    /// The destination.
    LyonPoint to;
};

/// Close a path.
struct Close
{};

/// SVG conform path commands
using PathSection = std::variant<MoveTo, LineTo, CubicBezierTo, Close>;

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

/// The SVG path data. It contains a list of instruction (move to, line to, ...).
using PathData = QList<PathSection>;

Now let finally use Lyon to generate the geometry primitives. This will need to be called every time the list of commands is updated. It’s using the command abstraction, I build previously, but this could directly call the LyonBuilder methods.

const auto commands << MoveTo { LyonPoint{0.0, 0.0} }
                    << LineTo { LyonPoint{0.0, 40.0} }
                    << LineTo { LyonPoint{40.0, 40.0} }
                    << CubicBezierTo{ LyonPoint{70.0, 40.0}, LyonPoint{70.0, 0.0}, LyonPoint{ 50.0, 20.0} }
                    << LineTo { LyonPoint{40.0, 0.0} }
                    << Close {};

auto lyonBuilder = new_builder();
for (const auto &command: commands) {
    std::visit(overloaded {
        [&lyonBuilder](MoveTo moveTo) { lyonBuilder->move_to(moveTo.to); },
        [&lyonBuilder](LineTo lineTo) { lyonBuilder->line_to(lineTo.to); },
        [&lyonBuilder](CubicBezierTo cubicBezierTo) { lyonBuilder->cubic_bezier_to(cubicBezierTo.ctrl1, cubicBezierTo.ctrl2, cubicBezierTo.to); },
        [&lyonBuilder](Close) { lyonBuilder->close(); },
    }, command);
}
auto m_geometry = build_fill(std::move(lyonBuilder));

And finally here is our updatePaintNode method. It’s using the GL_TRIANGLES drawing mode and the vertices and indices are copied directly from the geometry Lyon gave us.

QSGNode *PathItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
    QSGGeometryNode *node = nullptr;
    QSGGeometry *geometry = nullptr;

    if (!oldNode) {
        node = new QSGGeometryNode;
        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
                                   m_geometry.vertices.size(), m_geometry.indices.size());
        geometry->setIndexDataPattern(QSGGeometry::StaticPattern);
        geometry->setDrawingMode(GL_TRIANGLES);
        node->setGeometry(geometry);
        node->setFlag(QSGNode::OwnsGeometry);

        QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
        material->setColor(QColor(255, 0, 0));
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
    } else {
        node = static_cast<QSGGeometryNode *>(oldNode);
        geometry = node->geometry();
        geometry->allocate(m_geometry.vertices.size(), m_geometry.indices.size());
    }

    QSGGeometry::Point2D *points = geometry->vertexDataAsPoint2D();
    std::size_t i = 0;
    for (const auto &vertice: m_geometry.vertices) {
        points[i].set(vertice.x, vertice.y);
        i++;
    }

    quint16* indices = geometry->indexDataAsUShort();
    i = 0;
    for (const auto indice: m_geometry.indices) {
        indices[i] = indice;
        i++;
    }

    node->markDirty(QSGNode::DirtyGeometry);
    return node;
}

It is only using Lyon SVG path rendering, but Lyon provides a lot more APIs. For example, there is an abstraction that allows to draw circle, ellipse, rounded rectangle and other basic geometric forms.

There is also the possibility to add custom attributes for texture coordinate or color coordinate. Depending on your need more part of the API could be wrapped and I might create a small library wrapping most of the API.

Custom shape in action

I used this technique in a new toy I’m building. I’m not sure where it is going, but I currently have this:

As was previously discussed, since the 6.0.0 release of Qt, Qt 3D no longer ships as a pre-compiled module. If you need to use it on your projects, try out the new features, or just see your existing application is ready for the next chapter of Qt’s life, you need to compile Qt 3D from source.

In order to do this, you can do it the traditional way ([cq]make ...; make; make install) or use the Conan-based system that is being pioneered with the latest version of the MaintenanceTool.

Using Conan

The latest version of the MaintenanceTool (from the unified installers) will optionally install several things:

  • Conan, a package manager which can build C++ libraries and handle the dependencies
  • CMake, the meta-build system, which is required for Qt 6
  • Ninja, the lighting fast replacement for make and nmake

Additionally, you will need to select Qt 3D in the Additional libraries section so that the source code is available.

As mentioned above, these are optional. You need to find them and enable them in the MaintenanceTool’s UI.

 

Conan, being a package manager, is designed to handle all dependencies to make it easy to build libraries and applications. However, the way this is setup in the current packages installed by the MaintenanceTool is not complete.

Additional things you need for Conan

In particular, you need two other things:

  • You need to select the Qt Shader Tools module in the MaintenanceTool, if you intend to use the new RHI-based backend for Qt 3D. Without it, Qt 3D will build fine but only the original (and more feature rich) backend will be available.
  • Since Qt 3D is a Qt module, it has many of the similar requirements you need when building Qt itself. At the very least, you need a perl interpreter, so that the module header files can be generated. A full list of requirements for the various platforms is available here.

Once you have all the required bits, it’s time to open a console and run Conan. The process is platform-specific, as each platform has it’s own Conan profile. A complete example is available on the Qt wiki, but this is how it works on my mac where the Qt SDK is installed in /Users/Shared/Qt:

conan install qt3d/6.0.0@qt/final --build=missing -s build_type=Release -g cmake_paths -g=cmake -g deploy --profile=/Users/Shared/Qt/Tools/Conan/profiles/qt-6.0.0-macx-clang

and wait…it takes a while to build Qt 3D.

Notes:
  • conan, cmake and ninja need to be in your path. In particular, make sure the Qt provided version of Conan is found first, because…
  • the MaintenanceTool installs a custom version of Conan and write config files in the $HOME/.conan folder. This means, at least on multi-user systems, Conan will only properly work for the user who installed the Qt SDK, and that version is found before any platform-provided version.

 

Using QMake or CMake

Using the MaintenanceTool and Conan makes it easy to get the source and the required dependencies for building Qt 3D. As we saw in the aforementioned, there are still some rough edges. So, in particular, if you are familiar with building Qt modules already, you can just use the “old way”, i.e., download the source and use QMake, or now, CMake.

The packages for the released versions of Qt 3D (and other Additional modules) are available here.

QMake

Once extracted, you can do a shadow build of Qt 3D pretty easily (this has the same build requirements as above though, QtShaderTools module and perl interpreter).

tar zxf qt3d-everywhere-src-6.0.0.tar.xz
cd qt3d-everywhere-src-6.0.0
mkdir build
cd build
/Users/Shared/Qt/6.0.0/clang_64/bin/qmake ..
make
make install

And, again, wait 🙂.

CMake

The process is similar if you want to use CMake instead. In the build folder, do:

/Users/Shared/Qt/6.0.0/clang_64/bin/qt-cmake -G Ninja ..
ninja
ninja install

Once done, your applications should be able to find the Qt 3D modules.

You can optionally build the documentation using the docs target (but this will not be installed; you will need to register it with Creator and/or QtAssistant for it to appear).

Note:

Qt 3D will likely preserve the dual QMake/CMake build system for a while still.

Conclusion

As you see, building Qt 3D involves a bit more work than it did before. Fortunately, it needs to be done only once for each release. Going forward, we will be able to release Qt 3D on a different cycle from the rest of Qt and, as a result, introduce features and bug fixes more frequently. Please do not hesitate to get in touch with us or the Qt build system team if you have questions regarding this new setup.

 

KDAB provides a number of services around Qt 3D, including mentoring your team and embedding Qt 3D code into your application, among others. You can find out more about these services here.

 

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post Getting your 3D ready for Qt 6 appeared first on KDAB.