This week we plunged into fixing issues in Plasma 5.19 that slipped through QA as well as some older ones–a truly enormous number in all! We are taking to heart your pleas to focus on stability and polish. But of course we also worked on some new features too, because we can walk and chew bubblegum at the same time!

New Features

Spectacle’s timer feature and Shift+Printscreen (take full screen screenshot) and Meta+Shift+Printscreen (take rectangular region screenshot) shortcuts now work on Wayland (Méven Car, Plasma 5.20 and Spectacle 20.08.0)

Yakuake now shows a system tray item when running (which is optionally hide-able of course), so it’s easier to tell that it’s running and so that there’s a graphical way to summon (Maximillian Schiller, Yakuake 20.08.0)

It’s now possible to perform searches in a new private/incognito window using KRunner’s web shortcuts (Alexander Lohnau, Plasma 5.20)

KRunner’s web shortcuts runner now includes a module for searching the Arch wiki; search for “arch: foobar” in KRunner to try it out! (Alexander Lohnau, Frameworks 5.72)

For all those super secret Arch wiki searches that you don’t want anyone else to know about

Bugfixes & Performance Improvements

Okular’s form-filling feature now works more reliably for certain forms using JavaScript (Albert Astals Cid, Okular 1.10.3)

Apps removed from the favorites list shared across Kickoff, Kicker, and Application Dashboard now always stay removed after you reboot (Alexander Lohnau, Plasma 5.18.7)

Fix Plasma 5.19 regression: the Battery & Brightness applet now appears in the system tray again (David Edmundson, Plasma 5.19.1)

Fix Plasma 5.19 regression: The System Tray battery icon is no longer vertically mis-aligned when not showing the battery percentage (Michail Vourlakos, Plasma 5.19.1)

Fix Plasma 5.19 regression: System Tray icons set to “always show” are now always shown (Konrad Materka, Plasma 5.19.1)

Fix Plasma 5.19 regression: resolved various display issues with the new Hard Disk monitor (Arjen Hiemstra, Plasma 5.19.1)

The Kicker Application Menu no longer suffers from a visual glitch whereby the search results list is too small when there are a lot of favorite items (Alexander Lohnau, Plasma 5.19.1)

Copying system information to the clipboard no longer includes trailing spaces and newlines (Claudius Ellsel, Plasma 5.19.1)

System Settings no longer crashes when you visit the Thunderbolt page and then switch to another page (David Edmundson, Plasma 5.19.1)

Fix Plasma 5.19 regression: the Screen Edges effects now works again (Vlad Zahorodnii, Plasma 5.19.2)

Fix Plasma 5.19 regression: the “Configure Window Manager” menu item from the titlebar context menu now works again (Méven Car, Plasma 5.19.2)

Fix Plasma 5.19 regression: WiFi network names in the Networks applet no longer interpret HTML, preventing maliciously-crafted network names from doing potentially nefarious things (me: Nate Graham, 5.19.2

Fix Plasma 5.19 regression: the Global Menu applet once again updates itself correctly when the current application changes (Carson Black, Plasma 5.19.2)

Fix Plasma 5.19 regression: KRunner’s window is once again positioned correctly when using a high DPI screen and the PLASMA_USE_QT_SCALING=1 environment variable (David Edmundson, Plasma 5.19.2)

Fix Plasma 5.19 regression: there is no longer a faint blue outline around the circular timeout indicator for notification pop-ups (Arjen Hiemstra, Plasma 5.19.2)

Interacting with the media controls on the lock screen will now pause the timer that determines when the UI fades out (me: Nate Graham, Plasma 5.19.2)

Copying system information to the clipboard in English now actually uses English for everything (Claudius Ellsel, Plasma 5.19.2)

The search feature in the new System Monitor configuration pages now works (David Edmundson, Plasma 5.19.2)

Picture of The Day wallpapers now work on the lock screen (Yunhe Guo, Plasma 5.20)

Fix Plasma 5.19 regression: Placeholder messages in Plasma applets now respect the Plasma style and color scheme (me: Nate Graham, Plasma 5.20, though I have asked distros to backport it to 5.19)

Fix Plasma 5.19 regression: After entering the password for a Wi-Fi network in the networks applet, you’re no longer pointlessly asked for the same password again in a dialog box (me: Nate Graham, Plasma 5.20, though I have asked distros to backport it to 5.19)

The Plasma panel no longer appears on top of full screen apps and views when on Wayland (somebody awesome, Plasma 5.20)

The trash now automatically purges its records for files that are deleted by hand, preventing a situation where the whole system would freeze because it was trying in vain to collect information about files that no longer existed (Ahmad Samir, Frameworks 5.72)

Monochrome icons in QML-based software are no longer often subtly blurry (Noah Davis, Frameworks 5.72)

Clicking on the scrollbar in certain scrollable Kirigami sheets no longer unexpectedly closes the sheet (Ismael Asensio, Frameworks 5.72)

Setting a default web shortcut now works (Alexander Lohnau, Frameworks 5.72)

The legal notice in KHelpCenter is now readable no matter what color scheme you’re using (Yuri Chornoivan, Frameworks 5.72)

Fixed a graphical glitch that could cause notification pop-ups’ timeout indicators to be drawn as a black square on old graphics hardware (Yaroslav Sidlovsky, Frameworks 5.72)

User Interface Improvements

Dolphin now displays remote shares and FUSE mounts with their user-friendly display name rather than the full path (Méven Car, Dolphin 20.08.0)

The new System Monitor widgets now have more sane default sizes (Marco Martin, Plasma 5.19.2)

System Tray items now respect Fitts’ Law; you can activate on the nearest applet when clicking on the pixel adjacent to the screen edge, making the click target effectively infinite in that direction (Michail Vourlakos, Plasma 5.20)

The Plasma audio applet and System Settings page now filter out unused audio devices by default to avoid cluttering up the view. You can however still show them if you’d like (me: Nate Graham, Plasma 5.20):

Discover now shows you when an application listed in the search or browse list comes from a different source than your default source (Aleix Pol Gonzalez, Plasma 5.20):

Notice the little Flatpak icons in the corner? I know this presentation isn’t ideal; I was planning to polish it up and add text but didn’t have time this week, sorry. Maybe next week!

The layout of the Digital Clock widget’s calendar pop-up has been refined and streamlined in response to user feedback (me: Nate Graham, Plasma 5.20):

Shadows for pop-ups in GTK3 apps using the Breeze-GTK theme now better match the shadows in Qt-based apps (Somebody going by the pseudonym “Toluschr,” Plasma 5.20)

The Web Shortcuts window now has a sane default size when opened from the System Settings Krunner page (Alexander Lohnau, Plasma 5.20)

The “Switch Application Language.. “menu item has been relocated from the Help menu to the Settings menu and renamed to “Configure Language…” to match the style of other items in that menu (me: Nate Graham, Frameworks 5.72):

How You Can Help

Have a look at https://community.kde.org/Get_Involved to discover ways to help 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

18 June, 2020

Last week, I did not manage to work as much as I should, because I was not feeling very well. Now that I am feeling better, I am trying to work more to compensate. This is the post that should have been written last week.

This week, I implemented the first version of the graph-layout-plugin for Rocs. For now, it supports only a force based graph layout algorithm, which is an adaption of the Fruchtermani-Reingold algorithm. The graph-layout-plugin can be found at Graph Document - Tools - Graph Layout. The user interface is the following.

Screenshot of the graph-layout-plugin

The user can control the magnitude of the forces acting on the nodes using these slide bars. I decided to use slide bars because I do not think the user would benefit from having the exact value of each parameter.

I used the graph-layout-plugin to generate the following layouts. All of them are for connected graphs because I still need to handle the disconnected ones.

Complete graph. Tree. Circle graph. Planar graph. Random graph.

I experimented a little in order to find default parameters that work well. In particular, the layout algorithm used requires a rectangle in which all centers of the nodes should be placed and the forces applied depend on the area of this rectangle. In order to make things simpler, I decided to go with squares.

How big should a square for a given graph be? Sincerely, I do not now. I came up with an heuristic, though. My heuristic finds a square big enough for one to be able to place a certain number of circles at positions chosen at random with uniform probability, with high probability of not having intersections between circles. Currently, I am using one circle for each vertex and for each edge. If a random algorithm can succeed at placing this many circles without getting intersection, there should be enough space in the square so the graph layout algorithm can move nodes around easily.

Three months after the release of Krita 4.2.9, we’ve got a major new feature release for you: Krita 4.3.0! We’ve spent the past year not just fixing bugs, even though we fixed over a thousand issues, but have also been busy adding cool new stuff to play with.

There’s a whole new set of brush presets that evoke watercolor painting. There’s a color mode in the gradient map filter and a brand new palettize filter and a high pass filter. The scripting API has been extended. It’s now possible to adjust the opacity and lightness on colored brush tips separately. You can now create animated brush tips that select brush along multiple dimensions. We’ve made it possible to put the canvas area in a window of its own, so on a multi monitor setup, you can have all the controls on one monitor, and your images on the other. The color selector has had a big update. There’s a new snapshot docker that stores states of your image, and you can switch between those. There’s a brand new magnetic selection tool. Gradients can now be painting as spirals.

Watch Ramon Miranda’s introduction to Krita 4.3 and check out the release notes for all the details!

Download

Windows

If you’re using the portable zip files, just open the zip file in Explorer and drag the folder somewhere convenient, then double-click on the krita icon in the folder. This will not impact an installed version of Krita, though it will share your settings and custom resources with your regular installed version of Krita. For reporting crashes, also get the debug symbols folder.

Linux

(If, for some reason, Firefox thinks it needs to load this as text: to download, right-click on the link.)

OSX

Note: the gmic-qt is not available on OSX.

Android/ChromeOS Beta

This time, the Android releases are made from the release tarball, so there are translations. Despite being create from the stable 4.3.0 release, we consider Krita on ChromeOS and Android still beta. There are many things that don’t work and other things that are impossible without a real keyboard.

It is still not recommended to use these betas on phones, though they do install. This beta will also be available in the Google Play Store.

Source code

md5sum

For all downloads:

Support Krita

Krita is a free and open source project. Please consider supporting the project with donations or by buying training videos! With your support, we can keep the core team working on Krita full-time.

The post Krita 4.3.0 Released appeared first on Krita.

Wednesday

17 June, 2020

Last week I implemented Automatic Addition/Removal of Tag Icons. DigiKam provides users with the option to assign Icons to Tags, to allow easy visibility of these tags. For Face Tags in particular, Users may assign a Face associated with that Tag as the Tag Icon. However, in the current implementation, most users don’t make use of the Tag Icon assignment. This is because the process involves 2 steps:

  • Confirm a face to a tag (which may lead to the creation of a new tag)
  • Manually assign the face as the Tag Icon.
Watch on Imgur for higher resolution: https://imgur.com/a/ME34rht

This can be annoying for the User if there are a large number of Tags.

These two processes can be easily automated, so that whenever a new Tag is created (as a consequence of Face Confirmation), then the Face is automatically assigned as the Tag Icon. A similar process can be implemented in the reverse process, that is if the User deletes the last Face associated with the Tag, then the Tag Icon should be deleted.

Implementation Details

1. Automatic Assignment of Tag Icons

People Tags are defined as Tag Albums (TAlbums) in the DigiKam database, as such there already exist methods for assignment of Album Icons (updateTAlbumIcon) provided by the AlbumManager class.

Confirming and Rejecting faces ultimately happens at the Database level, which led to a problem. Functions of AlbumManager can only be used in files that are compiled with AlbumManager (gui_digikam_obj), and since Database functions are pretty low level, they aren’t compiled with AlbumManager.

To get around this problem, I included Automatic Icon Assignment in higher-level classes (such as FaceUtils), and made use of QTimers to ensure that the lower level Database functions (sometimes in different threads), were completed before trying to assign a tag.

Here are my commits regarding the same:

2. Automatic Removal of Tag Icons

In case the User accidentally confirms a Face, this would lead to automatic assignment of the wrong face to a tag. Hence it’s important to enable Automatic Removal of Tag Icons, as it would allow re-Assignment of a different face to the Tag.

If the face just deleted (or rejected) was the final face associated with a tag, then the Tag Icon associated should be removed. Here the role of QTimer becomes even more important, as you should check the number of Faces associated with the Tag, only when all the Core Database functionality have been completed.

Faces can be removed from a Tag in 2 ways.

  • Delete the Face Region (using the ✖)
  • Remove the Face Tag (using the context menu for each Face)

I patched tag removal for both of these cases in the following commits:

Final Results

All said and done, here’s how the feature looks!

Animated GIF - Find & Share on GIPHY
Watch on Imgur for higher resolution: https://imgur.com/a/cGNZCPr

In the video I demonstrate 4 kinds of Automatic Icon functionality:

  • Icon Assignment to a newly created Tag.
  • Icon Assignment to an existing Tag with no Icon.
  • Icon Removal when Face Region is deleted.
  • Icon Removal when Face Tag is deleted.

Future Improvements

  • Implement batch Auto Assignment. This would be helpful to current Users of DigiKam who might have a large set of Face Tags without Icon.
  • Increase the default size of the rows in People Sidebar, so that Tag Icons are easily recognizable.
  • Standardize the size of Icons. In this implementation, since the Face is assigned as the Tag Icon, the size of the Face (as recognized by the Detection Algorithm) has slight control over the size of the Icon. Rarely this leads to a slightly unequal Tag Icons. This isn’t a huge issue, but can be improved.

Today’s blog post is about something that should be simple and apparently it causes trouble: how to declare a qHash overload for a custom datatype. This is necessary when we want to use custom datatypes as keys in a QHash. From the documentation:

A QHash’s key type has additional requirements other than being an assignable data type: it must provide operator==(), and there must also be a qHash() function in the type’s namespace that returns a hash value for an argument of the key’s type.

In other words, given a class like:

namespace NS {

class EXPORT MyClass
{
    A m_a;
    B m_b;
    C m_c; 
    D m_d;
    ~~~
};

}

How do we implement its qHash() overload? Let’s find out! (Most of this applies to std::hash too.)

Look at its operator==

The first thing to look at is the implementation of operator== (or operator<=>, coming C++20).

Assuming the implementation is sane, then the code will tell us which members are relevant for equality and which ones are not (caches, tags, whatever). The ones not relevant shall not be hashed (otherwise, you might end up with objects that compare equal and have different hashes).

If the type in question has a botched operator==, then either fix it if possible, otherwise kill the corresponding qHash() overload via = delete. This will avoid having people re-adding it in client code.

If the type does not have an operator==, or cannot have a meaningful one, do not provide qHash() at all.

What’s a “sane” implementation? Generally one that recursively uses plain operator== on the member subobjects. Fuzzy comparisons are a bad idea, making the type not hashable efficiently.

Prepare for Qt 6 (optional)

Optional step: prepare for some source-incompatible changes in Qt 6 (the impact is still unknown; possibly just warnings). Qt 6 changed the return type of qHash() from uint to size_t, so you can guard yourself with something like:

#if QT_VERSION < QT_VERSION(6, 0, 0)
using qhash_result_t = uint;
#else
using qhash_result_t = size_t;
#endif

Declaring your qHash overload

The idiomatic approach is:

namespace NS {

// 1. add a forward declaration for the class
class MyClass;

// 2. consume the forward declaration for the class, by
// forward declaring its qHash overload
EXPORT qhash_result_t qHash(const MyClass &c, qhash_result_t seed = 0) noexcept;

class EXPORT MyClass
{
    ~~~ // same as before
    
    // 3. add:
    friend EXPORT qhash_result_t qHash(const MyClass &c, qhash_result_t seed) noexcept;
};

}

The reason for going all the trouble with 1+2 is because friend declarations cannot have default parameters unless you’re also defining the function (cf. [dcl.fct.default]). Although QHash will never call your qHash overload without a seed, you may want to unit test it, call it from your own containers that aren’t seeded, etc.; so it’s just “polite” to have the second argument defaulted.)

In case your implementation is entirely inline, you can skip 1+2 and go just with 3, adding the default argument there, and defining (not just declaring) qHash in the body of the class. This would also make the qHash a hidden friend, which would be generally a good thing (see here, here and here for more information about hidden friends).

Using just 3 is not doable in general (e.g. if MyClass is pimpled), in which case your qHash must be out of line, so you have 1+2+3.

Whatever way you go, this will put your qHash overload in the surrounding namespace of your class, which is what you want (qHash is called via argument-dependent lookup in Qt).

Implementing your qHash overload

Then, implement it (inline or outline depending on MyClass). The idiomatic way to do so is to look at your operator==:

bool operator==(const MyClass &lhs, const MyClass &rhs) noexcept
{
    return 
        lhs.a == rhs.a &&
        lhs.b == rhs.b && ~~~;
}

Build your qHash exactly like it: on the same fields, in the same order. This explains why you want qHash to be a friend, just like operator==, to spare going through accessors:

qhash_result_t qHash(const MyClass &c, qhash_result_t seed) noexcept
{
    QtPrivate::QHashCombine hash;
   
    seed = hash(seed, c.a);
    seed = hash(seed, c.b);
    seed = hash(seed, c.c);
    ~~~
    return seed;
}

QtPrivate::QHashCombine is private API, but does not require any special buildsystem magic; it’s in <qhashfunctions.h>, a public header.

The same result can be obtained with the convenience that I have added in Qt 6:

qhash_result_t qHash(const MyClass &c, qhash_result_t seed) noexcept
{
    return qHashMulti(seed, c.a, c.b, ~~~);
}

Reusing the same fields as operator==, in the same order, ensures the casual reader of the code that your hashing is correctly implemented.

If two objects are to be considered identical even if they have their data in a different order, then use QHashCombineCommutative / qHashMultiCommutative. For instance, you must use the commutative versions if you have a class that represents a set of elements, and two sets are equal if they have the same elements, even if stored in a different order. This again can be detected by looking at operator== and spotting a call to std::is_permutation or similar.

However, if your class simply uses a set, then in your operator== you will compare those sets via their own operator==, which means that hashing for your class will still use the non-commutative qHashMulti.

Things not to do in your qHash implementation

Summing

inline size_t qHash(const QQuickShapeGradientCacheKey &v, uint seed = 0)
{
    uint h = seed + v.spread;
    for (int i = 0; i < 3 && i < v.stops.count(); ++i)
        h += v.stops[i].second.rgba();
    return h;
}

A simple sum usually doesn’t spread different values apart enough for the hashing to be effective.

XORing

inline size_t qHash(const QV4Debugger::BreakPoint &b, uint seed = 0) Q_DECL_NOTHROW
{
    return qHash(b.fileName, seed) ^ b.lineNumber;
}

This cancels information out (the seed, the fields themselves).

Getting creative with magic numbers

inline size_t qHash(const QQuickPixmapKey &key)
{
    return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.frame*23) ^
            (key.region->x()*29) ^ (key.region->y()*31) ^ (key.options.autoTransform() * 0x5c5c5c5c);
}

Completely “magic” prime sequence, raising many questions on the values used.

Being out of sync with your operator==

inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs)
{
    return *lhs.region == *rhs.region && *lhs.size == *rhs.size && *lhs.url == *rhs.url &&
            lhs.options == rhs.options && lhs.frame == rhs.frame;
}

This is the same datatype as the previous example. How can you know, from a quick glance, if the qHash overload is correct? (Not good, just correct; equal objects yield equal hashes.)

Creating lots of unnecessary copies/temporaries

size_t qHash(const QQuickTextNodeEngine::BinaryTreeNodeKey &key)
{
    return qHash(qMakePair(key.fontEngine, qMakePair(key.clipNode,
                                                     qMakePair(key.color, key.selectionState))));
}

This is because qMakePair copies. Not necessarily a big deal in case of small, trivially copiable types; but qHashMulti would just lead to cleaner code, and possibly spread the hashing more equally.

Hashing Qt datatypes for which Qt does not provide a qHash overload

All the above discussion refers to the case where the datatype is a user-defined one. Sometimes we may need to build a QHash using a Qt datatype as a key, and we find out that the data type does not have a qHash overload. What do we do in this case?

  1. Report a bug. Any Qt datatype that provides an operator== should also provide a qHash overload.
  2. Roll your own qHash, but protected by a Qt version check that blocks the usage of a higher version of Qt. That code must be revisited when Qt itself gets upgraded — if a new version introduces a qHash overload, your code may break in interesting ways (e.g. ODR violation).
  3. (“Worst case scenario”) Ditch QHash for std::unordered_map and a custom hasher (following the same reasoning of 1+2, you don’t want to specialize std::hash for Qt datatypes), or use a wrapper type.

Hashing std:: datatypes via qHash

Unfortunately, you really shouldn’t do it. First, it should be Qt’s job at providing hashers for datatypes defined by the Standard, not yours, so the previous point applies.

Second, it’s impossible to do it reliably, because a qHash overload for such datatypes should be declared in the std namespace, and that’s not allowed.

An overload in any other namespace will not work correctly. Given your rolled out qHash for an arbitrary std::foo datatype:

// not in namespace std
qhash_result_t qHash(const std::foo &foo, qhash_result_t seed = 0)
{
    return ~~~;
}

Then merely using a QHash will lull us into a false sense of security, as this code will work:

QHash<std::foo, bar> h; 
h.insert(~~~, ~~~);

But many other usages will be, unfortunately, broken. Suppose you just use the datatype as a data member of a class:

struct MyStruct 
{
    std::foo m_a;
    int m_b;
    ~~~
};

And then you want to define its qHash overload just like we’ve seen before:

qhash_result_t qHash(const MyStruct &s, qhash_result_t seed = 0) 
{
    // (very likely) ERROR: qHashMulti does not find qHash(std::foo)
    return qHashMulti(seed, c.m_a, c.m_b); 
}

How is this possible? This is due to how ADL works, combined with 2-phase name lookup. Suppose that you have this little piece of code:

// calls f() on a T object
template <typename T> void call_f() { T t; f(t); }

struct S {};
void f(S);

void use() { call_f<S>(); }

This code works, even if S and f(S) were declared after call_f(). This is what gives us the power of using qHashMulti (AKA call_f) that can use qHash overloads on arbitrary types (AKA f(S)), even if these overloads got introduced after qHashMulti‘s own definition.

If we change the code just slightly, however, we have a problem:

// same as before
template <typename T> void call_f() { T t; f(t); }

#include <stdfoo>
void f(std::foo);

void use() { call_f<std::foo>(); }

This code now does not compile (surprise!). The reason is that the call to f() inside call_f() will only look in some scopes:

  1. it will check if a f(std::foo) was declared before call_f() (it wasn’t)
  2. it will check for a f(std::foo) in std::foo‘s associated namespaces. Which is namespace std, not the one we’re in!

To make the example work, we would need to do something like this:

// same as before
template <typename T> void call_f() { T t; f(t); }

#include <stdfoo>
namespace std {
void f(foo);
}

void use() { call_f<std::foo>(); }

… which would be OK for just about any other namespace. Not for namespace std, though: as explained before, we are not allowed to declare an arbitrary new function in that namespace.

Moving the definition of f(std::foo) before call_f would work (this would mean defining our qHash(std::foo) before including the definition of qHashMulti). But if we do so, we lose the convenience of using qHashMulti (as well as qHash for any other fundamental type or Qt type!).

You can play with this code here on Godbolt.

Long story short: due to qHash design, it’s not possible to conveniently and reliably hash datatypes defined by the Standard. You can use workarounds, e.g. define wrapper types and use such types as keys in your QHash objects.

Bonus points

As in, nice things to have.

  • qHash overloads should really really be noexcept. Throwing from a hashing function sounds wrong on multiple levels.
  • qHash overloads are good candidates for being marked as pure functions, that is, functions that only read memory, never modify it (or have any visible side effects). Such markers should help the compiler optimizing the code further.Qt defines a couple of convenience macros for this. One is Q_DECL_PURE_FUNCTION, that means that the function only reads global memory. There’s also Q_DECL_CONST_FUNCTION, as a even more strict version: the function only reads the values of the parameters, not any memory at all. Note that passing a reference/pointer and reading through it makes a function PURE, not CONST (something like strlen is PURE). Use CONST with a qHash overload where you’d pass an argument by value because it’s a small trivially copiable type like QPoint, QVector3D or similar, otherwise use PURE.

Why do I bother with all this?

Because declaring “ordinary” hashing functions should be straightforward, unless you have profiling data to believe that you can do better than the idiomatic way. I keep seeing code that makes it look dark magic. It’s not!

Ideally, in some not-too-far future, we may reach a level where the compiler will autogenerate the hash function for us — just like in C++20 it may autogenerate the implementation of operator== for us!

Thanks for reading!

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 How to declare a qHash overload appeared first on KDAB.

We have released 9th patch release to Qt 5.12 LTS today. As usual it doesn't bring any new features but many bug fixes & other improvements.

Yes, that title is too long and I know it.

If my previous blog post didn't make it clear, I don't like dealing with XML. Obtuse to write, obtuse to read. Given that I wrote a program so that I wouldn't need to write XML for an application menu protocol, it only makes sense that I would do the same for reading Wayland protocols. And thus, ReadWay and its non-web cousin ilo Welenko were born.

Parsing the XML

If you're familiar with Wayland, you're probably familiar with the XML files you can find in /usr/share/wayland and /usr/share/wayland-protocols. What you may not have noticed is the /usr/share/wayland/wayland.dtd file lurking alongside the core Wayland protocol. This is a document type definition file, which defines what a valid XML document looks like. Thankfully, this is a fairly simple DTD to write Go structures for. This DTD definition:

<!ELEMENT description (#PCDATA)>
  <!ATTLIST description summary CDATA #REQUIRED>

becomes this Go code:

type Description struct {
    Summary string `xml:"summary,attr"`
    Body    string `xml:",chardata"`
}

And this:

<!ELEMENT protocol (copyright?, description?, interface+)>
  <!ATTLIST protocol name CDATA #REQUIRED>

becomes

type Protocol struct {
    Name        string      `xml:"name,attr"`
    Copyright   string      `xml:"copyright"`
    Description Description `xml:"description"`
    Interfaces  []Interface `xml:"interface"`
}

Fairly simple, eh?

To unmarshal a protocol XML into a Go structure, you just xml.Unmarshal like this:

data, err := ioutil.ReadFile(path)
// handle error
proto := Protocol{}
err = xml.Unmarshal(data, &proto)
// handle error
// do something with proto

Templates

Of course, Go structs aren't particularly easy to read for documents even compared to XML. This is when Go's html/template package comes into play. You can throw a Protocol and a template at it like so:

<h1>{{ .Name }} <small class="text-muted">protocol</small></h1>

<p>
    {{ .Description.Body }}
</p>

{{ range $iface := .Interfaces }}
    <h2>{{ $iface.Name }} <small class="text-muted">interface version {{ $iface.Version }}</small></h2>

    <!-- finish rendering interfaces -->

{{ end }}

Of course, you have the more generic text/template package, which is what ilo Welenko uses. Same concept applies:

Kirigami.Page {
    title: "{{ .Name }}"
    ColumnLayout {
        {{ range $iface := .Interfaces }}
        Kirigami.Heading {
            text: "{{ $iface.Name }} version {{ $iface.Version }}"
        }
        {{ end }}
    }
}

(And yes, I am statically generating QML code in Go and loading it instead of marshalling it into Qt data types and using model/views/repeaters.)

See Also:

  • ReadWay hosted: ReadWay hosted on the internet. The “special thing that might happen when you drag an XML file onto [the] paragraph” is a Wayland protocol being rendered in your browser using WASM. The future is now. And it don't need no cookies.
  • ReadWay source: The static generator for ReadWay.
  • ilo Welenko: The desktop counterpart to ReadWay that renders into QML rather than HTML. At the time of this post, it's very incomplete compared to the web version.

Contact Me

Have any thoughts/comments/concerns about this post, or want to tell me that I shouldn't statically render QML? Here's how you can contact me:

  • Telegram: @pontaoski
  • Discord: pontaoski blackquill 🏳🌈#8758
  • Matrix: pontaoski@tchnics.de
  • IRC: appadeia_
  • Email: uhhadd@gmail.com

Tags: #libre

I found a way to get external microphones connected to the audio jack working for my laptop ! KDE contributor Debarpan Debnath pointed me to an Arch wiki page that helped me reach a solution:

  1. Open/create the file /etc/modprobe.d/alsa.conf and add the following on a new line: options snd_hda_intel index=0 model=dell-headset-multi
  2. go to System Settings > Audio > Advanced > and check the checkbox saying “Automatically switch all running streams when a new output becomes available” (this applies to inputs too)
  3. Reboot
  4. Mute the microphone using the keyboard’s microphone mute button

Now the audio jack microphone is detected and will be automatically switched to! This also fixes the issue I was having with the mute button not muting the right microphone; it was in fact the same problem as the external microphone itself not working. I have added this information to the relevant Kernel bug report.

Now my headset’s microphone is working as expected–too late for last week’s virtual Plasma sprint though. Oh well, my colleagues got to hear my kids playing/rioting a lot. 🙂

There is now one remaining issue with the use of an external microphone: the two audio input sources are represented in the plasma-pa applet as different devices rather than as different ports of the same device, which would make the switch-on-connect module unnecessary and clean up the display in the Plasma audio applet. Apparently this is a PulseAudio issue in that the device’s hardware doesn’t fit cleanly into PulseAudio’s abstraction model! There are some PulseAudio patches to clean things up which I have reviewed. So for now, this is the best we can do:

Slow but consistent progress…

Tuesday

16 June, 2020

First of all, sorry for not making a blog post early on during the community bonding period. I couldn't because I was mostly busy with Krita's Android release.

Secondly, some of you might remember me from the previous year. I was GSoC student for Krita. Now it is my second time! :-)

Finally, to tell a bit about my project. My project is to add support for SVG mesh gradients in Krita and the relevant task is: https://phabricator.kde.org/T13101 and branch is: https://invent.kde.org/graphics/krita/-/merge_requests/378

Now over on to the interesting stuff!

So, let's see what I did the past couple of weeks.

1. Parsing

This is probably going to be easiest and most of us know hows and whats about his. So, we throw any SVG with meshgradient element, in it and Krita understands it now.

2. Making a shape out of the path.

Now, this turned out to be a bit complicated than I had anticipated, not because this was hard. But because there were a few edge cases which I overlooked. I would fix one edge case, but it would break the other one, classic whack-a-mole. So, I had to do a few rewrites of this tiny 'SvgMeshPatch and SvgMeshArray' component. Finally, I just put it all on paper and got it working for all cases, in a proper manner *.

The way I started was to manually compare some 'edge case' values to see if they're correct and fit the logic. But, there's so much that an eye can overlook. What I think I should've done is to write unit tests and then handle each edge case respectively. However the good thing is after writing unit tests, I discovered two cases where my logic wasn't right.

3. Deeper dive into the technicalities

I will try to explain how I did in a bit more detail. If you have any suggestions/critique, you're welcome.

In the big picture sense there are two things to consider, when talking about meshgradients, meshpatch and meshrow (which is a linear array of meshpatches). As per the specifications, meshpatch is a Coons Patch, which is just the shading/fill defined by the interpolation of colors placed on the corners (i.e. edges of the curve).

Because they can be seen as a two dimensional arrays, creating an array of meshpatches, seems the most logical approach and that's what I did. However there is a slight catch, each patch shares side with other patches. Eg. in case of a gradient with a single row, up to two sides are shared. So, while parsing this had to be taken care of.

So, now we have SvgMeshArray which is an array of of SvgMeshPatch and each SvgMeshPatch owns a KoPathShape, which is how the fill boundaries are going to be defined. And that's basically all there's to it for now...

On a side note, one thing which is in my mind is that because each meshpatch is treated as an individual Shape, there's some duplication with the shared sides. But I think this is an optimization problem, which has to be taken care of, but after rendering :-)

Now a couple of obligatory screenshots, just to double check :)





PS:

* I in no way consider myself an expert, yet. Feel free to look at the code and comment on it.

A quick update, QML Online now has a new home!

What is next

Now that the project is under KDE organization, I'll start with the planned new capabilities, such as the Kirigami support and the html element to help with online documentation of qml snippets.

And to finish this quick update, be invited to help with the project and send Merge Requests, feature requests and opinions.

image