<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Carl Schwan</title><link>https://carlschwan.eu/</link><description>Recent content on Carl Schwan</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 08 Jan 2026 22:00:00 +0000</lastBuildDate><atom:link href="https://carlschwan.eu/index.xml" rel="self" type="application/rss+xml"/><item><title>Events in December 2025</title><link>https://carlschwan.eu/2026/01/08/events-in-december-2025/</link><pubDate>Thu, 08 Jan 2026 22:00:00 +0000</pubDate><guid>https://carlschwan.eu/2026/01/08/events-in-december-2025/</guid><description>&lt;p&gt;December was quite an eventful month for me, with over 4,000 km travelled by
train. This was in part caused by the holidays and visiting family, but also by the
KDE PIM sprint in Paris and the 39th Chaos Communication Congress.&lt;/p&gt;
&lt;h2 id="kde-pim-sprint-in-paris"&gt;KDE PIM sprint in Paris&lt;/h2&gt;
&lt;p&gt;From the 12th to the 14th of December, I was in Paris. It was actually my first time
there for more than a day trip, so I arrived a day earlier to explore the city
a bit. I went on a walk across the city with Tobias and Nicolas, and I took
&lt;a class="link" href="https://carlschwan.eu/photography/#paris-2025" &gt;some photos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2026/01/08/events-in-december-2025/DSCF2263.jpg" data-size="656x437"&gt;
&lt;img src="https://carlschwan.eu/2026/01/08/events-in-december-2025/DSCF2263.jpg" width="656" height="437" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The weekend was also very productive. We advanced our goal of making
&lt;a class="link" href="https://api.kde.org/kmime-index.html" target="_blank" rel="noopener"
&gt;KMime&lt;/a&gt; a proper KDE Framework; made
Message-IDs in emails more privacy-conscious; and discussed various important
topics such as the retirement of the Kolab resource and the switch to SQLite as the
default backend for Akonadi.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2026/01/08/events-in-december-2025/kde-pim-sprint-2025-group-photo.jpg" data-size="700x443"&gt;
&lt;img src="https://carlschwan.eu/2026/01/08/events-in-december-2025/kde-pim-sprint-2025-group-photo.jpg" width="700" height="443" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Huge thanks to &lt;a class="link" href="https://haute-couture.enioka.com/en/" target="_blank" rel="noopener"
&gt;enioka Haute Couture&lt;/a&gt; for
having us in their office in Paris.&lt;/p&gt;
&lt;p&gt;The sprint being in Paris also allowed me to afterward go visit my grandma, 350 km
further south of Paris, so this was particularly convenient.&lt;/p&gt;
&lt;h2 id="39th-chaos-communication-congress-39c3"&gt;39th Chaos Communication Congress (39c3)&lt;/h2&gt;
&lt;p&gt;Another event I went to was 39c3, which is the third year in a row that I attended,
and this year again we had an assembly as part of the Bits und Bäume umbrella,
thanks to Joseph.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2026/01/08/events-in-december-2025/DSCF3242.JPG" data-size="423x634"&gt;
&lt;img src="https://carlschwan.eu/2026/01/08/events-in-december-2025/DSCF3242.JPG" width="423" height="634" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I love the vibe of this event. It&amp;rsquo;s not very dry or only tech-focused, but also has
a big artistic and political aspect to it. And while the number of attendees is
very large, at the same time it&amp;rsquo;s very chill and I don&amp;rsquo;t feel overwhelmed, unlike
at FOSDEM.&lt;/p&gt;
&lt;p&gt;At the KDE assembly, we met a lot of interested users, some GNOME friends, and since
a bunch of KDE devs were there, we managed to work on a few productive things, like
switching the map backend from Itinerary to MapLibre.&lt;/p&gt;
&lt;p&gt;And this year, I even managed to go on national TV for a few seconds to speak
about Nextcloud. My German grandma called me the day afterward, very happy to have
seen me.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2026/01/08/events-in-december-2025/Screenshot_2025-12-31-00-17-03-48_3aea4af51f236e4932235fdada7d1643.jpg" data-size="1919x1080"&gt;
&lt;img src="https://carlschwan.eu/2026/01/08/events-in-december-2025/Screenshot_2025-12-31-00-17-03-48_3aea4af51f236e4932235fdada7d1643.jpg" width="1919" height="1080" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Backpacking in the Balkans - Part 2</title><link>https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/</link><pubDate>Mon, 06 Oct 2025 20:10:00 +0000</pubDate><guid>https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/</guid><description>&lt;p&gt;Better late than never! After quite a few requests, here’s the second part of my backpacking adventure through the Balkans from back in July. If you missed the first part, you can check it out &lt;a class="link" href="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After leaving Croatia, I traveled to the central Balkan countries: Bosnia and Herzegovina and Serbia.&lt;/p&gt;
&lt;h2 id="mostar"&gt;Mostar&lt;/h2&gt;
&lt;p&gt;Since there were no trains from Split to Mostar, I took the bus instead.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250715125754_20250927003212.LARGE.jpeg" alt="Stop on the middle of the road" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Stop on the middle of the road&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250715133704_20250927003212.LARGE.jpeg" alt="Landscape from the bus" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Landscape from the bus&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;In Mostar, I stayed at another Hostel called Taso&amp;rsquo;s House. It was a small hostel
but the host made us discover the whole Herzegovina region and explained us a lot
of things about his country, the complex political situation there and how it
was to grow up in Mostar during the war.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250715193441_20250927003212.LARGE.jpeg" alt="The old bridge of Mostar" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The old bridge of Mostar&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250715164412_20250927003212.LARGE.jpeg" alt="View from the old bridge" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;View from the old bridge&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716102744_20250927003212.LARGE.jpeg" alt="Bunker for airplanes build during the Yugoslavia time" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Bunker for airplanes build during the Yugoslavia time&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716110350_20250927003212.LARGE.jpeg" alt="Town next to Mostar called Blagaj" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Town next to Mostar called Blagaj&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716110507_20250927003212.LARGE.jpeg" alt="Another view of Blagaj" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Another view of Blagaj&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716115944_20250927003213.LARGE.jpeg" alt="Two rivers merging together and creating some waterfalls" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Two rivers merging together and creating some waterfalls&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716115958_20250927003213.LARGE.jpeg" alt="The same two rivers" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The same two rivers&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716124428_20250927003213.LARGE.jpeg" alt="Old Bosnian village" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Old Bosnian village&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716164231_20250927003213.LARGE.jpeg" alt="The Kravice Falls" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The Kravice Falls&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG-20250716-WA0024.LARGE.jpeg" alt="The group of people I spent my time in Mostar" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The group of people I spent my time in Mostar&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250716200212_20250927003213.LARGE.jpeg" alt="View from Mostar in the evening" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;View from Mostar in the evening&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250717101354_20250927003213.LARGE.jpeg" alt="Close view from the old bridge" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Close view from the old bridge&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;I only stayed there for two nights, and I regret not staying a bit longer or
visiting the genocide museum, which I’d heard great things about.&lt;/p&gt;
&lt;h2 id="sarajevo"&gt;Sarajevo&lt;/h2&gt;
&lt;p&gt;The next stop on my trip was Sarajevo, the capital of Bosnia and Herzegovina. I
booked a train ticket online, and this is what I got. It was slightly tricky to
import into KDE Itinerary, but the train ride itself was very enjoyable.
Interestingly, although the train has both first- and second-class coaches, all
tickets are sold as second class, and passengers are free to sit in the
first-class coaches — which I did. 😉&lt;/p&gt;
&lt;img src="IMG20250717160927_20250927004946.LARGE.jpeg" alt="train ticket printed" width="400"&gt;
&lt;p&gt;Sarajevo itself was beautiful, and I took the cable car up into the mountains
to visit the old Winter Olympics park. I even ended up appearing on the
&lt;a class="link" href="https://www.youtube.com/watch?app=desktop&amp;amp;v=AbGF974gJRo&amp;amp;t=71s" target="_blank" rel="noopener"
&gt;city&amp;rsquo;s television YouTube channel&lt;/a&gt;
as a sort of side quest, along with three Australians.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250718123244_20250927004946.LARGE.jpeg" alt="River in Sarajevo" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;River in Sarajevo&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250718141949_20250927004946.LARGE.jpeg" alt="Taking the cable car" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Taking the cable car&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250718151037_20250927004946.LARGE.jpeg" alt="Old Bobsleigh" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Old Bobsleigh&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250718154235_20250927004946.LARGE.jpeg" alt="Old destroyed building by the war" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Old destroyed building by the war&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250718155136_20250927004946.LARGE.jpeg" alt="View from the old destroyed building" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;View from the old destroyed building&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250718160858_01_20250927004946.LARGE.jpeg" alt="View of Sarajevo" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;View of Sarajevo&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250719191628_20250927004946.LARGE.jpeg" alt="A copper shop (there was a lot of them in Bosnia)" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;A copper shop (there was a lot of them in Bosnia)&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="belgrade"&gt;Belgrade&lt;/h2&gt;
&lt;p&gt;Afterward, I went to Serbia — more precisely, to the capital, Belgrade. I took
a night bus, which turned out to be a bit stressful. I almost missed it because
I didn’t have one euro in cash to pay a surprising platform fee to enter the bus
station. Then, at the border around 2 a.m., I was the only one who had to go
through a full luggage check.&lt;/p&gt;
&lt;p&gt;In Belgrade itself, I didn’t do much. I spent a lot of time at the hostel
talking with other travelers and petting the hostel cat. I was a bit shocked by
the amount of military propaganda I saw everywhere in the city.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250719203739_20250927010312.jpg" alt="Me before taking the bus" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Me before taking the bus&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250720191123_20250927010312.jpg" alt="Cool place in Belgrade full of bars" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Cool place in Belgrade full of bars&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250721112000_20250927010312.jpg" alt="The citadel. There was a massive amount of old war machines next to it." lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The citadel. There was a massive amount of old war machines next to it.&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250721121934_20250927010313.jpg" alt="Ice tea" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Ice tea&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250721124659_20250927010312.jpg" alt="Matcha &amp;lt;3" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Matcha &amp;lt;3&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250721143302_20250927010313.jpg" alt="A orthodox church" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;A orthodox church&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250721143831_20250927010313.jpg" alt="The crypt of orthodox church" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The crypt of orthodox church&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250722152424_20250927010313.jpg" alt="Soviet style monument about the second world war" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Soviet style monument about the second world war&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250722164242_20250927010313.jpg" alt="The cutest cat ever &amp;lt;3" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The cutest cat ever &amp;lt;3&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="niš"&gt;Niš&lt;/h2&gt;
&lt;p&gt;On my way to Bulgaria, I made a short stop in Niš, in southern Serbia. It’s one
of the largest cities in the country and has a strong Turkish influence.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250723134328_20251006212347.LARGE.jpeg" alt="Entrance of the old Turkish fortrest in Nis" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Entrance of the old Turkish fortrest in Nis&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250723134518_20251006212347.LARGE.jpeg" alt="The other side of the entrance of this fortrest" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The other side of the entrance of this fortrest&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250723141738_20251006212347.LARGE.jpeg" alt="Selfie of me :)" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Selfie of me :)&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="sofia"&gt;Sofia&lt;/h2&gt;
&lt;p&gt;My next stop was Sofia, the capital of Bulgaria. Since there are no longer any
passenger trains running between Niš and Sofia, I took an old bus from Niš
across the Serbian and Bulgarian countryside, but the ride was confortable.&lt;/p&gt;
&lt;p&gt;I only stayed one day in Sofia and scrolled the city with some people I
meet in my hostel.&lt;/p&gt;
&lt;p&gt;I visited the &amp;lsquo;&lt;a class="link" href="https://redflatsofia.com/" target="_blank" rel="noopener"
&gt;Red Flat&lt;/a&gt;&amp;rsquo;, which was a time capsule of how
a flat looked during the end of the comunism time. This was quite a retro experience.
I also ended up visiting more orthodox churches.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250723171505_20251006212643.LARGE.jpeg" alt="A train" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;A train&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250724104735_20251006212643.LARGE.jpeg" alt="Morning Matcha &amp;lt;3" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Morning Matcha &amp;lt;3&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250724115309_20251006212643.LARGE.jpeg" alt="The living room of the &amp;#39;Red Flat&amp;#39;" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The living room of the &amp;#39;Red Flat&amp;#39;&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250724120155_20251006212644.LARGE.jpeg" alt="The bedroom room of the &amp;#39;Red Flat&amp;#39;" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The bedroom room of the &amp;#39;Red Flat&amp;#39;&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250724151641_20251006212644.LARGE.jpeg" alt="Another gigantic orthodox church" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Another gigantic orthodox church&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="istanbul"&gt;Istanbul&lt;/h2&gt;
&lt;p&gt;The last stop on my trip was Istanbul. I took the night train there, which
turned out to be a bad experience — we ended up waiting four hours at the
border, and the whole process was quite inefficient.&lt;/p&gt;
&lt;p&gt;Istanbul itself was amazing. I had originally planned to stay only three days
before heading back to Bucharest, but I found a cheap flight (€50 with luggage)
back to Berlin, so I ended up staying a total of six days. I stayed at a hostel
called &lt;a class="link" href="https://hostelsh.com/" target="_blank" rel="noopener"
&gt;Second Home Hostel&lt;/a&gt;, which turned out to be a
really fun place. The staff were extremely friendly, I met a lot of amazing
people there, and we went out partying almost every night.&lt;/p&gt;
&lt;p&gt;I also meet &lt;a class="link" href="https://gruene.social/@jon" target="_blank" rel="noopener"
&gt;Jon Worth&lt;/a&gt; who was working on his
#CrossBorderRail project and was at the same time in Istanbul.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250724175452_20251006212644.LARGE.jpeg" alt="The night train I took" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The night train I took&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250725013048_20251006212644.LARGE.jpeg" alt="Me at 2 in the morning waiting for border check" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Me at 2 in the morning waiting for border check&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250725162044_20251006212644.LARGE.jpeg" alt="Me in front the Hagia Sophia" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Me in front the Hagia Sophia&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250725174048_20251006212645.LARGE.jpeg" alt="One more hostel cat :)" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;One more hostel cat :)&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726124710_01_20251006215612.LARGE.jpeg" alt="Small cute street" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Small cute street&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726124829_20251006215612.LARGE.jpeg" alt="Lot of olives" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Lot of olives&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726125517_20251006215612.LARGE.jpeg" alt="Nice food but I can&amp;#39;t remember what was inside, just that it was vegetarian and with some rice" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Nice food but I can&amp;#39;t remember what was inside, just that it was vegetarian and with some rice&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726154757_20251006215612.LARGE.jpeg" alt="More cute street" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;More cute street&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726161327_20251006215612.LARGE.jpeg" alt="Interior of a mosque" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Interior of a mosque&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726162020_20251006215612.LARGE.jpeg" alt="The spice market" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The spice market&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726165444_20251006215612.LARGE.jpeg" alt="I drank some chai so many times a day" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;I drank some chai so many times a day&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250726165815_20251006215612.LARGE.jpeg" alt="And there was cats everywhere" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;And there was cats everywhere&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250727130028_20251006215613.LARGE.jpeg" alt="Famous umbrella street" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Famous umbrella street&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250727162830_20251006215613.LARGE.jpeg" alt="Beach during a day trip" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Beach during a day trip&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250727203529_01_20251006215613.LARGE.jpeg" alt="Istanbul by night" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Istanbul by night&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250727204426_20251006215613.LARGE.jpeg" alt="View from a ferry to the other side of Istanbul" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;View from a ferry to the other side of Istanbul&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250727212428_20251006215613.LARGE.jpeg" alt="The group of people I hanged with during this trip" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The group of people I hanged with during this trip&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250728130422_20251006215613.LARGE.jpeg" alt="Prince island" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Prince island&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250728143619_20251006215613.LARGE.jpeg" alt="One of the biggest wood structure of the world: Büyükada Greek Orphanage" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;One of the biggest wood structure of the world: Büyükada Greek Orphanage&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250728151459_20251006215613.LARGE.jpeg" alt="The Blue Mosque" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The Blue Mosque&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250729124517_20251006215614.LARGE.jpeg" alt="The Blue Mosque inside" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The Blue Mosque inside&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG20250729135715_20251006215613.LARGE.jpeg" alt="Sunset" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Sunset&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG_20250729_202335_864.LARGE.jpeg" alt="Selfie on a ferry" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Selfie on a ferry&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/10/06/backpacking-in-the-balkans-part-2/IMG_20250729_202340_050.LARGE.jpeg" alt="Selfie after going to a hammam/turkish bath" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Selfie after going to a hammam/turkish bath&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;And now back to the reality but I am already looking forward to my next
backpacking trip.&lt;/p&gt;
&lt;script src="https://carlschwan.eu/swiper-bundle.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
document.querySelectorAll('.swiper').forEach((swiperElement) =&gt; {
console.log('centerd', swiperElement.dataset.centered ?? true)
let swiper = new Swiper(swiperElement, {
centeredSlides: swiperElement.dataset.centered ?? true,
slidesPerView: 'auto',
spaceBetween: 30,
loop: swiperElement.dataset.centered ?? false,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
})
&lt;/script&gt;</description></item><item><title>Kirigami Addons 1.10.0</title><link>https://carlschwan.eu/2025/09/27/kirigami-addons-1.10.0/</link><pubDate>Sat, 27 Sep 2025 10:10:00 +0000</pubDate><guid>https://carlschwan.eu/2025/09/27/kirigami-addons-1.10.0/</guid><description>&lt;p&gt;Kirigami Addons is a collection of supplementary components for Kirigami
applications. Version 1.10.0 is a relatively minor release, introducing KirigamiApp
and some improvements on Android regarding the new edge-to-edge support introduced in Android 15.&lt;/p&gt;
&lt;h2 id="new-features"&gt;New Features&lt;/h2&gt;
&lt;p&gt;Aleix Pol Gonzalez added a KirigamiApp component which removes quite a bit of boilerplate to setup a Kirigami applications.&lt;/p&gt;
&lt;p&gt;It now looks like this and will setup theming, crash reporting and more automatically in one place:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;KirigamiApp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;KirigamiApp&lt;/span&gt; &lt;span class="n"&gt;kapp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Set up KAboutData
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// QCommandLineParser creation and processing
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kapp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;org.kde.myapp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Main&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;QQmlApplicationEngine&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="bug-fixes"&gt;Bug fixes&lt;/h2&gt;
&lt;p&gt;Volker Krause added edge-to-edge support to the BottomDrawer and the MaximizedComponents.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.10.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Backpacking in the Balkans - Part 1</title><link>https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/</link><pubDate>Sat, 19 Jul 2025 16:10:00 +0000</pubDate><guid>https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/</guid><description>&lt;p&gt;I&amp;rsquo;m currently backpacking in the Balkans and, considering that it&amp;rsquo;s been such a
long time since I wrote a blog post on my blog, I figured it was a good
idea to write about it.&lt;/p&gt;
&lt;p&gt;As I am traveling, I am also field testing &lt;a class="link" href="https://apps.kde.org/itinerary" target="_blank" rel="noopener"
&gt;KDE
Itinerary&lt;/a&gt; and sending patches as I buy new my tickets and reserve my hostels.&lt;/p&gt;
&lt;img width="400" style="margin-left: 20px" src="trip.jpeg" &gt;
&lt;h2 id="ljubiana-slovenia"&gt;Ljubiana, Slovenia&lt;/h2&gt;
&lt;p&gt;My first stop was in the capital of Slovenia, Ljubljana. I went there by train
from Berlin. I first took the night train to Graz and then an intercity train
to Ljubljana. And while the connection was perfect and there was no delay, the
whole journey took almost 19 hours.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/nighttrain1.jpeg" alt="Night Jet" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Night Jet&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/nighttrain2.jpeg" alt="Sleeping car in the night jet" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Sleeping car in the night jet&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;I stayed only one night in Ljubljana in the party hostel Zzz and I enjoyed my time
there quite a bit. Thanks to the &lt;a class="link" href="https://www.hostelworld.com/" target="_blank" rel="noopener"
&gt;Hostelworld&lt;/a&gt;
app, it was super easy to find a group of fellow solo travelers to enjoy the
evening with. We went to a food market and I had some delicious local pasta.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/ljubiana2.jpeg" alt="Street in Ljubiana" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Street in Ljubiana&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/ljubiana4.jpeg" alt="Castel" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Castel&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/ljubiana1.jpeg" alt="Food market" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Food market&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/ljubiana3.jpeg" alt="Another view of the food market" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Another view of the food market&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/ljubianafood1.jpeg" alt="The food at the food market" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The food at the food market&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/ljubianafood2.jpeg" alt="The food at the food market" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The food at the food market&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="rijeka-croatia"&gt;Rijeka, Croatia&lt;/h2&gt;
&lt;p&gt;My second stop was in Rijeka, the third biggest city in Croatia. I also took the
train to get there. The city itself is very beautiful, same for the beaches.
But I didn&amp;rsquo;t really like that the city has a massive sea port splitting the
city center from the beaches. The hostel experience was the most disapointing of
the whole trip so far.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/rijeka3.jpeg" alt="Small port of Rijeka" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Small port of Rijeka&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/rijeka4.jpeg" alt="Small stairs going to a swimming spot in Rijeka" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Small stairs going to a swimming spot in Rijeka&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/rijeka1.jpeg" alt="Sunset from the swimming spot" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Sunset from the swimming spot&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/rijeka5.jpeg" alt="The sea" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The sea&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="split-croatia"&gt;Split, Croatia&lt;/h2&gt;
&lt;p&gt;My next stop was in Split, the second largest city in Croatia. While there was
a train connection from Rijeka to Split, I decided to take the bus as the train
was significantly slower than the bus. I stayed at the Hostel Old Town.&lt;/p&gt;
&lt;p&gt;I really had a blast in Split. The city is very old, with Greek, Roman,
Byzantine, and Venetian influence. I met a cool group of American/Norwegian travelers in
a hostel bar and got dragged to the Ultra festival, which was an amazing
experience.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/split1.jpeg" alt="Small allway in the old town of Split" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Small allway in the old town of Split&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/split2.jpeg" alt="Old fortification" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Old fortification&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/split4.jpeg" alt="Small town scare" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Small town scare&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/split5.jpeg" alt="Another old building" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Another old building&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/split3.jpeg" alt="Picture of the festival" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Picture of the festival&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/splitboat4.jpeg" alt="Group picture of the people I meet at the festival" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Group picture of the people I meet at the festival&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/split7.jpeg" alt="Chilling out the day after the festival in the shades of a tree" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Chilling out the day after the festival in the shades of a tree&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;The day after, I went to a boat party which was equally a blast.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/splitboat2.jpeg" alt="Boat party" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Boat party&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/splitboat1.jpeg" alt="Boat party with the sunset" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Boat party with the sunset&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/splitboat3.jpeg" alt="Drinking a reasonable amount of Aperol" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Drinking a reasonable amount of Aperol&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/splitboat5.jpeg" alt="Dancing with a stick" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Dancing with a stick&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/splitboat6.jpeg" alt="More dancing" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;More dancing&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2025/07/19/backpacking-in-the-balkans-part-1/splitboat7.jpeg" alt="Group photo at the end" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Group photo at the end&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;script src="https://carlschwan.eu/swiper-bundle.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
document.querySelectorAll('.swiper').forEach((swiperElement) =&gt; {
console.log('centerd', swiperElement.dataset.centered ?? true)
let swiper = new Swiper(swiperElement, {
centeredSlides: swiperElement.dataset.centered ?? true,
slidesPerView: 'auto',
spaceBetween: 30,
loop: swiperElement.dataset.centered ?? false,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
})
&lt;/script&gt;</description></item><item><title>Kirigami Addons 1.9.0</title><link>https://carlschwan.eu/2025/07/06/kirigami-addons-1.9.0/</link><pubDate>Sun, 06 Jul 2025 19:10:00 +0000</pubDate><guid>https://carlschwan.eu/2025/07/06/kirigami-addons-1.9.0/</guid><description>&lt;p&gt;Kirigami Addons is a collection of supplementary components for Kirigami
applications. Version 1.9.0 is a relatively minor release, introducing two new
form delegates along with various quality-of-life enhancements.&lt;/p&gt;
&lt;h2 id="new-features"&gt;New Features&lt;/h2&gt;
&lt;p&gt;I took over the work from Tomasz Bojczuk and finished the addition of the file and folder form delegate.&lt;/p&gt;
&lt;p&gt;These two components wrap a FileDialog and FolderDialog respectively and like KUrlRequester in KIO provide a text field with autocompletion on desktop. Currently the autocompletion is a bit basic and is based on a Controls.TextField with a Controls.Popup, but hopefully with Qt 6.10 we can use Controls.SearchField.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2025/07/06/kirigami-addons-1.9.0/formfiledelegate.png" data-size="636x879"&gt;
&lt;img src="https://carlschwan.eu/2025/07/06/kirigami-addons-1.9.0/formfiledelegate.png" width="636" height="879" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="bug-fixes"&gt;Bug fixes&lt;/h2&gt;
&lt;p&gt;Outside of these two new components, this release includes minor fixes and improvements from Antonio Rojas, Nicolas Fella Soumyadeep Ghosh, Thiago Sueto, Volker Krause and Yuki Joou.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.9.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.8.0</title><link>https://carlschwan.eu/2025/05/19/kirigami-addons-1.8.0/</link><pubDate>Mon, 19 May 2025 21:30:00 +0000</pubDate><guid>https://carlschwan.eu/2025/05/19/kirigami-addons-1.8.0/</guid><description>&lt;p&gt;Kirigami Addons is a collection of supplementary components for Kirigami
applications. Version 1.8.0 is a relatively minor release, introducing two new
form delegates along with various quality-of-life enhancements.&lt;/p&gt;
&lt;h2 id="new-features"&gt;New Features&lt;/h2&gt;
&lt;p&gt;I added two new form delegates: &lt;code&gt;FormLinkDelegate&lt;/code&gt; (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/343" target="_blank" rel="noopener"
&gt;!343&lt;/a&gt;) and &lt;code&gt;FormIconDelegate&lt;/code&gt; (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/355" target="_blank" rel="noopener"
&gt;!355&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The first one is similar to &lt;code&gt;FormButtonDelegate&lt;/code&gt;, but it&amp;rsquo;s used to display an external link. It&amp;rsquo;s already used on the About page:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2025/05/19/kirigami-addons-1.8.0/aboutpage.png" data-size="1032x699"&gt;
&lt;img src="https://carlschwan.eu/2025/05/19/kirigami-addons-1.8.0/aboutpage.png" width="1032" height="699" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The second one was upstreamed from Marknote and allows the user to pick an icon and display the selected icon.&lt;/p&gt;
&lt;p&gt;I also added a password quality checker to &lt;code&gt;FormPasswordFieldDelegate&lt;/code&gt; (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/345" target="_blank" rel="noopener"
&gt;!345&lt;/a&gt;). This is particularly useful when asking users to create an account:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2025/05/19/kirigami-addons-1.8.0/password-checker.png" data-size="990x720"&gt;
&lt;img src="https://carlschwan.eu/2025/05/19/kirigami-addons-1.8.0/password-checker.png" width="990" height="720" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="visual-changes"&gt;Visual Changes&lt;/h2&gt;
&lt;p&gt;Kai Uwe Broulik improved avatar rendering. Initials are now always displayed consistently even on small screen (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/363" target="_blank" rel="noopener"
&gt;!363&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Kai also fixed an issue on mobile where library information on the About page was being ellipsized (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/356" target="_blank" rel="noopener"
&gt;!356&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Balló György fixed several issues when using Kirigami with the QtQuick software rendering backend (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/350" target="_blank" rel="noopener"
&gt;!350&lt;/a&gt;, &lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/351" target="_blank" rel="noopener"
&gt;!351&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I made the delegates provided by Kirigami Addons now have a slightly larger touch area on mobile (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/349" target="_blank" rel="noopener"
&gt;!349&lt;/a&gt;). Unfortunately, I also had to remove the small hover animations, as they occasionally caused visual glitches (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/commit/1d6e84cd0560d60718d37e252f173471347f1e33" target="_blank" rel="noopener"
&gt;1d6e84cd&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id="convenient-new-apis"&gt;Convenient New APIs&lt;/h2&gt;
&lt;p&gt;Joshua Goins added an &lt;code&gt;opened&lt;/code&gt; property to &lt;code&gt;ConvergentContextMenu&lt;/code&gt; (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/352" target="_blank" rel="noopener"
&gt;!352&lt;/a&gt;), and I added a &lt;code&gt;close&lt;/code&gt; method to allow closing the menu programmatically (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/364" target="_blank" rel="noopener"
&gt;!364&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I also added support for &lt;code&gt;trailing&lt;/code&gt; items in &lt;code&gt;FormTextFieldDelegate&lt;/code&gt; (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/commit/f996fc6e68303d3ab0cdc76f6f4f50e59b17cc96" target="_blank" rel="noopener"
&gt;f996fc6e&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id="documentation"&gt;Documentation&lt;/h2&gt;
&lt;p&gt;Thiago Sueto ported the entire library to QDoc (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/354" target="_blank" rel="noopener"
&gt;!354&lt;/a&gt;). QDoc provides much better support for QML.&lt;/p&gt;
&lt;h2 id="other-changes"&gt;Other Changes&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;trapped-in-dreams&amp;rdquo; significantly improved the performance of the date picker (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/360" target="_blank" rel="noopener"
&gt;!360&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Volker Krause updated the project templates to reflect current best practices for Android support (&lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/359" target="_blank" rel="noopener"
&gt;!359&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;1.8.0 had an issue with system not having QDoc, but a bug fix release is available as 1.8.1 with the fix for that.&lt;/p&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.8.1.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.7.0</title><link>https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/</link><pubDate>Thu, 16 Jan 2025 14:25:00 +0000</pubDate><guid>https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/</guid><description>&lt;p&gt;Kirigami Addons is a collection of additional components for Kirigami
applications. 1.7.0 is a relatively big release bringing a new convergent
component for context menus as well as various quality of life APIs to existing
components.&lt;/p&gt;
&lt;h2 id="convergentcontextmenu"&gt;ConvergentContextMenu&lt;/h2&gt;
&lt;p&gt;This release bring a new component which wraps the tradional context menu
&lt;code&gt;Controls.Menu&lt;/code&gt; provided by Qt and on mobile will instead displays a &lt;code&gt;BottomDrawer&lt;/code&gt;
with the list of actions.&lt;/p&gt;
&lt;p&gt;Using it, is really easy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Controls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigami&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Components&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formcard&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;FormCard&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConvergentContextMenu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Only visible on mobile to show a bit of information about the selected element
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;headerContentItem:&lt;/span&gt; &lt;span class="nx"&gt;RowLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;spacing:&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Units&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smallSpacing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Avatar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Heading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;level:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Room Name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:inmenu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Simple Action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:inmenu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Nested Action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:inmenu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Nested Action with Multiple Choices&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:inmenu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Follow Global Settings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;checkable:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;autoExclusive:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// Since KF 6.10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:inmenu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;checkable:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;autoExclusive:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// Since KF 6.10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:inmenu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Disabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;checkable:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;autoExclusive:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// Since KF 6.10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// custom FormCard delegate only supported on mobile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;visible:&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;displayComponent:&lt;/span&gt; &lt;span class="nx"&gt;FormCard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormButtonDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://blogs.kde.org/2025/01/05/this-week-in-kde-apps-mobile-context-menus/neochat1.png" alt="" /&gt;&lt;/p&gt;
&lt;h2 id="icons-on-android"&gt;Icons on Android&lt;/h2&gt;
&lt;p&gt;Kirigami Addons components are using some breeze icons which needs to be packaged
manually on android by calling &lt;code&gt;kirigami_package_breeze_icons&lt;/code&gt; with the icons used.
Now Kirigami Addons, provides a Cmake variable &lt;code&gt;KIRIGAMI_ADDONS_ICONS&lt;/code&gt; listing all
the icons used by Kirigami Addons, simplifying the maintainance work of applications
to keep the list of icons used up-to-date.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmake" data-lang="cmake"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;kirigami_package_breeze_icons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;ICONS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KIRIGAMI_ADDONS_ICONS&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;//&lt;/span&gt; &lt;span class="s"&gt;own&lt;/span&gt; &lt;span class="s"&gt;icons&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="shortcut-editor"&gt;Shortcut Editor&lt;/h2&gt;
&lt;p&gt;Kirigami Addons&amp;rsquo; shortcut editor can now be embedded in normal &lt;code&gt;ConfigurationView&lt;/code&gt; via a new &lt;code&gt;ConfigurationModule&lt;/code&gt;: &lt;code&gt;ShortcutsConfigurationModule&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConfigurationView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="nx"&gt;TokodonApplication&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;modules:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ShortcutsConfigurationModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;application:&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/shortcuts.png" data-size="1032x699"&gt;
&lt;img src="https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/shortcuts.png" width="1032" height="699" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="formcard"&gt;FormCard&lt;/h2&gt;
&lt;p&gt;The FormCardPage now uses a slighly less grey to get more contrasts with the sidebar.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/kdeconnect.png" data-size="1129x799"&gt;
&lt;img src="https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/kdeconnect.png" width="1129" height="799" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We cleaned up &lt;code&gt;FormComboBoxDelegate&lt;/code&gt; to not relly on the &lt;code&gt;applicationWindow()&lt;/code&gt;
hack from Kirigami anymore. This fixes using &lt;code&gt;FormComboBoxDelegate&lt;/code&gt; in Plasma
Settings. Unfortunately some areas of Kirigami Addons still implicitely rely on
&lt;code&gt;applicationWindow()&lt;/code&gt; to set the parent of popups (Kirigami has a similar
issue). If you are using dialogs or popup in your code, make sure to
explicitely pass a valid &lt;code&gt;Controls.Overlay.overlay&lt;/code&gt; as parent to them instead
of rellying on &lt;code&gt;applicationWindow()&lt;/code&gt; being valid all the time.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;FormCard.AboutPage&lt;/code&gt; now show the KDE Frameworks version in use rather than
the one we built against. We are also using in the &lt;code&gt;AboutKDEPage&lt;/code&gt; component,
the same bug address as in the &lt;code&gt;AboutPage&lt;/code&gt; component. And we fixed various other small
issues with the about pages. Thanks Volker and Joshua!&lt;/p&gt;
&lt;h2 id="other"&gt;Other&lt;/h2&gt;
&lt;p&gt;We now use clang-format automatically and various clang-tidy warnings were fixed. Thanks Alex!&lt;/p&gt;
&lt;p&gt;Avatar are now loaded asynchronously which should make NeoChat, Tokodon and Merkuro list views smoother. Thank Kai! Addionally the text fallback is now only rendered as plain text, which should also be sligly faster.&lt;/p&gt;
&lt;p&gt;The RadioSelector now uses the style from Marknote.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/radioselector.png" data-size="529x254"&gt;
&lt;img src="https://carlschwan.eu/2025/01/16/kirigami-addons-1.7.0/radioselector.png" width="529" height="254" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We updated the templates provided by Kirigami Addons to the latest version of the flatpak
runtimes and some other minor improvements like using the new &lt;code&gt;KLocalizedQmlContext&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AlbumMaximizeComponent&lt;/code&gt; now expose not only the currentItem from the internal
view, but also the currentIndex.&lt;/p&gt;
&lt;p&gt;The IndicatorItemDelegate and RoundedItemDelegate components are now easier to
use with drag and drop interaction. You can see that in effect in last week update
of &lt;a class="link" href="https://blogs.kde.org/2025/01/12/this-week-in-kde-apps-usability-improvements-new-features-and-updated-apps/#merkuro.mail" target="_blank" rel="noopener"
&gt;Merkuro Mail&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;MessageDialog now behaves better on mobile.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.7.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.6.0</title><link>https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/</link><pubDate>Sat, 30 Nov 2024 14:30:00 +0000</pubDate><guid>https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/</guid><description>&lt;p&gt;Kirigami Addons is a collection of additional components for Kirigami
applications. This release brings mostly improvements to the &lt;code&gt;FormCard&lt;/code&gt; module.&lt;/p&gt;
&lt;h2 id="aboutpage"&gt;AboutPage&lt;/h2&gt;
&lt;p&gt;The about page provided by Kirigami Addons received many improvements. Joshua
added icons to all the buttons.&lt;/p&gt;
&lt;p&gt;I worked on the component section, which now contains more information about the
default components as well as the underlying platform and now has a button to
copy all this information to the clipboard. This is super helpful, when writing
a bug report. There were also some small bug fixes with, for example, the
license dialog being correctly sized.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/aboutpage.png" data-size="1090x1022"&gt;
&lt;img src="https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/aboutpage.png" width="1090" height="1022" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="radioselector"&gt;RadioSelector&lt;/h2&gt;
&lt;p&gt;A new component is the RadioSelector, which is a simple component that allows one to
choose an option between two or more choices in a horizontal layout. This is
not a new component as it has already been used in Itinerary and Marknote for a
long time.&lt;/p&gt;
&lt;p&gt;There is also a &lt;code&gt;FormCard&lt;/code&gt; version of this, called &lt;code&gt;FormRadioSelectorDelegate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/radioselector.png" data-size="583x334"&gt;
&lt;img src="https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/radioselector.png" width="583" height="334" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="formplaceholdermessagedelegate"&gt;FormPlaceholderMessageDelegate&lt;/h2&gt;
&lt;p&gt;Another new component is &lt;code&gt;FormPlaceholderMessageDelegate&lt;/code&gt;, which is basically a
&lt;code&gt;Kirigami.PlaceholderMessage&lt;/code&gt;, but instead of putting it in a &lt;code&gt;ListView&lt;/code&gt;, this one
is to be put inside a &lt;code&gt;FormCard&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/placeholdermessage.png" data-size="552x273"&gt;
&lt;img src="https://carlschwan.eu/2024/11/30/kirigami-addons-1.6.0/placeholdermessage.png" width="552" height="273" loading="lazy"
alt="FormPlaceholderMessageDelegate for the health certificate"&gt;
&lt;/a&gt;
&lt;figcaption&gt;FormPlaceholderMessageDelegate for the health certificate&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="other"&gt;Other&lt;/h2&gt;
&lt;p&gt;Volker fixed the Android integration of the date picker. He also added support for
static builds (required for iOS and probably hopeful for other platforms).&lt;/p&gt;
&lt;p&gt;Claudio fixed various issues with the DatePicker.&lt;/p&gt;
&lt;p&gt;Joshua made the caption used in &lt;code&gt;AlbumMaximizeComponent&lt;/code&gt; selectable with the
mouse. He also fixed the separator for the &lt;code&gt;IndicatorItemDelegate&lt;/code&gt; which only
appeared after the first item.&lt;/p&gt;
&lt;p&gt;I added icon support to &lt;code&gt;FormSwitchDelegate&lt;/code&gt;, which is similar to what we
already have in &lt;code&gt;FormRadioDelegate&lt;/code&gt; and &lt;code&gt;FormCheckDelegate&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;Kirigami Addons 1.6.0 was tagged but the tarball are not yet available. I will update
this post once it is available.&lt;/p&gt;</description></item><item><title>OptiImage 1.0.0 is out!</title><link>https://carlschwan.eu/2024/11/30/optiimage-1.0.0-is-out/</link><pubDate>Sat, 30 Nov 2024 14:30:00 +0000</pubDate><guid>https://carlschwan.eu/2024/11/30/optiimage-1.0.0-is-out/</guid><description>&lt;p&gt;The first release of OptiImage is finally out! OptiImage is a useful image
compressor that supports PNG, JPEG, WebP and SVG file types. It doesn&amp;rsquo;t do the
compression itself but uses various tools like &lt;code&gt;oxipng&lt;/code&gt; to do the compression.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/11/30/optiimage-1.0.0-is-out/optiimage2.png" data-size="732x879"&gt;
&lt;img src="https://carlschwan.eu/2024/11/30/optiimage-1.0.0-is-out/optiimage2.png" width="732" height="879" loading="lazy"
alt="OptiImage compressing screenshots"&gt;
&lt;/a&gt;
&lt;figcaption&gt;OptiImage compressing screenshots&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/11/30/optiimage-1.0.0-is-out/settings.png" data-size="732x879"&gt;
&lt;img src="https://carlschwan.eu/2024/11/30/optiimage-1.0.0-is-out/settings.png" width="732" height="879" loading="lazy"
alt="OptiImage’s settings page"&gt;
&lt;/a&gt;
&lt;figcaption&gt;OptiImage’s settings page&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Thanks to Mathis Brüchert for his work on the icon and to Soumyadeep Ghosh for
a bunch of bug fixes and pushing me to do the release.&lt;/p&gt;
&lt;img src="https://apps.kde.org/app-icons/org.kde.optiimage.svg" alt="" width=128 height=128 /&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;OptiImage 1.0.0 was tagged but the tarball are not yet available. I will update
this post once it is available.&lt;/p&gt;
&lt;!--
You can find the package on
[download.kde.org](https://download.kde.org/stable/optiimage/optiimage-1.0.0.tar.xz.mirrorlist)
and it has been signed with my [GPG key](/gpg-02325448204e452a/).
--&gt;</description></item><item><title>Kirigami Addons 1.5</title><link>https://carlschwan.eu/2024/10/10/kirigami-addons-1.5/</link><pubDate>Thu, 10 Oct 2024 11:30:00 +0000</pubDate><guid>https://carlschwan.eu/2024/10/10/kirigami-addons-1.5/</guid><description>&lt;p&gt;Kirigami Addons is out. This releases contains mostly code cleanup and minor
improvements. There is netherless a few relevant changes. Thanks to everyone
who contributed some code.&lt;/p&gt;
&lt;h2 id="new-kapptemplates-template"&gt;New KAppTemplate&amp;rsquo;s template&lt;/h2&gt;
&lt;p&gt;A new KAppTemplate is available as a good starting point for application that
manage multimedia libraries. It is based on shared design of
&lt;a class="link" href="http://apps.kde.org/peruse" target="_blank" rel="noopener"
&gt;Peruse&lt;/a&gt;,
&lt;a class="link" href="https://apps.kde.org/arianna/" target="_blank" rel="noopener"
&gt;Arianna&lt;/a&gt; and the WIP Calligra Launcher.&lt;/p&gt;
&lt;p&gt;Hopefully it helps people who want to develop game launchers and other type of
specialized multimedia applications.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/10/10/kirigami-addons-1.5/kirigami-app.png" data-size="1226x866"&gt;
&lt;img src="https://carlschwan.eu/2024/10/10/kirigami-addons-1.5/kirigami-app.png" width="1226" height="866" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;More templates are planned (e.g. for chat applications), so stay tunned!&lt;/p&gt;
&lt;h2 id="formcard"&gt;FormCard&lt;/h2&gt;
&lt;p&gt;FormCard is the part of Kirigami Addons that received the most changes in this
release. First of all, FormCard now use more consistent spacing and padding,
which slighly less horizontal padding. Descriptions for radio and checkbox
delegates are also put underneath the delegate&amp;rsquo;s main text and checkbox, in an
effort to make FormCard a bit more compact.&lt;/p&gt;
&lt;img-comparison-slider class="max-width-800" value="20" &gt;
&lt;div slot="first" class="img-comparison-figure-before"&gt;&lt;div&gt;Before&lt;/div&gt;&lt;img src="marble-behaim-before.png" alt="" loading="lazy"/&gt;
&lt;/div&gt;
&lt;div slot="second" class="img-comparison-figure-after"&gt;&lt;div&gt;After&lt;/div&gt;&lt;img src="marble-behaim.png" alt="" loading="lazy"/&gt;
&lt;/div&gt;
&lt;svg slot="handle" class="img-comparison-figure-handle" xmlns="http://www.w3.org/2000/svg" width="100" viewBox="-8 -3 16 6"&gt;
&lt;path d="M -5 -2 L -7 0 L -5 2 M -5 -2 L -5 2 M 5 -2 L 7 0 L 5 2 M 5 -2 L 5 2" fill="currentColor"/&gt;
&lt;/svg&gt;
&lt;/img-comparison-slider&gt;
&lt;p&gt;Additionally &lt;code&gt;FormComboBoxDelegate&lt;/code&gt; now lets you display an inline status
similar to that is available in other FormCard&amp;rsquo;s delegates.&lt;/p&gt;
&lt;p&gt;Finally &lt;code&gt;FormCard.AboutKDE&lt;/code&gt; was renamed to &lt;code&gt;FormCard.AboutKDEPage&lt;/code&gt;. This improve
the naming consistency with other page compoenents. A compatibility wrapper on
top of &lt;code&gt;AboutKDEPage&lt;/code&gt; named &lt;code&gt;AboutKDE&lt;/code&gt; is still available to not break any
existing applications.&lt;/p&gt;
&lt;h2 id="deprecations"&gt;Deprecations&lt;/h2&gt;
&lt;p&gt;The Banner component is now deprecated. &lt;code&gt;Kirigami.InlineMessage&lt;/code&gt; now has a
&lt;code&gt;position&lt;/code&gt; parameter which can be set to &lt;code&gt;Header&lt;/code&gt; or &lt;code&gt;Footer&lt;/code&gt;. Additionally
with KDE Frameworks 6.8 &lt;code&gt;Kirigami.InlineMessage&lt;/code&gt; will look exactly the same as
Banner! So there is no more reasons for this component to exists
in Kirigami Addons.&lt;/p&gt;
&lt;h2 id="other"&gt;Other&lt;/h2&gt;
&lt;p&gt;Kirigami Addons supports &lt;a class="link" href="https://invent.kde.org/libraries/kirigami-addons/-/merge_requests/275" target="_blank" rel="noopener"
&gt;static builds&lt;/a&gt; with a recent enough version of extra-cmake-modules.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.5.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Open Source Conferences in September</title><link>https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/</link><pubDate>Sun, 29 Sep 2024 18:00:35 +0000</pubDate><guid>https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/</guid><description>&lt;p&gt;After having participated to both &lt;a class="link" href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/" &gt;Qt Contributor Summit and Akademy&lt;/a&gt;,
I ended up going to a few more conferences in September.&lt;/p&gt;
&lt;h2 id="nextcloud-conference"&gt;Nextcloud Conference&lt;/h2&gt;
&lt;p&gt;I went to Nextcloud Conference just after going back from Akademy.
Unfortunately I was quite tired from Akademy and Qt Contributor Summit and I
only stayed Saturday morning. Still it was great to meet some old colleagues
there.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/nextcloud-conference-2024-group-photo.jpg" data-size="700x351"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/nextcloud-conference-2024-group-photo.jpg" width="700" height="351" loading="lazy"
alt="Group photo of the Nextcloud conference"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Group photo of the Nextcloud conference&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="matrix-conference"&gt;Matrix Conference&lt;/h2&gt;
&lt;p&gt;The &lt;a class="link" href="https://2024.matrix.org/" target="_blank" rel="noopener"
&gt;Matrix Conference&lt;/a&gt; happened the weekend after
the Nextcloud conference. This was the first Matrix Conference and a gathering
of all types of actors involved in Matrix. From the grassroots community to
companies deploying Matrix based solutions to their customers. The NeoChat team
was there and we were super productive into bringing back the Android version,
thanks to the help of Volker Krause. This resulted in &lt;a class="link" href="https://blogs.kde.org/2024/09/22/this-week-in-kde-apps/#neochat" target="_blank" rel="noopener"
&gt;many
patches&lt;/a&gt; in
NeoChat itself but also &lt;a class="link" href="https://invent.kde.org/frameworks/kirigami/-/merge_requests/1617" target="_blank" rel="noopener"
&gt;one patch in
Kirigami&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Special mention to the food and coffee offered at the conference, which was
always excellent and either vegan or vegetarian. Outside of the venue, food
was also execellent with a lot of middle eastern food choice.&lt;/p&gt;
&lt;p&gt;Here some photos of the event and the food:&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240920_160147.jpg" alt="Donuts" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Donuts&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240919_194416.jpg" alt="Hacking at the conference venue" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Hacking at the conference venue&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240920_231801.jpg" alt="Hacking at C-Base" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Hacking at C-Base&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240921_123544.jpg" alt="Food outside of the venue" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Food outside of the venue&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="linux-days-dornbirn"&gt;Linux Days Dornbirn&lt;/h2&gt;
&lt;p&gt;It was my &lt;a class="link" href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/" &gt;second time going to the Linux Days in Dornbirn&lt;/a&gt;
and while the weather wasn&amp;rsquo;t as welcoming as last time, the local Linux
community was again super welcoming!&lt;/p&gt;
&lt;p&gt;During the event, I did a talk in German about Plasma 6. This was my first time
doing a talk in German and I hope I did okay. I also hosted a KDE stand with
&lt;a class="link" href="https://mastodon.social/@LibreKitsune" target="_blank" rel="noopener"
&gt;Simon Österle&lt;/a&gt;, who offered his help
with the stand. His help has been invaluable to me, so huge thanks to him.&lt;/p&gt;
&lt;p&gt;Like last year, after the conference, all helpers, presenters and stand holders went to a local
restaurent to enjoy Käsespätzle. It was again delicious.&lt;/p&gt;
&lt;p&gt;Here some photos of the event and the food:&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240928_083144.jpg" alt="Banner at the entrance of the Linux Days" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Banner at the entrance of the Linux Days&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240928_094710.jpg" alt="Steam Deck and Plasma Mobile" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Steam Deck and Plasma Mobile&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240928_095048.jpg" alt="The new KDE Banner" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;The new KDE Banner&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240928_191030.jpg" alt="Linux Days dinner menu" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Linux Days dinner menu&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240928_202014.jpg" alt="Käsespätzle" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Käsespätzle&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240928_213525.jpg" alt="Apfelstrudel" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Apfelstrudel&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240927_160435.jpg" alt="Bodensee on the way to Austria" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Bodensee on the way to Austria&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2024/09/29/open-source-conferences-in-september/IMG_20240929_115605.jpg" alt="Bodensee from the train on the way back" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Bodensee from the train on the way back&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;script src="https://carlschwan.eu/swiper-bundle.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
document.querySelectorAll('.swiper').forEach((swiperElement) =&gt; {
console.log('centerd', swiperElement.dataset.centered ?? true)
let swiper = new Swiper(swiperElement, {
centeredSlides: swiperElement.dataset.centered ?? true,
slidesPerView: 'auto',
spaceBetween: 30,
loop: swiperElement.dataset.centered ?? false,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
})
&lt;/script&gt;</description></item><item><title>Qt Contributor Summit and Akademy</title><link>https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/</link><pubDate>Sat, 14 Sep 2024 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/</guid><description>&lt;p&gt;This year I went to Würzburg, which is a nice small German city famous for its
wine. But I didn&amp;rsquo;t only go there for the wine, but also to attend Qt
Contributor Summit and Akademy.&lt;/p&gt;
&lt;h2 id="qt-contributor-summit"&gt;Qt Contributor Summit&lt;/h2&gt;
&lt;p&gt;The travel to Würzburg didn&amp;rsquo;t go as planned as Deutsch Bahn had some technical
issues with their train and couldn&amp;rsquo;t reboot our train. We still managed to get
in Würzburg on time and even had the change to get a small touristic tour from
some locals.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/wurzburg.jpg" data-size="1338x837"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/wurzburg.jpg" width="1338" height="837" loading="lazy"
alt="Würzburg Residence and the Wine briget"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Würzburg Residence and the Wine briget&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The event itself was great and was the first time I attended fully a Qt
Contributor Summit. Last year, I only attended a few session since the event was
20 min away from home.&lt;/p&gt;
&lt;p&gt;There was many breakout rooms focused on some spcial
topics, for me the most interesting sessions were about
&lt;a class="link" href="https://wiki.qt.io/QtCS2024_Qt_for_Python" target="_blank" rel="noopener"
&gt;Qt for Python&lt;/a&gt;,
&lt;a class="link" href="https://wiki.qt.io/QtCS2024_How_to_hate_QML" target="_blank" rel="noopener"
&gt;how to hate QML&lt;/a&gt;,
&lt;a class="link" href="https://wiki.qt.io/QtCS2024_qt-project.org" target="_blank" rel="noopener"
&gt;qt-project.org&lt;/a&gt;,
&lt;a class="link" href="https://wiki.qt.io/QtCS2024_Vector_Graphics" target="_blank" rel="noopener"
&gt;Vector Graphics in Qt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It was great to see how the KDE community still plays a big role in Qt and the
Qt developers really appreciated what KDE finally moved to Qt6. They reported
that the flow of contributors and bug reports increased.&lt;/p&gt;
&lt;p&gt;Qt Chief Maintainer Volker Hilsheimer even stressed out how important it was
for some of their customers to see KDE ported to Qt6, as it shows what Qt6
is stable and mature enough. Qt6 is indeed a hugo improvement over Qt5 and I am
very happy how good the transition was.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/qt-contributor.jpg" data-size="5712x4284"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/qt-contributor.jpg" width="5712" height="4284" loading="lazy"
alt="Qt Contributor Summit"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Qt Contributor Summit&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I think it was a great idea to have Qt Contributor Summit just before Akademy.
This allowed to have many KDE Contributor to the Qt Contributor Summit and many
Qt developers to Akademy. It would be great next year to do the same next if
possible and encourage more people from the Open Source Qt ecosystem to join
too.&lt;/p&gt;
&lt;h2 id="akademy"&gt;Akademy&lt;/h2&gt;
&lt;p&gt;Once the Qt Contributor Summit ended, we started a few hours later with the
Akademy welcome event for some KDE beers. But before that, I had some bubble tea
and spent some time with some friends exploring the city.&lt;/p&gt;
&lt;p&gt;The weekend was full with a lot of great presentations. I presented a small
report about the Accessibility Goal and the Fundraising working group. I also
gave a bigger talk about the KDE Application Ecosystem, which I am really
passionate about. The whole slides ware made with Calligra.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/me3.jpg" data-size="1024x1538"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/me3.jpg" width="1024" height="1538" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I was also very happy to see the &lt;a class="link" href="https://kde.org/goals/" target="_blank" rel="noopener"
&gt;new elected goals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sunday was also my birthday, thanks to everyone for congratulating me. I also
received a super fancy special birthday sticker and some amazing cake.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/cake.png" data-size="1302x1120"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/cake.png" width="1302" height="1120" loading="lazy"
alt="Cake and stickers"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Cake and stickers&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="day-trip"&gt;Day Trip&lt;/h2&gt;
&lt;p&gt;We had our yearly day trip too, this time at Rothenburg ob der Tauber. A
charming small town in south Germany.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/daytrip.jpg" data-size="1816x939"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/daytrip.jpg" width="1816" height="939" loading="lazy"
alt="Day trip Rothenburg ob der Tauber"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Day trip Rothenburg ob der Tauber&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="bofs"&gt;BoFs&lt;/h2&gt;
&lt;p&gt;The next few days were filled with BoFs and many informal discussions. During
the Promo BoF, we decided to create a &amp;ldquo;This Week in KDE Apps&amp;rdquo; blog posts. Paul
volunteered Tobias, Joshua, and me as the initial team for this.&lt;/p&gt;
&lt;p&gt;I also hosted a BoF about a future replacement of KWallet. There were some
discussions about the scope of this effort. Should we just focus on storing
OAuth2 tokens as a background service what the normal users should never
interact with or do a full-blow password manager like macOS Keychain. I
presented my work toward the latter around based on KeePassXC and the KeePass
format (see my
&lt;a class="link" href="https://carlschwan.eu/2024/07/04/keychain-development-update-yubikey-support/" target="_blank" rel="noopener"
&gt;old blogpost&lt;/a&gt;)
as it would allow to use a standardized file format that also work on other
platforms. The KeePassXC developers are working on providing a
&lt;a class="link" href="https://github.com/keepassxreboot/keepassxc/issues/5717" target="_blank" rel="noopener"
&gt;reusable library&lt;/a&gt;, so
we don&amp;rsquo;t need to fork their code. There will likely be more discussion about
this in a separate gitlab issue. The lack of a good story around passwords is
not unique to KDE but to the whole Linux ecosystem. If you have some opinions
about this, feel free to reach out.&lt;/p&gt;
&lt;p&gt;I discussed with Ben, Lydia and Aniqua the infrastructure for newsletters for our
&lt;a class="link" href="https://kde.org/donate/" target="_blank" rel="noopener"
&gt;supporting members&lt;/a&gt;. Ben managed to get an instance
of &lt;a class="link" href="https://listmonk.app/" target="_blank" rel="noopener"
&gt;Listmonk&lt;/a&gt; in a matter of minutes and this seems to be
the right way for us to manage a newsletters or at least way better than using a
mailing list for this.&lt;/p&gt;
&lt;p&gt;Kieryn hosted the best BoF: the Sticker BoF where we shared stickers and had a
competition to see who had the best decorated laptop. I won!!! and received another
special sticker. Thanks Kieryn for organizing this BoF and generally making
Akademy this year such an awesome event!&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/stickers.png" data-size="1772x1064"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/stickers.png" width="1772" height="1064" loading="lazy"
alt="Stickers"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Stickers&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I also ended up finishing a lot of work. I finally ported the last Drupal 7
website to Hugo: &lt;a class="link" href="https://invent.kde.org/websites/dot-kde-org" target="_blank" rel="noopener"
&gt;dot.kde.org&lt;/a&gt;
which was a quite massive website with more than 20 years of history. I
migrated the Hugo version used by KDE from 0.110.0 (which was more than a year
old) to 0.134.0 and I am happy to report that the Hugo folks care a lot about
stability and there was only some very small breaking changes. If you are
working on some KDE websites, don&amp;rsquo;t forget to download the latest version of
&lt;a class="link" href="https://github.com/gohugoio/hugo/releases" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt; and to run the following
command to update the KDE Hugo theme.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;hugo mod get invent.kde.org/websites/hugo-kde@master
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With Volker, we finally merged the status bar integration for Android apps
so that KDE apps running on Android and now use breeze colors in their
status bar, which looks much more integrated and like on Plasma Mobile.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/itinerary.png" data-size="1600x839"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/itinerary.png" width="1600" height="839" loading="lazy"
alt="Itinerary on Android with the new statubar"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Itinerary on Android with the new statubar&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I also got some improvements ideas during Akademy, and I already started
implementing some of them:
&lt;a class="link" href="https://invent.kde.org/pim/itinerary/-/merge_requests/324" target="_blank" rel="noopener"
&gt;https://invent.kde.org/pim/itinerary/-/merge_requests/324&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally I started rewritting the Calligra launcher to Kirigami based on the old
Gemini UI. Still a bit far away from a being in a mergable state but it already
looks quite good.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/calligra1.png" data-size="1122x879"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/calligra1.png" width="1122" height="879" loading="lazy"
alt="Calligra text document templates selection"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Calligra text document templates selection&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/calligracustom.png" data-size="1386x879"&gt;
&lt;img src="https://carlschwan.eu/2024/09/14/qt-contributor-summit-and-akademy/calligracustom.png" width="1386" height="879" loading="lazy"
alt="Calligra new document"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Calligra new document&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a great Akademy again. Thanks a lot for all the organizers for all
their work. I hope to see some KDE contributors again soon at the Nextcloud
Conference and Matrix Summit both in Berlin this month. And to the Linux Days
in Dornbirn.&lt;/p&gt;</description></item><item><title>Calligra 4.0.1</title><link>https://carlschwan.eu/2024/09/03/calligra-4.0.1/</link><pubDate>Tue, 03 Sep 2024 09:25:00 +0000</pubDate><guid>https://carlschwan.eu/2024/09/03/calligra-4.0.1/</guid><description>&lt;p&gt;Calligra 4.0.1 is out. This small releases mostly contains fixes for
distributions issues and updated translations.&lt;/p&gt;
&lt;p&gt;I fixed some compatibility issues for Flatpak which is since yesterday
available on &lt;a class="link" href="https://flathub.org/apps/org.kde.calligra" target="_blank" rel="noopener"
&gt;Flathub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/03/calligra-4.0.1/flathub.png" data-size="767x668"&gt;
&lt;img src="https://carlschwan.eu/2024/09/03/calligra-4.0.1/flathub.png" width="767" height="668" loading="lazy"
alt="Flathub website showing Calligra"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Flathub website showing Calligra&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Yaakov Selkowitz fixed the installation of the Okular generators so now if
Calligra is installed, you can read your office document in Okular correctly.&lt;/p&gt;
&lt;p&gt;Antonio Rojas dropped the unused KPart dependency and reenabled the user
documentations.&lt;/p&gt;
&lt;p&gt;I removed the old space navigator plugin which didn&amp;rsquo;t build anymore and was
only used to navigate an office document with some retro controllers.&lt;/p&gt;
&lt;p&gt;Finally, I fixed a few issues in Stage, I found while dog footing it for my
slides for my Akademy presentation.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/09/03/calligra-4.0.1/stage-slides.png" data-size="665x293"&gt;
&lt;img src="https://carlschwan.eu/2024/09/03/calligra-4.0.1/stage-slides.png" width="665" height="293" loading="lazy"
alt="The updated slides sidebar of Calligra Stage"&gt;
&lt;/a&gt;
&lt;figcaption&gt;The updated slides sidebar of Calligra Stage&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="get-it"&gt;Get It&lt;/h2&gt;
&lt;p&gt;Calligra 4.0 is now available on
&lt;a class="link" href="https://flathub.org/apps/org.kde.calligra" target="_blank" rel="noopener"
&gt;Flathub&lt;/a&gt;. It&amp;rsquo;s also now available
on Arch, KDE Neon and OpenBSD and I am aware there is some work in progress for
Fedora and Mageia. Thanks everyone for packaging Calligra!&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/calligra/calligra-4.0.1.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Calligra Office 4.0 is Out!</title><link>https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/</link><pubDate>Tue, 27 Aug 2024 11:05:00 +0000</pubDate><guid>https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/</guid><description>&lt;p&gt;Calligra is the office and graphics suite developed by KDE and is the successor
to KOffice. With some traditional parts like Kexi and Plan having an
independent release schedule, this release only contains the four following
components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://calligra.org/words/" target="_blank" rel="noopener"
&gt;Calligra Words&lt;/a&gt;: Word Processor&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://calligra.org/sheets/" target="_blank" rel="noopener"
&gt;Calligra Sheets&lt;/a&gt;: Spreadsheet Application&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://calligra.org/stage/" target="_blank" rel="noopener"
&gt;Calligra Stage&lt;/a&gt;: Presentation Application&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://calligra.org/karbon" target="_blank" rel="noopener"
&gt;Karbon&lt;/a&gt;: Vector Graphics Editor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most significant updates are that Calligra has been fully transitioned to
Qt6 and KF6, along with a major overhaul of its user interface.&lt;/p&gt;
&lt;h2 id="general"&gt;General&lt;/h2&gt;
&lt;p&gt;Words, Sheets, and Stage now feature a new sidebar design. Currently, this is
implemented using a proxy style, which will no longer be necessary once the
related merge request in Breeze is
&lt;a class="link" href="https://invent.kde.org/plasma/breeze/-/merge_requests/478" target="_blank" rel="noopener"
&gt;merged&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/sidebar.png" data-size="574x864"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/sidebar.png" width="574" height="864" loading="lazy"
alt="Sidebar with the new immutable tab design"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Sidebar with the new immutable tab design&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I revamped the content of each sidebar tab, addressing various visual glitches
and making the spacing much more consistent.&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;Custom Shape&amp;rdquo; docker has been removed, and custom shapes are now
accessible through a popup menu in the toolbar across all Calligra
applications.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/customshapes.png" data-size="522x407"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/customshapes.png" width="522" height="407" loading="lazy"
alt="Custom shapes popup"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Custom shapes popup&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Regarding the toolbar, I streamlined the default layout by removing basic
actions like copy, cut, and paste.&lt;/p&gt;
&lt;p&gt;The settings dialogs were also cleaned up and are now using the new FlatList
style also used by System Settings and most Kirigami applications.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/dialogs2.png" data-size="1232x850"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/dialogs2.png" width="1232" height="850" loading="lazy"
alt="Settings Dialog"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Settings Dialog&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="words"&gt;Words&lt;/h2&gt;
&lt;p&gt;Word now features the new sidebar design, and the main view uses a shadow to
define the document borders.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/words.png" data-size="1321x917"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/words.png" width="1321" height="917" loading="lazy"
alt="Calligra Words"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Calligra Words&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Style Manager&lt;/em&gt; and &lt;em&gt;Page Layout&lt;/em&gt; dialog were also updated.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/stylemanager.png" data-size="1238x886"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/stylemanager.png" width="1238" height="886" loading="lazy"
alt="Style Manager"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Style Manager&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/pagelayout.png" data-size="729x611"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/pagelayout.png" width="729" height="611" loading="lazy"
alt="Page Layout"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Page Layout&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="stage"&gt;Stage&lt;/h2&gt;
&lt;p&gt;Stage didn&amp;rsquo;t really change aside of the sidebar redesign. But I am using it to work
on my slides for Akademy and it is a pretty solid choice.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/stage.png" data-size="1412x879"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/stage.png" width="1412" height="879" loading="lazy"
alt="Calligra Stage"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Calligra Stage&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The tooltip for the slides are now compatible with Wayland.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/tooltip.png" data-size="543x352"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/tooltip.png" width="543" height="352" loading="lazy"
alt="Tooltip showing a slide"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Tooltip showing a slide&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="calligra-sheets"&gt;Calligra Sheets&lt;/h2&gt;
&lt;p&gt;As part of the Qt6 port, Sheets lost its scripting system based on the
unmaintained Kross framework. In the future, it would be possible to add Python
scriping, thanks to the work of &lt;a class="link" href="https://invent.kde.org/manuelal" target="_blank" rel="noopener"
&gt;Manuel Alcaraz
Zambrano&lt;/a&gt; on getting Python bindings for the
KDE Frameworks.&lt;/p&gt;
&lt;p&gt;Visually a noticable change is that the cell editor moved from a docker
positioned on the left of the spreadsheet view by default to a normal widget on
the top. This takes a lot less space which can be used by the spreadsheet.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/sheets.png" data-size="1339x919"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/sheets.png" width="1339" height="919" loading="lazy"
alt="Calligra Sheets"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Calligra Sheets&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="karbon"&gt;Karbon&lt;/h2&gt;
&lt;p&gt;Karbon didn&amp;rsquo;t received much change outside of the one affecting the whole
platform.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/karbon.png" data-size="1457x931"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/karbon.png" width="1457" height="931" loading="lazy"
alt="Karbon"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Karbon&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="launcher"&gt;Launcher&lt;/h2&gt;
&lt;p&gt;The intial window when opening one of the Calligra application was redesign and
adopted the new &amp;ldquo;&lt;a class="link" href="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/" &gt;frameless style&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/open.png" data-size="1438x917"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/open.png" width="1438" height="917" loading="lazy"
alt="Custom Document tab of the launcher page"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Custom Document tab of the launcher page&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/open2.png" data-size="1412x879"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/open2.png" width="1412" height="879" loading="lazy"
alt="Template tab of the launcher page"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Template tab of the launcher page&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="other"&gt;Other&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://calligra.org/old-components/#braindump" target="_blank" rel="noopener"
&gt;Braindump&lt;/a&gt; is now able to
compile again, but since it lacks an active maintainer, the component is
disabled in release builds.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The webshape plugin has been ported from the outdated QtWebkit module to
QtWebEngine and is no longer exclusive to Braindump. This means you can now
embed websites directly into your word documents, slides, and spreadsheets.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/webshape.png" data-size="1438x917"&gt;
&lt;img src="https://carlschwan.eu/2024/08/27/calligra-office-4.0-is-out/webshape.png" width="1438" height="917" loading="lazy"
alt="Webshape"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Webshape&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The AppStream id of every components is prefixed by &lt;code&gt;org.kde.calligra&lt;/code&gt;. This
allow Flatpak to expose every Calligra applications to your application
launcher.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;Calligra needs your support! You can contribute by getting involved in
development, providing new or updated templates, or making a donation to &lt;a class="link" href="https://kde.org/donate/" target="_blank" rel="noopener"
&gt;KDE
e.V.&lt;/a&gt;. Join the discussion in our &lt;a class="link" href="https://go.kde.org/matrix/#/#calligra:kde.org" target="_blank" rel="noopener"
&gt;Matrix
channel&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="credits"&gt;Credits&lt;/h2&gt;
&lt;p&gt;This release would not have been possible without the high quality
&lt;a class="link" href="https://phabricator.kde.org/T12837" target="_blank" rel="noopener"
&gt;mockups&lt;/a&gt; provided by &lt;a class="link" href="https://mastodon.social/@manueljlin" target="_blank" rel="noopener"
&gt;Manuel Jesús de la
Fuente&lt;/a&gt;. Also big thanks to everyone who
contributed to this Calligra release: Evgeniy Harchenko, Dmitrii Fomchenkov and
bob sayshilol.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/calligra/calligra-4.0.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>FrOScon 2024</title><link>https://carlschwan.eu/2024/08/26/froscon-2024/</link><pubDate>Mon, 26 Aug 2024 08:45:00 +0000</pubDate><guid>https://carlschwan.eu/2024/08/26/froscon-2024/</guid><description>&lt;p&gt;This year, I attended &lt;a class="link" href="https://froscon.org/en/" target="_blank" rel="noopener"
&gt;FrOScon&lt;/a&gt; for the first time .
FrOScon is the biggest conference about free and open-source software in
Germany. It takes place every year in Bonn/Siegburg (Germany) at the
weekend and is free to attend.&lt;/p&gt;
&lt;p&gt;For the first time, I was not at a conference to staff a KDE stand. My employer
had a stand there, and it was a great occasion for me to meet some colleagues,
fellow KDE, and Matrix contributors.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/26/froscon-2024/gnupg.jpg" data-size="1920x1440"&gt;
&lt;img src="https://carlschwan.eu/2024/08/26/froscon-2024/gnupg.jpg" width="1920" height="1440" loading="lazy"
alt="GnuPG Stand"&gt;
&lt;/a&gt;
&lt;figcaption&gt;GnuPG Stand&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;So I spent the majority of my time at the GnuPG stand and discussing many things
with Volker, including KDE PIM and the future of KWallet.&lt;/p&gt;
&lt;p&gt;I also meet many Matrix community members and am excited to attend the
&lt;a class="link" href="https://2024.matrix.org/" target="_blank" rel="noopener"
&gt;Matrix Conference&lt;/a&gt; next month.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/08/26/froscon-2024/matrix.jpg" data-size="4000x1868"&gt;
&lt;img src="https://carlschwan.eu/2024/08/26/froscon-2024/matrix.jpg" width="4000" height="1868" loading="lazy"
alt="Matrix Stand"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Matrix Stand&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;All in one, it was a great conference and I hope to see more KDE people there
next year and maybe even having out own KDE stand.&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.4</title><link>https://carlschwan.eu/2024/07/23/kirigami-addons-1.4/</link><pubDate>Tue, 23 Jul 2024 21:20:35 +0000</pubDate><guid>https://carlschwan.eu/2024/07/23/kirigami-addons-1.4/</guid><description>&lt;p&gt;Kirigami Addons 1.4 is out! This release introduce a new module to manage
actions similar to that we can find in the QtWidgets world with KXmlGui. This
was not written from scratch but upstream the existing infrastructure from
Merkuro (ex-Kalendar) and Marknote. These two applications have already been
ported to this new module and more like Tokodon or
&lt;a class="link" href="https://carlschwan.eu/2024/07/04/keychain-development-update-yubikey-support/" target="_blank" rel="noopener"
&gt;KDE Keychain&lt;/a&gt;
will follow soon.&lt;/p&gt;
&lt;p&gt;This includes a shortcut editor to assign and modify the shortcuts of an
application and a command bar to quickly search and trigger actions&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/07/23/kirigami-addons-1.4/shortcut-editor.png" data-size="1373x699"&gt;
&lt;img src="https://carlschwan.eu/2024/07/23/kirigami-addons-1.4/shortcut-editor.png" width="1373" height="699" loading="lazy"
alt="Shortcut editor"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Shortcut editor&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/07/23/kirigami-addons-1.4/commandbar.png" data-size="1041x731"&gt;
&lt;img src="https://carlschwan.eu/2024/07/23/kirigami-addons-1.4/commandbar.png" width="1041" height="731" loading="lazy"
alt="Command bar"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Command bar&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Similar to KXmlGui, the actions are defined in C++, which allows to make use
&lt;a class="link" href="https://api.kde.org/frameworks/kconfig/html/namespaceKStandardActions.html" target="_blank" rel="noopener"
&gt;KStandardActions&lt;/a&gt;
and get consistent shortcuts accross all your applications.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApplication&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;AbstractKirigamiApplication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Q_OBJECT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;QML_ELEMENT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;explicit&lt;/span&gt; &lt;span class="n"&gt;MyApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setupActions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;Q_SIGNALS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;addNotebook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;MyApplication&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MyApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractKirigamiApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;setupActions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;MyApplication&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setupActions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;AbstractKirigamiApplication&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setupActions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;actionName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QLatin1String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;add_notebook&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KAuthorized&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;authorizeAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actionName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mainCollection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actionName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;MyApplication&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;addNotebook&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;@action:inmenu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;New Notebook&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QIcon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fromTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QStringLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-add-symbolic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mainCollection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;objectName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mainCollection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setDefaultShortcut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QKeySequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SHIFT&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Key_N&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These new actions can then be used from QML thanks to the new
&lt;code&gt;Kirigami.Action::fromQAction&lt;/code&gt; property.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statefulapp&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;StatefulApp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;StatefulApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatefulWindow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;windowName:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Main&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;application:&lt;/span&gt; &lt;span class="nx"&gt;MyApplication&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;configurationView:&lt;/span&gt; &lt;span class="nx"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConfigurationView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fromQAction:&lt;/span&gt; &lt;span class="nx"&gt;MyApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add_notebook&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Connections&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;target:&lt;/span&gt; &lt;span class="nx"&gt;MyApplication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;onAddNotebook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is a new template available in KAppTemplate, which allows you to
kickstart your new Kirigami application with the basic skeleton with this new
module and other &amp;ldquo;Kirigami Addons&amp;rdquo; modules.&lt;/p&gt;
&lt;h2 id="other-changes"&gt;Other Changes&lt;/h2&gt;
&lt;p&gt;The FormCard design was tweaked a bit more when using a dark theme, thanks to
James and Joshua for their feedback.&lt;/p&gt;
&lt;p&gt;Speaking of FormCard, with the development of KeyChain, I ended up adding a new
component to the FormCard collection: FormTextAreaDelegate. This component
is the equivalent of FormTextFieldDelegate but with a TextArea instead.
FormComboBoxDelegate and FormTextFieldDelegate also received a bunch of new
properties and functions to proxy the underlying QtQuick.Controls component.&lt;/p&gt;
&lt;p&gt;Evgeniy Harchenko tweaked a bit the headers of the TableView component.&lt;/p&gt;
&lt;p&gt;Finally, a new contributor Andreas Gattringer fixed a crash in the video
maximizing component which was affecting NeoChat.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.4.0.tar.xz.sig.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Keychain Development Update: Yubikey Support</title><link>https://carlschwan.eu/2024/07/04/keychain-development-update-yubikey-support/</link><pubDate>Thu, 04 Jul 2024 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/2024/07/04/keychain-development-update-yubikey-support/</guid><description>&lt;p&gt;Following my &lt;a class="link" href="https://carlschwan.eu/2024/07/01/initial-work-on-keychain/" &gt;latest post about Keychain&lt;/a&gt;,
here is a new development update. Yubikey and Key Files are now supported,
which allows you to requires a YubiKey to open a password database but also to
save it.&lt;/p&gt;
&lt;figure style="max-width: 100%; margin-left: auto; margin-right: auto;" class="embed-responsive embed-responsive-16by9"&gt;
&lt;video src="yubikey-unlock.mp4" loop autoplay muted&gt;&lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;Saving and editing groups also now works.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/07/04/keychain-development-update-yubikey-support/edit-group.png" data-size="672x699"&gt;
&lt;img src="https://carlschwan.eu/2024/07/04/keychain-development-update-yubikey-support/edit-group.png" width="672" height="699" loading="lazy"
alt="Group editing dialog"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Group editing dialog&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;And I now started working on the database creation process. The UI is ready but
I still need to bind it to the backend.&lt;/p&gt;
&lt;p&gt;Thanks to everyone who send me encouragement messages and also to Laurent who
did a lot of
&lt;a class="link" href="https://invent.kde.org/carlschwan/keychain/-/commits/master?ref_type=heads" target="_blank" rel="noopener"
&gt;cleanups&lt;/a&gt;
in the codebase.&lt;/p&gt;
&lt;p&gt;See you in the next development update.&lt;/p&gt;</description></item><item><title>Initial work on Keychain</title><link>https://carlschwan.eu/2024/07/01/initial-work-on-keychain/</link><pubDate>Mon, 01 Jul 2024 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/2024/07/01/initial-work-on-keychain/</guid><description>&lt;p&gt;A month ago, I started working on a new application to manage your passwords in
Plasma. And while still at a PoC status, this weekend, it finally started to
look like something almost usable, so it sounded like a good occassion to write
a small blog post about it.&lt;/p&gt;
&lt;p&gt;The current name is &amp;ldquo;Keychain&amp;rdquo; or &amp;ldquo;Plasma Keychain&amp;rdquo; but this is subject to
change and suggestions are more than welcome.&lt;/p&gt;
&lt;p&gt;My end goal is to provide a more future proof replacement to the ageing KWallet
application. From a technical point of view, this is a fork of the internal of
KeepassXC with a Kirigami GUI completely written from scratch. This means it
uses the standardized Keepass format to store the passwords in the database
which is implemented by many applications including on other platforms like Android and
iOS (see the list of &lt;a class="link" href="https://keepass.info/download.html" target="_blank" rel="noopener"
&gt;Keepass port&lt;/a&gt;). And
while not yet exposed in the GUI, basing the work on top of KeepassXC enables a
lot of interesting features not available in KWallet, like Yubikey and PassKey
support, password sharing, export and import for various other password
database formats, TOTP support and browser integration&amp;hellip;&lt;/p&gt;
&lt;p&gt;While also providing vital features for the desktop integration like the
Freedesktop Secret Service protocol what we also have in KWallet.&lt;/p&gt;
&lt;p&gt;Here are some screenshots of the current state.&lt;/p&gt;
&lt;p&gt;This is the main view where viewing, adding, editing and removing entries
already work.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/07/01/initial-work-on-keychain/mainview.png" data-size="1122x735"&gt;
&lt;img src="https://carlschwan.eu/2024/07/01/initial-work-on-keychain/mainview.png" width="1122" height="735" loading="lazy"
alt="Main View"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Main View&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;This is the database generator page which unfortunately doesn&amp;rsquo;t work yet.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/07/01/initial-work-on-keychain/generate.png" data-size="625x810"&gt;
&lt;img src="https://carlschwan.eu/2024/07/01/initial-work-on-keychain/generate.png" width="625" height="810" loading="lazy"
alt="Database generator"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Database generator&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;And this is the UI to open an existing database.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/07/01/initial-work-on-keychain/open.png" data-size="712x705"&gt;
&lt;img src="https://carlschwan.eu/2024/07/01/initial-work-on-keychain/open.png" width="712" height="705" loading="lazy"
alt="Database generator"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Database generator&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;As you can see there is still a lot of work required, so if people are
interested to help or to take a look at the current progress, the code is
&lt;a class="link" href="https://invent.kde.org/carlschwan/keychain" target="_blank" rel="noopener"
&gt;on KDE&amp;rsquo;s gitlab instance&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.3.0</title><link>https://carlschwan.eu/2024/06/26/kirigami-addons-1.3.0/</link><pubDate>Wed, 26 Jun 2024 11:20:35 +0000</pubDate><guid>https://carlschwan.eu/2024/06/26/kirigami-addons-1.3.0/</guid><description>&lt;p&gt;Kirigami Addons 1.3.0 is out. Kirigami Addons is a collection of components to
enhance your Kirigami/QML application. This release contains many change
related to the settings module.&lt;/p&gt;
&lt;h2 id="configurationview"&gt;ConfigurationView&lt;/h2&gt;
&lt;p&gt;The current way to create a settings page in your application is to use
&lt;code&gt;CategorizedSettings&lt;/code&gt; with some &lt;code&gt;SettingAction&lt;/code&gt; for each setting page. This was
based on &lt;code&gt;Kirigami.PageRow&lt;/code&gt; which was then either pushed on a layer on mobile
or to a seperate page on desktop. This turned out to be quite unreliable in
practice as &lt;code&gt;Kirigami.PageRow&lt;/code&gt; is a visual element.&lt;/p&gt;
&lt;p&gt;The new &lt;code&gt;ConfigurationView&lt;/code&gt; is based on a plain non-visual &lt;code&gt;QtObject&lt;/code&gt; with for
the moment two backends:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One for mobile which looks similar to the Plasma Settings application of
Plasma Mobile.&lt;/li&gt;
&lt;li&gt;One for desktop which looks similar to the System Settings application of
Plasma Desktop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The API is almost the same as the previous CategorizedSettings which made
porting quite easy. Here is for example a button that open the settings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Controls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: button&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConfigurationView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;window:&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApplicationWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApplicationWindow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;modules:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConfigurationModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;moduleId:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;appearance&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:button&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Appearance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;icon.name:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;preferences-desktop-theme-global&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;page:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Qt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;org.kde.tokodon&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AppearancePage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConfigurationModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;moduleId:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;about&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:button&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;About Tokodon&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;icon.name:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;help-about&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;page:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Qt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;org.kde.kirigamiaddons.formcard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AboutPage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;category:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@title:group&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;About&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;icon.name:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;settings-configure-symbolic&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@action:button&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Settings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;onClicked:&lt;/span&gt; &lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this change, both &lt;code&gt;CategorizedSettings&lt;/code&gt; and &lt;code&gt;SettingAction&lt;/code&gt; are now deprecated.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/26/kirigami-addons-1.3.0/configurationview-desktop.png" data-size="1182x789"&gt;
&lt;img src="https://carlschwan.eu/2024/06/26/kirigami-addons-1.3.0/configurationview-desktop.png" width="1182" height="789" loading="lazy"
alt="ConfigurationView on desktop"&gt;
&lt;/a&gt;
&lt;figcaption&gt;ConfigurationView on desktop&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/26/kirigami-addons-1.3.0/configurationview-mobile.png" data-size="538x876"&gt;
&lt;img src="https://carlschwan.eu/2024/06/26/kirigami-addons-1.3.0/configurationview-mobile.png" width="538" height="876" loading="lazy"
alt="ConfigurationView on mobile"&gt;
&lt;/a&gt;
&lt;figcaption&gt;ConfigurationView on mobile&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="spellcheckingconfigurationmodule"&gt;SpellcheckingConfigurationModule&lt;/h2&gt;
&lt;p&gt;With &lt;code&gt;ConfigurationView&lt;/code&gt; each page is a &lt;code&gt;ConfigurationModule&lt;/code&gt; and Kirigami
Addons provides a &lt;code&gt;ConfigurationModule&lt;/code&gt; for the spellchecking configuration of
your application. This will allow to reduce code duplication between NeoChat,
Tokodon, Marknote and more applications which uses Sonnet.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConfigurationView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;modules:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;KirigamiSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SpellcheckingConfigurationView&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="formcard"&gt;FormCard&lt;/h2&gt;
&lt;p&gt;FormCard design was slighly updated and now uses shadows as you might have
already noticed from the previous screenshots.&lt;/p&gt;
&lt;h2 id="searchpopupfield"&gt;SearchPopupField&lt;/h2&gt;
&lt;p&gt;Another component which is getting deprecated in &lt;code&gt;SearchPopupField&lt;/code&gt;, there is
now a replacement for that in Kirigami with the same behavior and I added an
example how to port away to &lt;code&gt;Kirigami.SearchDialog&lt;/code&gt; and I also ported all the
know usage already.&lt;/p&gt;
&lt;h2 id="maintainance-work"&gt;Maintainance work&lt;/h2&gt;
&lt;p&gt;Aside from this major changes, there is ongoing maintaince works. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Removing the accidental QtWidgets on Android caused by QtLabs.ColorDialog (me: Carl Schwan)&lt;/li&gt;
&lt;li&gt;Ensure all translated strings are loaded from the correct translation domain (me: Carl Schwan)&lt;/li&gt;
&lt;li&gt;The license dialog in the AboutPage is now opened in the correct window (Jack Hill)&lt;/li&gt;
&lt;li&gt;Fix the focus in the FormComboBoxDelegate (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Fix the capitalization in the AboutPage (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Increase the padding in FormCardDialog to match the other FormCard components&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.3.0.tar.xz.sig.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Hash-o-Matic 1.0.1</title><link>https://carlschwan.eu/2024/06/25/hash-o-matic-1.0.1/</link><pubDate>Tue, 25 Jun 2024 10:10:35 +0000</pubDate><guid>https://carlschwan.eu/2024/06/25/hash-o-matic-1.0.1/</guid><description>&lt;p&gt;Hash-o-Matic 1.0.1 is out! Hash-o-Matic is a tool to compare and generate
checksum for your files to verify the authenticity of them. It also verify
files via their use PGP signatures.&lt;/p&gt;
&lt;p&gt;This new release of Hash-o-Matic provides updated translations and some small
visual changes. In the background, the application was ported to the new QML
type registration, we now support building Hash-o-Matic on Haiku and we now
require released version of KDE Frameworks instead of pre-released version.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager Section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/hash-o-matic/hash-o-matic-1.0.1.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>KDE PIM Sprint 2024 edition</title><link>https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/</link><pubDate>Sun, 16 Jun 2024 10:10:35 +0000</pubDate><guid>https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/</guid><description>&lt;p&gt;This year again I participated to the KDE PIM Sprint in Toulouse. As always it
was really great to meet other KDE contributors and to work together for one
weekend. And as you might have seen on my
&lt;a class="link" href="https://floss.social/@carlschwan" target="_blank" rel="noopener"
&gt;Mastodon account&lt;/a&gt;, a lot of food was also
involved.&lt;/p&gt;
&lt;h2 id="day-1-friday-afternoon"&gt;Day 1 (Friday Afternoon)&lt;/h2&gt;
&lt;p&gt;We started our sprint on Thursday with a lunch at the legendary cake place,
which I missed last year due to my late arrival.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/cake.jpg" data-size="3000x2354"&gt;
&lt;img src="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/cake.jpg" width="3000" height="2354" loading="lazy"
alt="Picture of some delicious cakes: a piece of cheesecake raspberry and basil, a piece of lemon tart with meringue and a piece of carrot cake)"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of some delicious cakes: a piece of cheesecake raspberry and basil, a piece of lemon tart with meringue and a piece of carrot cake)&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We then went to the coworking space where we would spend the remaining of this
sprint and started working on defining tasks to work on and putting them on
real kanban board.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/kanban-start.jpg" data-size="4000x1820"&gt;
&lt;img src="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/kanban-start.jpg" width="4000" height="1820" loading="lazy"
alt="A kanban board with tasks to discuss and to implement"&gt;
&lt;/a&gt;
&lt;figcaption&gt;A kanban board with tasks to discuss and to implement&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;To get a good summary of the specific topics we discussed, I invite you to
consult the &lt;a class="link" href="https://ervin.ipsquad.net/blog/2024/06/16/report-from-kdepim-spring-sprint-2024/" target="_blank" rel="noopener"
&gt;blog of Kevin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That day, aside from the high level discussion, I proceeded to port away the
IMAP authentification mechanism for Outlook accounts away from the KWallet API
to use the more generic QtKeychain API. I also removed a large dependency
from libkleo (the KDE library to interact with GPG).&lt;/p&gt;
&lt;h2 id="day-2-saturday"&gt;Day 2 (Saturday)&lt;/h2&gt;
&lt;p&gt;On the second day, we were greated by a wonderful breakfast (thanks Kevin).&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/croissant.jpg" data-size="3000x2931"&gt;
&lt;img src="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/croissant.jpg" width="3000" height="2931" loading="lazy"
alt="Picture of croissant, brioche and chocolatine"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of croissant, brioche and chocolatine&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I worked on moving EventViews (the library that renders
the calendar in KOrganizer) and IncidenceEditor (the library that provides
the event/todo editor in KOrganizer) to KOrganizer. This will allow to
reduce the number of libraries in PIM.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://invent.kde.org/pim/kdepim-addons/-/merge_requests/51" target="_blank" rel="noopener"
&gt;Remove KOrganizer plugins from kdepim-addons&lt;/a&gt;
and &lt;a class="link" href="https://invent.kde.org/pim/korganizer/-/merge_requests/122" target="_blank" rel="noopener"
&gt;move them instead to KOrganizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Do not use a complex plugin system for handling calendar invitation and instead
provide the incidence editor as a binary and call it instead.
(&lt;a class="link" href="https://invent.kde.org/pim/akonadi-calendar/-/merge_requests/88" target="_blank" rel="noopener"
&gt;MR 1&lt;/a&gt;,
&lt;a class="link" href="https://invent.kde.org/pim/kdepim-addons/-/merge_requests/52" target="_blank" rel="noopener"
&gt;MR 2&lt;/a&gt;
and &lt;a class="link" href="https://invent.kde.org/pim/incidenceeditor/-/merge_requests/59" target="_blank" rel="noopener"
&gt;MR 3&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For lunch, we ended up eating at the excellent Mexican restaurant next to the
location of the previous sprint.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/mexican.jpg" data-size="3000x2658"&gt;
&lt;img src="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/mexican.jpg" width="3000" height="2658" loading="lazy"
alt="Mexican food"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Mexican food&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I also worked on &lt;a class="link" href="https://invent.kde.org/pim/kmail/-/merge_requests/132" target="_blank" rel="noopener"
&gt;removing the &amp;ldquo;Add note&amp;rdquo; functionality in KMail&lt;/a&gt;.
This feature allow to store notes to emails following &lt;a class="link" href="https://www.rfc-editor.org/rfc/rfc5257" target="_blank" rel="noopener"
&gt;RFC5257&lt;/a&gt;.
Unfortunatelty this RFC never left the EXPERIMENTAL state and so these notes
were only stored in Akonadi and not synchronized with any services.&lt;/p&gt;
&lt;p&gt;This allow to remove the
&lt;a class="link" href="https://invent.kde.org/pim/pimcommon/-/merge_requests/41" target="_blank" rel="noopener"
&gt;relevant widget from the pimcommon library&lt;/a&gt;
and &lt;a class="link" href="https://invent.kde.org/pim/akonadi/-/merge_requests/195" target="_blank" rel="noopener"
&gt;the Akonadi attribute&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also started removing another type of notes: the KNotes app which
provided sticky notes. This application was not maintained anymore, didn&amp;rsquo;t work
so well with Wayland. If you were using KNotes, to make sure you don&amp;rsquo;t loose
your notes, I added support in Marknote to &lt;a class="link" href="https://invent.kde.org/office/marknote/-/merge_requests/31" target="_blank" rel="noopener"
&gt;import notes from KNotes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/marknote.png" data-size="997x873"&gt;
&lt;img src="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/marknote.png" width="997" height="873" loading="lazy"
alt="Marknote with the context menu to import notes"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Marknote with the context menu to import notes&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Finally I worked on &lt;a class="link" href="https://invent.kde.org/pim/akonadi/-/merge_requests/193" target="_blank" rel="noopener"
&gt;removing visible Akonadi branding from some KDE PIM applications&lt;/a&gt;.
The branding was usually only visible when an issue occurred,
which didn&amp;rsquo;t help with Akonadi reputation.&lt;/p&gt;
&lt;p&gt;We ended up working quite late and ordering Pizzas. I personally got one with a lot
of cheese (but no photo this time).&lt;/p&gt;
&lt;h2 id="day-3-sunday"&gt;Day 3 (Sunday)&lt;/h2&gt;
&lt;p&gt;The final day, we didn&amp;rsquo;t had any breakfast :( but instead a wonderful brunch.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/brunch.jpg" data-size="3000x3151"&gt;
&lt;img src="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/brunch.jpg" width="3000" height="3151" loading="lazy"
alt="Picture of the brunch"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of the brunch&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Aside from eating, I started writing a plugin system for the MimeTreeParser
which powers the email viewer in Merkuro and in Kleopatra. In the short term,
I want to be able to add Itinerary integration in Merkuro but in the longer
term the goal is to bring this email viewer to feature parity with the email
viewer from KMail and then replace the KMail email viewer with the one from
Merkuro. Aside from removing duplicate code, this will improve the security
since the individual email parts are isolated from each other and this will
makes it easier for the email view to follow KDE styling as this is just
normal QML instead of fake HTML components.&lt;/p&gt;
&lt;p&gt;I also merged and rebased some WIP merge requests in Marknote in preparation of
a new release soon and reviewed merge requests from the others.&lt;/p&gt;
&lt;h2 id="last-but-not-least"&gt;Last but not least&lt;/h2&gt;
&lt;p&gt;If you want to know more or engage with us, please join the &lt;a class="link" href="https://matrix.to/#/#kontact:kde.org" target="_blank" rel="noopener"
&gt;KDE PIM&lt;/a&gt; and the
&lt;a class="link" href="https://matrix.to/#/#merkuro:kde.org" target="_blank" rel="noopener"
&gt;Merkuro&lt;/a&gt; matrix channels! Let’s chat
further.&lt;/p&gt;
&lt;p&gt;Also, I’d like to thank again &lt;a class="link" href="https://www.etincelle-coworking.com/" target="_blank" rel="noopener"
&gt;Étincelle Coworking&lt;/a&gt;
and &lt;a class="link" href="https://ev.kde.org/" target="_blank" rel="noopener"
&gt;KDE e.V.&lt;/a&gt; to make this event possible. This wouldn’t be
possible without a venue and without at least partial support in the travel
expenses.&lt;/p&gt;
&lt;p&gt;Finally, if you like such meetings to happen in the future so that we can push
forward your favorite software, please consider making a
&lt;a class="link" href="https://www.kde.org/community/donations/index.php" target="_blank" rel="noopener"
&gt;tax-deductible donation&lt;/a&gt; to the
&lt;a class="link" href="https://ev.kde.org/" target="_blank" rel="noopener"
&gt;KDE e.V. foundation&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>KDE Apps Initiative</title><link>https://carlschwan.eu/2024/05/31/kde-apps-initiative/</link><pubDate>Fri, 31 May 2024 15:10:35 +0000</pubDate><guid>https://carlschwan.eu/2024/05/31/kde-apps-initiative/</guid><description>&lt;p&gt;A bit like Nate&amp;rsquo;s &amp;ldquo;5 minutes bugs&amp;rdquo; initiative, I&amp;rsquo;m announcing a new initiative to
improve our applications ecosystem. The goal is to improve the quality and
quantity of KDE applications and the number of application contributors. For
anybody who knows me, it is not that surprising. Inside KDE, I have been mainly
involved in apps for many years. I worked on all areas, from development
(maintaining or co-maintaining many apps like NeoChat, Kontrast, MarkNote,
Tokodon, and Arianna, and contributing to numerous other apps, but also design,
promotion, websites (e.g., apps.kde.org) and even a bit of packaging (Flatpak
and to a lesser extent Windows). Hopefully, making this a bit more public and
making this an initiative with a bit more coordination will encourage more
people to help :)&lt;/p&gt;
&lt;p&gt;The good thing is that we don&amp;rsquo;t start from zero. In almost 30 years, KDE
developers have developed over 200 applications, covering many use cases, from
high-quality applications for artists (&lt;a class="link" href="https://krita.org" target="_blank" rel="noopener"
&gt;Krita&lt;/a&gt;,
&lt;a class="link" href="https://kdenlive.org" target="_blank" rel="noopener"
&gt;Kdenlive&lt;/a&gt;, &lt;a class="link" href="https://glaxnimate.org" target="_blank" rel="noopener"
&gt;Glaxnimate&lt;/a&gt;) to
educational and office apps. We also have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tons of shared libraries that make developing new apps more straightforward
and consistent&lt;/li&gt;
&lt;li&gt;an increasing amount of technical documentation on develop.kde.org/docs
(Thanks to Thiago, Claudio, and everyone else who contributed to it)&lt;/li&gt;
&lt;li&gt;a nice auto-generated website that lists all of these apps (apps.kde.org)&lt;/li&gt;
&lt;li&gt;a whole CI/CD system that makes it easy to test and deploy our apps to
Flatpak, Windows, macOS, Android and FreeBSD&lt;/li&gt;
&lt;li&gt;tooling for the user documentation and the translations of apps&lt;/li&gt;
&lt;li&gt;an opt-in service to ensure that apps are regularly released (KDE Gear)&lt;/li&gt;
&lt;li&gt;tools like &lt;a class="link" href="https://apps.kde.org/clazy/" target="_blank" rel="noopener"
&gt;Clazy&lt;/a&gt; and
&lt;a class="link" href="https://apps.kde.org/heaptrack/" target="_blank" rel="noopener"
&gt;Heaptrack&lt;/a&gt; to improve the code quality and
performance of KDE apps&lt;/li&gt;
&lt;li&gt;and a lot more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However there are prominent areas where we should improve our story; otherwise,
we would already have a desktop and mobile market share of 90% and archived
world domination.&lt;/p&gt;
&lt;p&gt;More concrete here is a non-exclusive list of high-level tasks to achieve this
goal.&lt;/p&gt;
&lt;h2 id="closing-the-feature-gap"&gt;Closing the Feature Gap&lt;/h2&gt;
&lt;p&gt;We need to identify missing apps compared to other app ecosystems (Windows,
macOS, Android, iOS, and GNOME) and see where we could, with little effort,
improve our app offering. While creating a KDE 3D editor like Blender is
unrealistic, we could already go quite far by creating small apps that wrap up
existing CLI tools or KDE libraries. To give some examples, I saw a few days
ago that GNOME has a new document converter app called Morphosis. It&amp;rsquo;s a simple
wrapper around Pandoc, and we could either do the same and wrap Pandoc or use
Calligra&amp;rsquo;s rich collection of filters. Another example is a translation
application; we already have a library in KTextAddons that does translations
with many backends (offline and online), and the library even provides a
ready-to-use widget. We could create a simple wrapper around KTextAddons, and
boom; we get a new high-quality application with minimal maintenance effort.&lt;/p&gt;
&lt;h2 id="improving-our-existing-applications"&gt;Improving our Existing Applications&lt;/h2&gt;
&lt;p&gt;Aside from creating new applications, improving and reviving some of our
applications is also highly valued. A lot of work has already been put into
these applications, and by cleaning up their UI and bringing them up to our
latest standard, we could go quite far. Some examples: Calligra (a complete
office suite including presentation tool, spreadsheet, presentation, and vector
editor), KTechLab (an IDE for microcontrollers and electronics), KWave (a sound
editor), Parley (a vocabulary trainer).&lt;/p&gt;
&lt;p&gt;Usually for every release, I try to have one or two apps, where I focus some time
on it. In the past, I worked for example on KWordQuiz, KAlgebra, Koko. I
recently ported Calligra to Qt6, so now one of my side quests is to figure out a
way to have a modern QtQuick UI while using the current QPainter-based renderer
using the new &lt;a class="link" href="https://www.qt.io/blog/window-embedding-in-qt-quick" target="_blank" rel="noopener"
&gt;Window embedding in Qt 6.7&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="better-marketting-for-our-applications"&gt;Better Marketting for our Applications&lt;/h2&gt;
&lt;p&gt;We need not only more apps but also better promotion. The apps.kde.org website
has already helped a lot by listing all KDE apps, and more recently, we also
created a lot of kde.org/for websites that list some KDE apps for some niches
that might be interested in some of our apps. Further ideas on improving the
marketing effort would be to promote new and lesser-known applications on
social media regularly. But also publish a &amp;ldquo;This week in KDE apps&amp;rdquo; blog post
that would cover all the news relating to first and third-party apps (e.g., new
apps, updates, new app relevant APIs), and this would be community maintained
with a process similar to this week in Matrix/GNOME/&amp;hellip; where people write in a
Matrix channel and a bot compile the relevant posts togethers. We need to make
the progress on our apps more visible.&lt;/p&gt;
&lt;p&gt;In addition to promoting first-party apps, we must figure out how to better
promote third-party apps and extensions that use KDE Frameworks and integrate
well with Plasma. Here, we could get some inspiration from the GNOME Circle
initiative.&lt;/p&gt;
&lt;h2 id="make-it-more-accessible-to-start-a-new-project"&gt;Make it More Accessible to Start a New Project&lt;/h2&gt;
&lt;p&gt;Documentation is essential in making it easy for newcomers to start projects.
In the past few years, we have invested a lot of effort into that. I started
develop.kde.org/docs, moved and updated a lot of old documentation from
techbase.kde.org, and mentored a Season of KDE project to write a Kirigami
tutorial. Nowadays, Thiago is fabulously leading the documentation effort. It
is going in the right direction.&lt;/p&gt;
&lt;p&gt;Aside from pure documentation, I&amp;rsquo;m impressed by the quality of the GNOME
Workbench app and the number of examples it contains. I started a simple
prototype of the same idea with Kirigami a while ago, which I need to finish
(help is welcome 🤗). In the same vein, KAppTemplate and our default templates
need some love.&lt;/p&gt;
&lt;p&gt;Aside from documentation, we should create a support channel where people can
ask for help with their applications. We could also make it more evident that
developers are encouraged to ask on kde-devel and the VDG channel for help with
their apps, even if their apps are not first-party KDE applications.&lt;/p&gt;
&lt;h2 id="making-it-more-attractive-to-write-kde-applications"&gt;Making it More Attractive to Write KDE Applications&lt;/h2&gt;
&lt;p&gt;Writing applications using KDE Frameworks is already quite attractive, but we
should communicate more on the advantage of starting an application using the
KDE Frameworks more.&lt;/p&gt;
&lt;p&gt;Firstly, by leveraging an almost 30-year-old ecosystem, app developers can
reuse many libraries for their apps and find examples of how to implement the
most common workflow in existing code. KDE Frameworks are also extremely
cross-platform, and creating a KDE application doesn&amp;rsquo;t restrict you to only
Plasma. Krita and Kleopatra have famously had millions of Windows
installations. We also have Craft, which helps develop and deploy Qt
applications on Windows, macOS and Android.&lt;/p&gt;
&lt;p&gt;For first-party applications, our self-hosted Gitlab allows app developers to
have full CI/CD pipelines for many platforms and even automatically publish
them to the Windows Store. We also have infrastructure for translations, user
documentation, wikis, code search, file sharing (Nextcloud), chat (Matrix), OSM
hosting, and more. There is also a human factor; by making an app a first-party
KDE app, the app received a lot of help from experimented KDE developers as
part of the KDE review process and also during the entire life of the app. And
we have a promo team that helps promote your app as much as possible.&lt;/p&gt;
&lt;p&gt;For third-party applications, by being LGPL, we give users of KDE Frameworks a
lot of freedom in licensing and monetizing their applications as long as they
contribute their changes back to the library they use.&lt;/p&gt;
&lt;h2 id="not-limiting-us-to-c"&gt;Not Limiting Us to C++&lt;/h2&gt;
&lt;p&gt;In terms of cross-language support, there are also two independent efforts to
make KDE Frameworks accessible to more programming languages: one for &lt;a class="link" href="https://invent.kde.org/manuelal/pykde6" target="_blank" rel="noopener"
&gt;Python by
alcarazzam&lt;/a&gt;, which is part of a GSoC
project I am mentoring, and another one for
&lt;a class="link" href="https://invent.kde.org/mystchonky/cxx-kde-frameworks" target="_blank" rel="noopener"
&gt;Rust by mystchonky&lt;/a&gt;.
These efforts should make it easier for app developers to write KDE
applications even if they are unfamiliar with C++ or prefer not to use it.&lt;/p&gt;
&lt;h2 id="streamline-the-publishing-of-our-apps-for-other-platforms"&gt;Streamline the Publishing of our Apps for Other Platforms&lt;/h2&gt;
&lt;p&gt;We now reached a state where most of our applications are automatically
published on Flathub. This is not the case yet for Windows, Apple and Android.
Recently we gained the ability to publish directly from the gitlab CI to the
Microsoft Store, but we don&amp;rsquo;t make use of that yet in most of our apps. So
let&amp;rsquo;s change that!&lt;/p&gt;
&lt;h2 id="getting-involved"&gt;Getting Involved&lt;/h2&gt;
&lt;p&gt;I started creating a board of issues on &lt;a class="link" href="https://invent.kde.org/carlschwan/apps/-/boards" target="_blank" rel="noopener"
&gt;gitlab&lt;/a&gt;
and filled it with various applications ideas from a
&lt;a class="link" href="https://discuss.kde.org/t/application-ideas/150" target="_blank" rel="noopener"
&gt;discourse thread&lt;/a&gt;. Feel free
to take one of the open task or suggests a new app.&lt;/p&gt;
&lt;p&gt;Additionally I also created a
&lt;a class="link" href="https://go.kde.org/matrix/#/#kde-apps-initiative:kde.org" target="_blank" rel="noopener"
&gt;Matrix room&lt;/a&gt;
to have a room for conversation.&lt;/p&gt;</description></item><item><title>MarkNote 1.2</title><link>https://carlschwan.eu/2024/05/17/marknote-1.2/</link><pubDate>Fri, 17 May 2024 16:15:35 +0000</pubDate><guid>https://carlschwan.eu/2024/05/17/marknote-1.2/</guid><description>&lt;p&gt;The MarkNote team is happy to announce the 1.2 release of MarkNote, KDE&amp;rsquo;s
WYSIWYG note-taking application. Marknote lets you create rich text notes and
easily organise them into notebooks. You can personalise your notebooks by
choosing an icon and accent color for each one, making it easy to distinguish
between them and keep your notes at your fingertips. Your notes are saved as
Markdown files in your Documents folder, making it easy to use your notes
outside of Marknote as well as inside the app.&lt;/p&gt;
&lt;h2 id="notes-management"&gt;Notes management&lt;/h2&gt;
&lt;p&gt;This releases brings highly wanted features like the ability to choose a custom
folder where to store your notes. Mathis Brüchert also added the ability to
change the sorting of notes from alphabetically to by date.&lt;/p&gt;
&lt;p&gt;Mathis made the sidebar collapsable and added a focus mode where everything but
the editing page is displayed.&lt;/p&gt;
&lt;figure style="max-width: 100%; margin-left: auto; margin-right: auto;" class="embed-responsive embed-responsive-16by9"&gt;
&lt;video src="sidebar.mp4" loop autoplay muted&gt;&lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;Finally if you prefer to just use Marknote as a Markdown editor, we made it
possible to just open any markdown files directly from the file browser or the
console. Additionally Marknote supports markdown files with a so called front
matter, which is a common way to inject metadata to markdown in static website
generators like Hugo and Jekyll.&lt;/p&gt;
&lt;h2 id="editing"&gt;Editing&lt;/h2&gt;
&lt;p&gt;In term of edition support, the subset of markdown supported increased again.
Now it is possible to add and edit tables.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/05/17/marknote-1.2/table.png" data-size="926x850"&gt;
&lt;img src="https://carlschwan.eu/2024/05/17/marknote-1.2/table.png" width="926" height="850" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Additionally we started transforming inline markdown directly to rich text as
you type. Support is limited to a few markdown constructs but is likely to
grow over time.&lt;/p&gt;
&lt;figure style="max-width: 100%; margin-left: auto; margin-right: auto;"&gt;
&lt;video style="box-shadow: var(--shadow-l4); border-radius: 5px;" src="transform.mp4" loop autoplay muted&gt;&lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;You can now customize the font used by editor.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/05/17/marknote-1.2/settings.png" data-size="410x432"&gt;
&lt;img src="https://carlschwan.eu/2024/05/17/marknote-1.2/settings.png" width="410" height="432" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Aside from being able to edit text, it&amp;rsquo;s now possible to also create sketches
directly from MarkNote.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/05/17/marknote-1.2/sketches.png" data-size="1497x999"&gt;
&lt;img src="https://carlschwan.eu/2024/05/17/marknote-1.2/sketches.png" width="1497" height="999" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="mobile-support"&gt;Mobile Support&lt;/h2&gt;
&lt;p&gt;Mathis took care of ensuring MarkNote was fully usable when used with Plasma Mobile.&lt;/p&gt;
&lt;figure style="max-width: 100%; margin-left: auto; margin-right: auto;"&gt;
&lt;video style="box-shadow: var(--shadow-l4); border-radius: 5px; " src="mobile.mp4" loop autoplay muted&gt;&lt;/video&gt;
&lt;/figure&gt;
&lt;h2 id="windows-and-macos-support"&gt;Windows and macOS support&lt;/h2&gt;
&lt;p&gt;Marknote now provides nightly builds for Windows and macOS. While the Windows
builds should be fully usable, the macOS build still has an issue where most
icons are not displayed. This should be fixed as soon as we can make use of
the new KIconTheme version.&lt;/p&gt;
&lt;p&gt;As part of the work to improve the macOS support, Marknote also gained global
menu support for Linux.&lt;/p&gt;
&lt;h2 id="others"&gt;Others&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The command bar will show translated shortcuts. (Laurent Montel)&lt;/li&gt;
&lt;li&gt;Unify spelling of MarkNote and fix typos in the README.md (Jonah Brüchert)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;Mathis created a Matrix channel for MarkNote:
&lt;a class="link" href="https://go.kde.org/matrix/#/#marknote:kde.org" target="_blank" rel="noopener"
&gt;#marknote:kde.org&lt;/a&gt;. There is
also still a lot of small improvements that can be done everywhere and which
don&amp;rsquo;t require a lot of programming experience. Take a look at these two tasks
&lt;a class="link" href="https://invent.kde.org/office/marknote/-/issues/31" target="_blank" rel="noopener"
&gt;!31&lt;/a&gt; and
&lt;a class="link" href="https://invent.kde.org/office/marknote/-/issues/27" target="_blank" rel="noopener"
&gt;!27&lt;/a&gt; for some inspiration
on what you could work on.&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.2</title><link>https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/</link><pubDate>Thu, 09 May 2024 20:00:00 +0000</pubDate><guid>https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/</guid><description>&lt;p&gt;Kirigami Addons 1.2 is out with some accessibility fixes and one new component:
FloatingToolBar.&lt;/p&gt;
&lt;h2 id="accessibility"&gt;Accessibility&lt;/h2&gt;
&lt;p&gt;During the accessibility sprint, there was an effort to ensure the date and
time pickers were actually accessible. Aside from improving the screen reader
support, this also allow to write Selenium integration tests which uses these
components in Itinerary. Thanks Volker, David Redundo and others for working
on this!&lt;/p&gt;
&lt;h2 id="floatingtoolbar"&gt;FloatingToolBar&lt;/h2&gt;
&lt;p&gt;Mathis and I worked on a new addition to Kirigami Addons adding to the existing
&lt;code&gt;FloatingButton&lt;/code&gt; and &lt;code&gt;DoubleFloatingButton&lt;/code&gt; components. This component is perfect
to add tool to editing and drawing areas and can either contain a simple
&lt;code&gt;RowLayout&lt;/code&gt;/&lt;code&gt;ColumnLayout&lt;/code&gt; containing ToolButtons or a &lt;code&gt;Kirigami.ActionToolBar&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigami&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;FloatingToolBar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;contentItem:&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ActionToolBar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;actions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/floatingtoolbar.png" data-size="890x191"&gt;
&lt;img src="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/floatingtoolbar.png" width="890" height="191" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="dialogs"&gt;Dialogs&lt;/h2&gt;
&lt;p&gt;With the style used by &lt;code&gt;FormCardDialog&lt;/code&gt; and &lt;code&gt;MessageDialog&lt;/code&gt; merged in
&lt;a class="link" href="https://invent.kde.org/frameworks/kirigami/-/merge_requests/1519" target="_blank" rel="noopener"
&gt;Kirigami&lt;/a&gt; and soon in
&lt;a class="link" href="https://invent.kde.org/frameworks/qqc2-desktop-style/-/merge_requests/405" target="_blank" rel="noopener"
&gt;qqc2-desktop-style&lt;/a&gt;
too, I did some changes to the &lt;code&gt;FormCardDialog&lt;/code&gt; and &lt;code&gt;MessageDialog&lt;/code&gt; to use the
same padding as &lt;code&gt;Kirigami.Dialog&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;MessageDialog now works better on mobile with the layout adapting itself to the dialog size.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/messagedialog-mobile.png" data-size="341x638"&gt;
&lt;img src="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/messagedialog-mobile.png" width="341" height="638" loading="lazy"
alt="messagedialog with a mobile layout"&gt;
&lt;/a&gt;
&lt;figcaption&gt;messagedialog with a mobile layout&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Aditionally similar to KMessageBox, MessageDialog has an optional &amp;ldquo;don&amp;rsquo;t show again&amp;rdquo; option
which can be enabled by setting the &lt;code&gt;dontShowAgainName&lt;/code&gt; property similar to the KMessageBox api.&lt;/p&gt;
&lt;p&gt;I also prepared these two components to work as standalone windows which is likely to
come with &lt;a class="link" href="https://codereview.qt-project.org/c/qt/qtdeclarative/&amp;#43;/556010" target="_blank" rel="noopener"
&gt;this Qt 6.8 change request&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/qt68.png" data-size="863x667"&gt;
&lt;img src="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/qt68.png" width="863" height="667" loading="lazy"
alt="Dialog in Qt 6.8"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Dialog in Qt 6.8&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="categorizedsettings"&gt;CategorizedSettings&lt;/h2&gt;
&lt;p&gt;Jonah fixed a bug where it would be impossible to escape the settings on mobile.&lt;/p&gt;
&lt;h2 id="documentation"&gt;Documentation&lt;/h2&gt;
&lt;p&gt;I added more screenshot to the API documentation and updated the TableView example app
to use a &amp;lsquo;frameless&amp;rsquo; style.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/tableview.png" data-size="732x859"&gt;
&lt;img src="https://carlschwan.eu/2024/05/09/kirigami-addons-1.2/tableview.png" width="732" height="859" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="qt-67-support"&gt;Qt 6.7 support&lt;/h2&gt;
&lt;p&gt;This release also brings support for Qt 6.7 on Android as this release introduced
an API and ABI change to the Android code. Thanks Joshua for tackling this issue.&lt;/p&gt;</description></item><item><title>Improvements to QTextDocument</title><link>https://carlschwan.eu/2024/04/14/improvements-to-qtextdocument/</link><pubDate>Sun, 14 Apr 2024 20:00:35 +0000</pubDate><guid>https://carlschwan.eu/2024/04/14/improvements-to-qtextdocument/</guid><description>&lt;p&gt;&lt;a class="link" href="https://apps.kde.org/marknote/" target="_blank" rel="noopener"
&gt;Marknote&lt;/a&gt; uses QTextDocument for its WYSIWYG
text editor. This is surpringly quite powerful and thanks to some code borrowed
from KMail rich text editor, it wasn&amp;rsquo;t hard to implement huge part of the
markdown specification.&lt;/p&gt;
&lt;p&gt;But while QTextDocument is great, I hit quickly some limits. This is why I
started fixing some of them and I already have some patches up for review in Qt.&lt;/p&gt;
&lt;h2 id="default-table-style"&gt;Default table style&lt;/h2&gt;
&lt;p&gt;By default the style of the tables looks straight from the 90s, I submitted a
patch to use something a bit nicer: &lt;a class="link" href="https://codereview.qt-project.org/c/qt/qtbase/&amp;#43;/554132" target="_blank" rel="noopener"
&gt;https://codereview.qt-project.org/c/qt/qtbase/+/554132&lt;/a&gt;
(merged!)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/14/improvements-to-qtextdocument/table-old.png" data-size="666x155"&gt;
&lt;img src="https://carlschwan.eu/2024/04/14/improvements-to-qtextdocument/table-old.png" width="666" height="155" loading="lazy"
alt="Old style"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Old style&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/14/improvements-to-qtextdocument/table-new.png" data-size="666x155"&gt;
&lt;img src="https://carlschwan.eu/2024/04/14/improvements-to-qtextdocument/table-new.png" width="666" height="155" loading="lazy"
alt="New style"&gt;
&lt;/a&gt;
&lt;figcaption&gt;New style&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;This is easy to change in the app itself and this is already the case in
Marknote and NeoChat table rendering, but by default I believe Qt should
provides a nice style so that apps don&amp;rsquo;t need to figure out how to overwrite
the default style.&lt;/p&gt;
&lt;p&gt;While working on this, I also noticed that the border-collapse property was not
supported by the layout engine in QtQuick and only in QtWidget. This resulted
in the border of the tables to be twice as thick as they should be. This was
fortunately fixed by a simple patch:
&lt;a class="link" href="https://codereview.qt-project.org/c/qt/qtdeclarative/&amp;#43;/554151" target="_blank" rel="noopener"
&gt;https://codereview.qt-project.org/c/qt/qtdeclarative/+/554151&lt;/a&gt; (merged!)&lt;/p&gt;
&lt;h2 id="responsive-images"&gt;Responsive images&lt;/h2&gt;
&lt;p&gt;A width and an height can be assigned to an image in a QTextDocument by using
the respectives html attributes &lt;code&gt;&amp;lt;img height=&amp;quot;500&amp;quot; width=&amp;quot;500&amp;quot; \&amp;gt;&lt;/code&gt;.This works
correctly when the window size is known and static but less so then the window
can be resized as will be the user. A common technique in web design is to add
the following styles to the website:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And while QTextDocument support some CSS properties, &lt;code&gt;max-width&lt;/code&gt; wasn&amp;rsquo;t
implemented. Adding the support for more properties in the HTML and CSS
parser is quite simple as it was just forwarding the value of the max-width
attribute to the QTextImageHandler, and a bit complicated was the handling of
the &lt;code&gt;%&lt;/code&gt; unit as previously only &lt;code&gt;px&lt;/code&gt; and &lt;code&gt;em&lt;/code&gt; was supported by Qt.&lt;/p&gt;
&lt;p&gt;This resulted in two more patches, one for &lt;a class="link" href="https://codereview.qt-project.org/c/qt/qtbase/&amp;#43;/554130" target="_blank" rel="noopener"
&gt;QtTextDocument and QtWidget&lt;/a&gt;
and one for &lt;a class="link" href="https://codereview.qt-project.org/c/qt/qtdeclarative/&amp;#43;/554131" target="_blank" rel="noopener"
&gt;QtQuick&lt;/a&gt;.&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;video class="embed-responsive-item" src="https://carlschwan.eu/2024/04/14/improvements-to-qtextdocument/img-fluid.mp4" controls allowfullscreen&gt;&lt;/video&gt;
&lt;/div&gt;
&lt;h2 id="future"&gt;Future&lt;/h2&gt;
&lt;p&gt;Hopefully these patches will be reviewed and merged soon. Afterward I want
to add support for a few more CSS properties like: &lt;code&gt;border-radius&lt;/code&gt; for images
and some other elements, as well the &lt;code&gt;border-{width, style, color}&lt;/code&gt; properties
for paragraphs to better support &lt;code&gt;&amp;lt;blockquote /&amp;gt;&lt;/code&gt; (important when displaying
emails).&lt;/p&gt;</description></item><item><title>Marknote 1.1.0</title><link>https://carlschwan.eu/2024/04/01/marknote-1.1.0/</link><pubDate>Mon, 01 Apr 2024 15:05:00 +0000</pubDate><guid>https://carlschwan.eu/2024/04/01/marknote-1.1.0/</guid><description>&lt;p&gt;Marknote 1.1.0 is out! Marknote is the new WYSIWYG note-taking application from
KDE. Despite the latest release being just a few days ago, we have been hard at
work and added a few new features and, more importantly, fixed some bugs.&lt;/p&gt;
&lt;p&gt;Marknote now boasts broader Markdown support, and can now display images and
task lists in the editor. And once you are done editing your notes, you can
export them to various formats, including PDF, HTML and ODT.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/01/marknote-1.1.0/export.png" data-size="3542x950"&gt;
&lt;img src="https://carlschwan.eu/2024/04/01/marknote-1.1.0/export.png" width="3542" height="950" loading="lazy"
alt="Export to PDF, HTML and ODT"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Export to PDF, HTML and ODT&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Marknote&amp;rsquo;s interface now seamlessly integrates the colors assigned to your
notebooks, enhancing its visual coherence and making it easier to distinguish
one notebook from another. Additionally, your notebooks remember the last
opened note, automatically reopening it upon selection.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/01/marknote-1.1.0/colors.png" data-size="1287x999"&gt;
&lt;img src="https://carlschwan.eu/2024/04/01/marknote-1.1.0/colors.png" width="1287" height="999" loading="lazy"
alt="Accent color in list delegate"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Accent color in list delegate&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve also introduced a convenient command bar similar to the one in Merkuro.
This provides quick access to essential actions within Marknote. Currently it
only creates a new notebook and note, but we plan to make more actions
available in the future. Finally we have reworked all the dialogs in Markdown
to use the newly introduced &lt;a class="link" href="https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/" &gt;FormCardDialog from KirigamiAddons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/01/marknote-1.1.0/commandbar.png" data-size="1287x999"&gt;
&lt;img src="https://carlschwan.eu/2024/04/01/marknote-1.1.0/commandbar.png" width="1287" height="999" loading="lazy"
alt="Command bar"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Command bar&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We have created a small &lt;a class="link" href="https://invent.kde.org/office/marknote/-/issues/27" target="_blank" rel="noopener"
&gt;feature roadmap&lt;/a&gt;
with features we would like to add in the future. Contributions are welcome!&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/marknote/marknote-1.1.1.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that this release introduce a new recommanded dependencies: md4c and require
the latest Kirigami Addons release (published a few hours ago).&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.1.0</title><link>https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/</link><pubDate>Mon, 01 Apr 2024 15:00:00 +0000</pubDate><guid>https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/</guid><description>&lt;p&gt;It&amp;rsquo;s again time for a new Kirigami Addons release. Kirigami Addons is a
collection of helpful components for your QML and Kirigami applications.&lt;/p&gt;
&lt;h2 id="formcard"&gt;FormCard&lt;/h2&gt;
&lt;p&gt;I added a new FormCard delegate: &lt;code&gt;FormColorDelegate&lt;/code&gt; which allow to select a
color and a new delegate container: &lt;code&gt;FormCardDialog&lt;/code&gt; which is a new type of
dialog.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/formcarddialog.png" data-size="373x359"&gt;
&lt;img src="https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/formcarddialog.png" width="373" height="359" loading="lazy"
alt="FormCardDialog containing a FormColorDelegate in Marknote"&gt;
&lt;/a&gt;
&lt;figcaption&gt;FormCardDialog containing a FormColorDelegate in Marknote&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Aside from these new components, Joshua fixed a newline bug in the &lt;code&gt;AboutKDE&lt;/code&gt;
component and I updated the code examples in the API documentation.&lt;/p&gt;
&lt;h2 id="tableview"&gt;TableView&lt;/h2&gt;
&lt;p&gt;This new component is intended to provide a powerful table view on top of the
barebone one provided by QtQuick and similar to the one we have in our
QtWidgets application.&lt;/p&gt;
&lt;p&gt;This was contributed by Evgeny Chesnokov. Thanks!&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/tableview.png" data-size="732x859"&gt;
&lt;img src="https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/tableview.png" width="732" height="859" loading="lazy"
alt="TableView with resizable and sortable columns"&gt;
&lt;/a&gt;
&lt;figcaption&gt;TableView with resizable and sortable columns&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="other-components"&gt;Other components&lt;/h2&gt;
&lt;p&gt;The default size of &lt;code&gt;MessageDialog&lt;/code&gt; was decreased and is now more appropriate.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/messagedialog.png" data-size="548x341"&gt;
&lt;img src="https://carlschwan.eu/2024/04/01/kirigami-addons-1.1.0/messagedialog.png" width="548" height="341" loading="lazy"
alt="MessageDialog new default size"&gt;
&lt;/a&gt;
&lt;figcaption&gt;MessageDialog new default size&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;James Graham fixed the autoplay of the video delegate for the maximized album
component.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.1.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kirigami Addons 1.0</title><link>https://carlschwan.eu/2024/02/22/kirigami-addons-1.0/</link><pubDate>Thu, 22 Feb 2024 10:00:00 +0000</pubDate><guid>https://carlschwan.eu/2024/02/22/kirigami-addons-1.0/</guid><description>&lt;p&gt;A new version of Kirigami Addons is out! Kirigami Addons is a collection of
helpful components for your QML and Kirigami applications. With the 1.0
release, we are now supporting Qt6 and KF6 and added a bunch of new components
and fixed various accessibility issues.&lt;/p&gt;
&lt;h2 id="formcard"&gt;FormCard&lt;/h2&gt;
&lt;p&gt;We added a bunch of new FormCard delegates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FormPasswordFieldDelegate&lt;/code&gt;: A password field&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FormDataTimeDelegate&lt;/code&gt;: A date and/or time delegate with integrated date and
time picker which use the native picker of the platform if available
(currently only on Android).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/02/22/kirigami-addons-1.0/formcard.png" data-size="932x659"&gt;
&lt;img src="https://carlschwan.eu/2024/02/22/kirigami-addons-1.0/formcard.png" width="932" height="659" loading="lazy"
alt="Form card example"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Form card example&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The existing delegates also recevied various accessibility issues when used
with a screen reader.&lt;/p&gt;
&lt;p&gt;Finally we droped the compatibility alias &lt;code&gt;MobileForm&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="bottomdrawer"&gt;BottomDrawer&lt;/h2&gt;
&lt;p&gt;Mathis added a new Drawer component that can be used a context menu or to
display some information on mobile.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2024/02/22/kirigami-addons-1.0/bottomdrawer.png" data-size="501x746"&gt;
&lt;img src="https://carlschwan.eu/2024/02/22/kirigami-addons-1.0/bottomdrawer.png" width="501" height="746" loading="lazy"
alt="Bottom Drawer in Itinerary showing the information about a station on the map"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Bottom Drawer in Itinerary showing the information about a station on the map&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="floatingbutton-and-doublefloatingbutton"&gt;FloatingButton and DoubleFloatingButton&lt;/h2&gt;
&lt;p&gt;These two components received significant sizing and consistency improvements
which should improve their touch area on mobile.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.0.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Announcing Brise theme</title><link>https://carlschwan.eu/2023/12/19/announcing-brise-theme/</link><pubDate>Tue, 19 Dec 2023 10:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/12/19/announcing-brise-theme/</guid><description>&lt;script
defer
src="https://carlschwan.eu/comparaison.js"&gt;&lt;/script&gt;
&lt;link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/img-comparison-slider@8/dist/styles.css"
/&gt;
&lt;style&gt;
:root {
--divider-color: #fff;
}
@media (prefers-color-scheme: dark) {
:root {
--divider-color: #fff;
}
}
&lt;/style&gt;
&lt;p&gt;&lt;a class="link" href="https://invent.kde.org/carlschwan/brise/" target="_blank" rel="noopener"
&gt;Brise theme&lt;/a&gt; is yet another fork of
Breeze. The name comes having both the French and German translations of Breeze,
being Brise.&lt;/p&gt;
&lt;p&gt;As some people know, I&amp;rsquo;m contributing quite a lot to the Breeze style for
the Plasma 6 release and I don&amp;rsquo;t intend to stop doing that. Both git repositories
share the same git history and I didn&amp;rsquo;t massively rename all the C++ classes from
BreezeStyle to BriseStyle to make it as easy as possible to backport commits
from one repository to the other. There are also no plans to make this the new
default style for Plasma.&lt;/p&gt;
&lt;p&gt;My goal with this Qt style is to have a style that is not a big departure of
Breeze like you know it but does contain some cosmetic small changes. This
would serve as a place where I can experiment with new ideas and if
they tend to be popular to then move them to Breeze.&lt;/p&gt;
&lt;p&gt;Here is a breakdown of all the changes I made so far.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I made Brise coinstallable with Breeze, so that users can have both installed
simultaneously. I minified the changes to avoid merge conflicts while
doing so.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I increased the border radius of all the elements from 3 pixels to 5 pixels.
This value is configurable between small (3 pixels), medium (5 pixels) and large
(7 pixels). A &lt;a class="link" href="https://invent.kde.org/plasma/breeze/-/merge_requests/388" target="_blank" rel="noopener"
&gt;merge request&lt;/a&gt;
was opened in Breeze and might make it into Plasma 6.1. The only difference is that
in breeze the default will likely keep being 3 pixels for the time being.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/12/19/announcing-brise-theme/rounded.png" data-size="858x561"&gt;
&lt;img src="https://carlschwan.eu/2023/12/19/announcing-brise-theme/rounded.png" width="858" height="561" loading="lazy"
alt="Cute buttons and frames with 5 pixels border radius"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Cute buttons and frames with 5 pixels border radius&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a separator between the search field and the title in the standard KDE
config windows which serves as an extension of the separator between the list
of the setting&amp;rsquo;s categories and the setting&amp;rsquo;s page. This is mostly to be similar to System
Settings and other Kirigami applications. There is a pending merge
request for this also in &lt;a class="link" href="https://invent.kde.org/plasma/breeze/-/merge_requests/371" target="_blank" rel="noopener"
&gt;Breeze&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A new tab style that removes the blue lines from the active lines and
introduce other small changes. Non-editable tabs are also now filling the
entire horizontal space available. I&amp;rsquo;m not completely happy with the look
yet, so no merge requests have been submitted to Breeze.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/12/19/announcing-brise-theme/separator.png" data-size="1207x228"&gt;
&lt;img src="https://carlschwan.eu/2023/12/19/announcing-brise-theme/separator.png" width="1207" height="228" loading="lazy"
alt="Separator in the toolbar and the new tabs"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Separator in the toolbar and the new tabs&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remove outlines from menu and combobox items. My goal is to go in the same
direction as KirigamiAddons.RoundedItemDelegate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/12/19/announcing-brise-theme/menu.png" data-size="406x348"&gt;
&lt;img src="https://carlschwan.eu/2023/12/19/announcing-brise-theme/menu.png" width="406" height="348" loading="lazy"
alt="Menu without outlines"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Menu without outlines&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure that all the controls have the same height. Currently a small disparency in height is noticeable when they are in the same row. The patch is still a bit hacky and needs some wider testing on a large range of apps to
ensure no regressions, but it is also a improvement I will definitively submit upstream
once I feel like it&amp;rsquo;s ready.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/12/19/announcing-brise-theme/alignment1.png" data-size="1166x175"&gt;
&lt;img src="https://carlschwan.eu/2023/12/19/announcing-brise-theme/alignment1.png" width="1166" height="175" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/12/19/announcing-brise-theme/alignment2.png" data-size="1172x123"&gt;
&lt;img src="https://carlschwan.eu/2023/12/19/announcing-brise-theme/alignment2.png" width="1172" height="123" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Here, in these two screenshots, every control has 35 pixels as height.&lt;/p&gt;
&lt;p&gt;Finally here is Kate and KMail&amp;rsquo;s settings with Breeze and Brise.&lt;/p&gt;
&lt;img-comparison-slider class="w-100" &gt;
&lt;img slot="first" class="w-100" src="kate-breeze.png" /&gt;
&lt;img slot="second" class="w-100" src="kate-brise.png" /&gt;
&lt;svg slot="handle" xmlns="http://www.w3.org/2000/svg" width="100" viewBox="-8 -3 16 6"&gt;
&lt;path stroke="var(--divider-color)" d="M -5 -2 L -7 0 L -5 2 M -5 -2 L -5 2 M 5 -2 L 7 0 L 5 2 M 5 -2 L 5 2" stroke-width="1" fill="var(--divider-color)" vector-effect="non-scaling-stroke"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/img-comparison-slider&gt;
&lt;img-comparison-slider class="w-100" &gt;
&lt;img slot="first" class="w-100" src="kmail-breeze.png" /&gt;
&lt;img slot="second" class="w-100" src="kmail-brise.png" /&gt;
&lt;svg slot="handle" xmlns="http://www.w3.org/2000/svg" width="100" viewBox="-8 -3 16 6"&gt;
&lt;path stroke="var(--divider-color)" d="M -5 -2 L -7 0 L -5 2 M -5 -2 L -5 2 M 5 -2 L 7 0 L 5 2 M 5 -2 L 5 2" stroke-width="1" fill="var(--divider-color)" vector-effect="non-scaling-stroke"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/img-comparison-slider&gt;</description></item><item><title>KQuickImageEditor 0.3.0 and Kirigami Adddons 0.12 alpha</title><link>https://carlschwan.eu/2023/11/02/kquickimageeditor-0.3.0-and-kirigami-adddons-0.12-alpha/</link><pubDate>Thu, 02 Nov 2023 16:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/11/02/kquickimageeditor-0.3.0-and-kirigami-adddons-0.12-alpha/</guid><description>&lt;p&gt;Two new releases are out in preparation of the first alpha release or the February
Megarelease.&lt;/p&gt;
&lt;h2 id="kquickimageeditor-030"&gt;KQuickImageEditor 0.3.0&lt;/h2&gt;
&lt;p&gt;This is the new stable version of KQuickImageEditor. Only notable change it the
support for Qt6 in addition of Qt5 support.&lt;/p&gt;
&lt;h2 id="kirigami-addons-01175"&gt;Kirigami Addons 0.11.75&lt;/h2&gt;
&lt;p&gt;This is an alpha release and depends on an unreleased KDE Frameworks. Please
only package it if you also package the coming alpha megarelease.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/unstable/kirigami-addons/kirigami-addons-0.11.75.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org (kirigami addons)&lt;/a&gt; and &lt;a class="link" href="https://download.kde.org/stable/kquickimageeditor/kquickimageeditor-0.3.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org (kquickimageeditor)&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Doxyqml 0.5.3 release</title><link>https://carlschwan.eu/2023/10/14/doxyqml-0.5.3-release/</link><pubDate>Sat, 14 Oct 2023 08:30:00 +0000</pubDate><guid>https://carlschwan.eu/2023/10/14/doxyqml-0.5.3-release/</guid><description>&lt;p&gt;A new release of Doxyqml is out. Doxyqml is the tool which we use to transform
our QML code in non working C++ code, but good enough for Doxyqml to understand
it. This power the documentation of Kirigami, Kirigami Addons, Plasma Framework
and more on &lt;a class="link" href="https://api.kde.org" target="_blank" rel="noopener"
&gt;api.kde.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This release mainly fix various compatibility bugs with more recent QML versions.&lt;/p&gt;
&lt;p&gt;Thanks to Frederik Gladhorn, Joshua Goins, Libor Tomsik, Matej Starc and Nikolai
R Kristiansen for their contributions to this release.&lt;/p&gt;
&lt;p&gt;Here is the full release log:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add support for single quote strings(Carl Schwan)&lt;/li&gt;
&lt;li&gt;Ignore private functions in generated api (Carl Schwan)&lt;/li&gt;
&lt;li&gt;Add support for typed functions (Carl Schwan)&lt;/li&gt;
&lt;li&gt;qmlclass.py::add_import: dynamically get import qualifier (Matej Starc)&lt;/li&gt;
&lt;li&gt;Allow arbitrary default arguments (Frederik Gladhorn)&lt;/li&gt;
&lt;li&gt;Fix wasteful newline if there was no meaningful comment (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Change expected test data to account for the new comments (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Simplify header comments, add version/import at the very last minute (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t parse internal module types (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Use @version instead of @since command (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Add import statements for QML components (Joshua Goins)&lt;/li&gt;
&lt;li&gt;Support array literals as function args (Nikolai R Kristiansen)&lt;/li&gt;
&lt;li&gt;Fixed parsing curly brackets in JS function (Libor Tomsik)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The release can be found on &lt;a class="link" href="https://pypi.org/project/doxyqml/" target="_blank" rel="noopener"
&gt;PyPi.org&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Linux Days Voralberg</title><link>https://carlschwan.eu/2023/10/03/linux-days-voralberg/</link><pubDate>Tue, 03 Oct 2023 14:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/10/03/linux-days-voralberg/</guid><description>&lt;p&gt;Last weekend I went to the Linux Days in Voralberg (Austria) to host a booth
with Tobias and Kai. It was hosted at the Fachhochschule (a sort of university
for applied science) in Dornbirn and it was my first time attending this event.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/entrance.jpg" data-size="1152x864"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/entrance.jpg" width="1152" height="864" loading="lazy"
alt="Me and Tobias in front of the LinuxDays poster at the entrance of the event"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Me and Tobias in front of the LinuxDays poster at the entrance of the event&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Our booth was well visited and we had a lot of interesting discussions. As
always, we had various pieces of hardware on our booth: 2 laptops, a Steam Deck,
a Pinephone, a graphic tablet with Krita and two Konqi amigurumis.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/stand.jpg" data-size="720x720"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/stand.jpg" width="720" height="720" loading="lazy"
alt="Our stand"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Our stand&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Between booth duty, I still managed to watched one talk about open source
deployment in public institutions in Baden Wurtenberg (a region/state in German).
After the linux days, we all went to a restaurant and mass ordered Käsespätzle.
Käsespätzle is a traditional food from this region and is made of cheese, Spätzle
(noodles) and onions. It was excellent.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/food1.jpg" data-size="1000x750"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/food1.jpg" width="1000" height="750" loading="lazy"
alt="Käsespätzle"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Käsespätzle&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;On Sunday, Tobias and I went to Golm with a local we met the day before. We
took a gondola lift to reach a high-rope park in the mountains and then took an
Alpine Coaster to go back in the valley. It was a lot of fun.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/gondola.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/gondola.jpg" width="750" height="1000" loading="lazy"
alt="The view from the gondola"&gt;
&lt;/a&gt;
&lt;figcaption&gt;The view from the gondola&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/park.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/park.jpg" width="750" height="1000" loading="lazy"
alt="Picture of the high-rope pakr"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of the high-rope pakr&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;After our little adventure, we again went to eat in a traditional restaurant.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/food2.jpg" data-size="1000x750"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/food2.jpg" width="1000" height="750" loading="lazy"
alt="Fish in a plate with noodles and pumpkin"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Fish in a plate with noodles and pumpkin&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Here a few more pictures of the trip:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/dornbirn.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/dornbirn.jpg" width="750" height="1000" loading="lazy"
alt="Dornbirn market place"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Dornbirn market place&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/10/03/linux-days-voralberg/castel.jpg" data-size="1000x750"&gt;
&lt;img src="https://carlschwan.eu/2023/10/03/linux-days-voralberg/castel.jpg" width="1000" height="750" loading="lazy"
alt="Castel"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Castel&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Matrix Community Summit and KDE Promo Sprint in Berlin</title><link>https://carlschwan.eu/2023/09/24/matrix-community-summit-and-kde-promo-sprint-in-berlin/</link><pubDate>Sun, 24 Sep 2023 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/09/24/matrix-community-summit-and-kde-promo-sprint-in-berlin/</guid><description>&lt;p&gt;On Thursday and Friday evenings, I went to the Matrix Community Summit at C-Base
in Berlin with Tobias. It was the occasion to meet a few other Matrix
developers particularly the Nheko developer, MTRNord and a few other devs
whom I only knew by nickname. It was great even though I could only spend a
few hours there. Tobias stayed longer and will be able to blog more about
the event.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/24/matrix-community-summit-and-kde-promo-sprint-in-berlin/cbase.jpg" data-size="3000x2882"&gt;
&lt;img src="https://carlschwan.eu/2023/09/24/matrix-community-summit-and-kde-promo-sprint-in-berlin/cbase.jpg" width="3000" height="2882" loading="lazy"
alt="Photo of the C-Base showing a lot of electronical equipements"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Photo of the C-Base showing a lot of electronical equipements&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;During the weekend, instead of going to the Matrix summit, I participated to
the KDE Promo sprint with Paul, Aniqa, Niccolo, Volker, Joseph. Aron also
joined us via video call on Saturday. This event was also in Berlin at the KDAB
officem which we are very thankful for hosting us.&lt;/p&gt;
&lt;p&gt;This sprint was the perfect occasion to move forward with many of our pending
tasks. I mainly worked on web-related projects as I tried to work on a few items
on my large todo list.&lt;/p&gt;
&lt;p&gt;We now have an updated donation page, which includes the new donnorbox widget.
Donnorboy is now our preferred way to make recurring donations and recurring
donations are vital to the success of KDE. &lt;a class="link" href="https://kde.org/community/donations/" target="_blank" rel="noopener"
&gt;Check it out!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/24/matrix-community-summit-and-kde-promo-sprint-in-berlin/donation.png" data-size="1320x830"&gt;
&lt;img src="https://carlschwan.eu/2023/09/24/matrix-community-summit-and-kde-promo-sprint-in-berlin/donation.png" width="1320" height="830" loading="lazy"
alt="Screenshot of the website KDE.org/community/donations"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Screenshot of the website KDE.org/community/donations&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;With Paul, we also looked at the next &lt;a class="link" href="kde.org/for" &gt;KDE For-pages&lt;/a&gt;. Two of them
are now done and we will publish them in the coming weeks. There are plans for
a few more and if you want to get involved there, this is the
&lt;a class="link" href="https://phabricator.kde.org/T13726" target="_blank" rel="noopener"
&gt;phabricator task to follow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also updated the &lt;a class="link" href="https://kde.org/for/kids/" target="_blank" rel="noopener"
&gt;KDE For Kids&lt;/a&gt; with the help of
Aniqa. It now features the book Ada &amp;amp; Zangemann from Matthias Kirschner
and Sandra Brandstätter that sensibilise kids to Free Software. Let me know if you have
other books suggestions for kids around Free Software and KDE that we can
include on our websites.&lt;/p&gt;
&lt;p&gt;This was only a short version of all the things we did during this sprint, I will
let the others blog about what they did. More blog posts will certainly pop up
on &lt;a class="link" href="https://planet.kde.org" target="_blank" rel="noopener"
&gt;planet.kde.org&lt;/a&gt; soon.&lt;/p&gt;
&lt;p&gt;The sprint would have been only possible thanks to the generous donation from
our users, so &lt;a class="link" href="https://kde.org/community/donations/" target="_blank" rel="noopener"
&gt;consider making a donation today&lt;/a&gt;!
Your donation also helps to pay for the cost of hosting conferences, server
infrastructure, and maintain KDE software.&lt;/p&gt;</description></item><item><title>Francis 1.0</title><link>https://carlschwan.eu/2023/09/08/francis-1.0/</link><pubDate>Fri, 08 Sep 2023 08:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/09/08/francis-1.0/</guid><description>&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/08/francis-1.0/francis-logo.png" data-size="128x128"&gt;
&lt;img src="https://carlschwan.eu/2023/09/08/francis-1.0/francis-logo.png" width="128" height="128" loading="lazy"
alt="Francis logo"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Francis logo&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Today is my birthday but it&amp;rsquo;s also the day Francis got its first release.
Francis is a pomodoro app, which was originally developed by Felipe Kinoshita.
The Pomodoro Technique is a time management method developed by Francesco
Cirillo in the late 1980s. It uses a kitchen timer to break work into
intervals, typically 25 minutes in length, separated by short breaks.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/08/francis-1.0/francis.png" data-size="751x769"&gt;
&lt;img src="https://carlschwan.eu/2023/09/08/francis-1.0/francis.png" width="751" height="769" loading="lazy"
alt="Francis screnshot"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Francis screnshot&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The app is very simple and can be used as inspiration to develop your own
Kirigami application.&lt;/p&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;If you are interested in helping, don&amp;rsquo;t hesitate to reach out in the Plasma Mobile
matrix channel (&lt;a class="link" href="https://matrix.to/#/#plasma-mobile:kde.org" target="_blank" rel="noopener"
&gt;#plasma-mobile:kde.org&lt;/a&gt;) and I
will be happy to guide you.&lt;/p&gt;
&lt;p&gt;I also regularly post about my progress on many KDE apps on my
&lt;a class="link" href="https://floss.social/@carlschwan" target="_blank" rel="noopener"
&gt;Mastodon account&lt;/a&gt;, so don&amp;rsquo;t hesitate to
follow me there ;)&lt;/p&gt;
&lt;p&gt;And in case you missed it, as a member of KDE&amp;rsquo;s fundraising working group, I
need to remind you that KDE e.V., the non-profit behind the KDE community
accepts &lt;a class="link" href="https://kde.org/fundraisers/yearend2022/" target="_blank" rel="noopener"
&gt;donations&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/francis/francis-1.0.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Freedom Not Fear 2023</title><link>https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/</link><pubDate>Thu, 07 Sep 2023 16:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/</guid><description>&lt;p&gt;Last weekend, I went to &lt;a class="link" href="https://freedomnotfear.org/" target="_blank" rel="noopener"
&gt;Freedom Not Fear 2023&lt;/a&gt; in
Brussels. Fnf is an unconference for and by European digital activists. It
covers various topics, from the latest terrible European law (Chat Control) to
discussing how to get more involved in our democracies.&lt;/p&gt;
&lt;p&gt;I usually attend more technical conferences, and it was refreshing to
participate in a conference where ethical and political discussions around
digital rights were a central topic. It was an occasion to meet people from
different backgrounds, from a Dutch politician (and self-proclaimed student
for life), to a member of various organizations (e.g. Edri, NlNet,
epicenter.works, Chatons, …) and journalists from Netzpolitik.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/love.jpg" data-size="730x1600"&gt;
&lt;img src="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/love.jpg" width="730" height="1600" loading="lazy"
alt="Encryption is Love, Encryption is democracy, Encryption is Safety"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Encryption is Love, Encryption is democracy, Encryption is Safety&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;On Friday evening, aside from the welcome talk, we had a presentation from the
European Data Protection Supervisor (Wojciech Wiewiórowski) about their work on
deploying a Mastodon instance for the EU institutions and how the lack of
subscribers makes it hard to justify continuing investing in it.&lt;/p&gt;
&lt;p&gt;The presentation is on &lt;a class="link" href="https://tube.network.europa.eu/videos/watch/4bac7534-6ce4-4583-9e04-b32d94777c27" target="_blank" rel="noopener"
&gt;Peertube&lt;/a&gt;
if someone wants to watch it.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/mastodon.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/mastodon.jpg" width="750" height="1000" loading="lazy"
alt="Picture of Wojciech Wiewiórowski presenting his talk"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of Wojciech Wiewiórowski presenting his talk&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;During the weekend, we had an unconference-style conference where everyone could
create a topic of discussion and present their work interactively. This worked
very well.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/topics.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/topics.jpg" width="750" height="1000" loading="lazy"
alt="Board with all the discussion topics"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Board with all the discussion topics&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Many participants were using Linux (and often with Plasma), but others were
unaware of KDE. So I did a small lighting talk about the KDE community and
presented a few utilities we create: Plasma, GCompris, Labplot, Krita, Merkuro
and Itinerary. Time was limited, so I couldn’t show everything we were doing,
but I hope this small list of software shows that we are covering many
different types of software.&lt;/p&gt;
&lt;p&gt;I prepared my slides the day before, as I saw some slots for lightning talks
were still available, and the new &lt;a class="link" href="https://kde.org/for" target="_blank" rel="noopener"
&gt;KDE For&lt;/a&gt; pages were of
great help. But it makes sense to have some slides provided by KDE Promo, which
can then be reused and modified depending on the audience. I’ll bring up the
idea at the next KDE Promo sprint in 2 weeks.&lt;/p&gt;
&lt;p&gt;Aside from the weekend, which was packed with discussion, we went on Monday to
the European Parlament and had a small presentation about how the European
Parlament works. We also had the opportunity to ask Patrick Breyer from the
German Pirate Party questions.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/demo.jpg" data-size="1000x750"&gt;
&lt;img src="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/demo.jpg" width="1000" height="750" loading="lazy"
alt="Picture of Carl Schwan in front of the European Parlament and a sign “Democraty in Action”"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of Carl Schwan in front of the European Parlament and a sign “Democraty in Action”&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/parlement.jpg" data-size="1000x750"&gt;
&lt;img src="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/parlement.jpg" width="1000" height="750" loading="lazy"
alt="Picture of the European Parlament"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of the European Parlament&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I enjoyed this conference and thank the organizers and Digital Courage for
organizing this event and the two MEPs for using some of their travel allowance
to bring many people to Brussels.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/fnf-group.jpeg" data-size="1764x1176"&gt;
&lt;img src="https://carlschwan.eu/2023/09/07/freedom-not-fear-2023/fnf-group.jpeg" width="1764" height="1176" loading="lazy"
alt="Group photo with most of the participants"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Group photo with most of the participants&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Frameless view with QtWidgets</title><link>https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/</link><pubDate>Tue, 29 Aug 2023 17:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/</guid><description>&lt;p&gt;One design characteristic of our QtWidgets is that they contain a lot of
frames and frames inside other frames. This worked well with Oxygen style and
its skeuomorphism shadow, less so with Breeze.&lt;/p&gt;
&lt;p&gt;I first thought this was inheriten with QtWidgets and couldn&amp;rsquo;t be fixed without
much effort. But fortunately, after looking a bit into Qt source codes and in
particular in the internals of QDockAreaLayout, I discovered that the engine to
draw and style the built-in components of QtWidgets: QStyle has a
&lt;code&gt;QStyle::PE_IndicatorDockWidgetResizeHandle&lt;/code&gt; primitive which allows drawing
separators between detachable docks and similarly there is &lt;code&gt;QStyle::CE_Splitter&lt;/code&gt;
to paint the separator between elements inside a QSplitter. This is huge
because this means instead of drawing frames, we can render separator and then
get rid of most of our frames in our apps.&lt;/p&gt;
&lt;p&gt;This is how Qt Linguist tool looks like with this change.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/linguistic.png" data-size="1130x959"&gt;
&lt;img src="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/linguistic.png" width="1130" height="959" loading="lazy"
alt="Linguist with instead of using frames for each dock, use separators"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Linguist with instead of using frames for each dock, use separators&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, there are still some places where we do want to draw frames, so
we can&amp;rsquo;t remove them all. I added a heuristic that tries to determine when to
draw one based on the spacing and margins of the parent layout. A heuristic is
never perfect, but an app can additionally force the style to display a frame
or vice versa force the style not to display it.&lt;/p&gt;
&lt;p&gt;For more complex apps (read with more custom components), this change require a
bit of tweaking but with a handful of one-liner in Kate, I got this modern look.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/kate.png" data-size="1332x955"&gt;
&lt;img src="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/kate.png" width="1332" height="955" loading="lazy"
alt="Kate without frames"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Kate without frames&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;This is not perfect yet and this will require a bit more tweaking around the
tab bar, but is already a big departure from the current style.&lt;/p&gt;
&lt;p&gt;Similar, this is how Dolphin and Ark, look with these changes:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/dolphin.png" data-size="1614x1023"&gt;
&lt;img src="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/dolphin.png" width="1614" height="1023" loading="lazy"
alt="Dolphin with a frameless split view"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Dolphin with a frameless split view&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/ark.png" data-size="1124x727"&gt;
&lt;img src="https://carlschwan.eu/2023/08/29/frameless-view-with-qtwidgets/ark.png" width="1124" height="727" loading="lazy"
alt="Ark with 3 panes, one on the top left with a the archive content, one on the right with the name of the archive and one on the bottom left with the archive comment"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Ark with 3 panes, one on the top left with a the archive content, one on the right with the name of the archive and one on the bottom left with the archive comment&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;If you like this change, don&amp;rsquo;t hesitate to put a 👍 on the
&lt;a class="link" href="https://invent.kde.org/plasma/breeze/-/merge_requests/342" target="_blank" rel="noopener"
&gt;pending MR&lt;/a&gt;, and if
you are a developer, please test your app with this change and look at how I
adapted a few apps already. I tried the change with some big apps like Krita
and Kdevelop and it looks good, but the more testing, the better it is.&lt;/p&gt;</description></item><item><title>Kirigami Addons 0.11.0</title><link>https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/</link><pubDate>Wed, 16 Aug 2023 06:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/</guid><description>&lt;p&gt;Kirigami Addons 0.11.0 is out! This release brings a bunch of new components.
Since I forgot to write an announcement for the 0.10 release, I will mention
some of the new features of 0.10 too.&lt;/p&gt;
&lt;h2 id="banner-0100"&gt;Banner (0.10.0)&lt;/h2&gt;
&lt;p&gt;This helpful component is similar to &lt;code&gt;Kirigami.InlineMessage&lt;/code&gt; and can be used
as the footer or header of a page.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/banner.webp" data-size="930x661"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/banner.webp" width="930" height="661" loading="lazy"
alt="Banner component at the top of page"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Banner component at the top of page&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="delegates-0100"&gt;Delegates (0.10.0)&lt;/h2&gt;
&lt;p&gt;Kirigami Addons 0.10.0 bring two new list and grid delegates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RoundedItemDelegate:&lt;/code&gt; This delegate provides a nice rounded background for items
inside a list or grid. You can see it in action in Arianna where it is used both
in the grid and the sidebar.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/delegates.png" data-size="1257x945"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/delegates.png" width="1257" height="945" loading="lazy"
alt="Grid rounded delegates and list rounded delegate"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Grid rounded delegates and list rounded delegate&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IndicatorItemDelegate&lt;/code&gt;: This component is the perfect list delegate for an inbox
where elements can be marked as read or unread (e.g in an email client).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/mail.png" data-size="1560x1041"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/mail.png" width="1560" height="1041" loading="lazy"
alt="Merkuro Mail folder view where some emails are marked as read and others are still unread"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Merkuro Mail folder view where some emails are marked as read and others are still unread&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="avatar-0100"&gt;Avatar (0.10.0)&lt;/h2&gt;
&lt;p&gt;We moved &lt;code&gt;Kirigami.Avatar&lt;/code&gt; from Kirigami to Kirigami Addons. We tweaked the API a
bit at the same time, and &lt;code&gt;Avatar&lt;/code&gt; is no longer based on the &lt;code&gt;AbstractButton&lt;/code&gt; component
but is just an &lt;code&gt;Item&lt;/code&gt; which can be used for decorative purposes.&lt;/p&gt;
&lt;p&gt;In 0.11.0, we also introduced AvatarButton that can be used if you need an
interactable element.&lt;/p&gt;
&lt;p&gt;We also updated the look of the placeholder, when no avatar images is found, to
be a bit less visually heavy and use a pale color. Fun fact, this is a design I
helped introduce in Nextcloud a year ago.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/avatar.png" data-size="408x428"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/avatar.png" width="408" height="428" loading="lazy"
alt="New avatar look"&gt;
&lt;/a&gt;
&lt;figcaption&gt;New avatar look&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="mobileformformheader-0100"&gt;MobileForm.FormHeader (0.10.0)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;FormCardHeader&lt;/code&gt; is now deprecated, and we are replacing it with &lt;code&gt;FormHeader&lt;/code&gt;.
The difference is that &lt;code&gt;FormHeader&lt;/code&gt; is placed outside of the cards.&lt;/p&gt;
&lt;h2 id="kirigami-settings-0110"&gt;Kirigami Settings (0.11.0)&lt;/h2&gt;
&lt;p&gt;We moved &lt;code&gt;Kirigami.CategorizedSettings&lt;/code&gt; and &lt;code&gt;Kirigami.SettingAction&lt;/code&gt; from
Kirigami to Kirigami Addons. We used this opportunity to do a visual refresh of
the component.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/kirigami-settings.png" data-size="1230x821"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/kirigami-settings.png" width="1230" height="821" loading="lazy"
alt="NeoChat settings using both the new Settings and FormHeader component"&gt;
&lt;/a&gt;
&lt;figcaption&gt;NeoChat settings using both the new Settings and FormHeader component&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Currently, the search feature only searches inside the title of the categories, but
in the future, we would like it to also search inside the content of the pages.&lt;/p&gt;
&lt;h2 id="floating-buttons-0110"&gt;Floating buttons (0.11.0)&lt;/h2&gt;
&lt;p&gt;In KF6, for Kirigami we are removing the automatic floating buttons. As a replacement,
we are adding two new components: &lt;code&gt;FloatingButton&lt;/code&gt; and &lt;code&gt;DoubleFloatingButton&lt;/code&gt;. We
extracted these components from &lt;a class="link" href="https://apps.kde.org/powerplant" target="_blank" rel="noopener"
&gt;Powerplant&lt;/a&gt;,
&lt;a class="link" href="https://apps.kde.org/audiotube" target="_blank" rel="noopener"
&gt;Audiotube&lt;/a&gt; and &lt;a class="link" href="https://apps.kde.org/marknote" target="_blank" rel="noopener"
&gt;Marknote&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is how this looks in Powerplant.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/powerplant.png" data-size="924x717"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/powerplant.png" width="924" height="717" loading="lazy"
alt="Floating button at the bottom right"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Floating button at the bottom right&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="mobileform-is-now-formcard-0110"&gt;MobileForm is now FormCard (0.11.0)&lt;/h2&gt;
&lt;p&gt;We decided to rename MobileForm to FormCard since this new layout is also used
on other form factors. As part of the renaming we used the opportunity to do
some small but welcome change to the api. A lot of boilerplate for the layout
is not needed anymore.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/src/qml/RoomSettings/Permissions.qml b/src/qml/RoomSettings/Permissions.qml
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index 07b8a942..674ee4a5 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/src/qml/RoomSettings/Permissions.qml
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/src/qml/RoomSettings/Permissions.qml
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+import org.kde.kirigamiaddons.formcard 1.0 as FormCard
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-Kirigami.ScrollablePage {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+FormCard.FormCardPage {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- id: root
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- title: i18nc(&amp;#34;@title:window&amp;#34;, &amp;#34;Notifications&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- topPadding: 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- leftPadding: 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- rightPadding: 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- ColumnLayout {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- spacing: 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- MobileForm.FormCard {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- contentItem: ColumnLayout {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- spacing: 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- MobileForm.FormCardHeader {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- Layout.fillWidth: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- title: i18n(&amp;#34;Room notifications setting&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- MobileForm.FormRadioDelegate {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- text: i18n(&amp;#34;Follow global setting&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- onToggled: { ... }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ FormCard.FormHeader {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ title: i18n(&amp;#34;Room notifications setting&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ FormCard.FormCard {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ FormCard.FormRadioDelegate {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ text: i18n(&amp;#34;Follow global setting&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ onToggled: { ... }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The qml import &lt;code&gt;org.kde.kirigamiaddons.labs.mobileform&lt;/code&gt; is still here and will
remain for the foreseeable future. Still, please update to the new import name to
get all the latest improvements.&lt;/p&gt;
&lt;h2 id="new-about-pages"&gt;New About Pages&lt;/h2&gt;
&lt;p&gt;The look of the about page and the about kde page was also updated to use the
new Avatar and FormCard components.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/about1.png" data-size="1230x821"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/about1.png" width="1230" height="821" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/about2.png" data-size="1230x821"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/about2.png" width="1230" height="821" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/about3.png" data-size="1230x821"&gt;
&lt;img src="https://carlschwan.eu/2023/08/16/kirigami-addons-0.11.0/about3.png" width="1230" height="821" loading="lazy"
alt="&amp;nbsp;"&gt;
&lt;/a&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="bug-fixes-and-minor-improvements"&gt;Bug fixes and minor improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;We fixed various issues with the translations not loading for some components
and added some ci checks to ensure that this doesn&amp;rsquo;t happen in the future.&lt;/li&gt;
&lt;li&gt;In AlbumMaximizeComponent, we are now using icon name compatible with more
xdg-icon-themes&lt;/li&gt;
&lt;li&gt;The AboutPage now displays more information which were previously not displayed
due to some broken checks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-0.11.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Reuse annotate automation</title><link>https://carlschwan.eu/2023/08/05/reuse-annotate-automation/</link><pubDate>Sat, 05 Aug 2023 17:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/08/05/reuse-annotate-automation/</guid><description>&lt;p&gt;I &lt;s&gt;tooted&lt;/s&gt; posted &lt;a class="link" href="https://floss.social/@carlschwan/110837388714548838" target="_blank" rel="noopener"
&gt;on Mastodon&lt;/a&gt;
about how it would be great if more open source project would adopt REUSE.&lt;/p&gt;
&lt;p&gt;Someone pointed out that it would be great to automate the addition of the metadata inside the files based on the git repository.&lt;/p&gt;
&lt;p&gt;So here is a small script that does exactly that. It goes over all your .cpp and .h file and will add the header based on the list of authors as well as the first commit on that particular file.&lt;/p&gt;
&lt;p&gt;You will want to adapt it to the license you are using for your project as well as change &lt;code&gt;**/*.cpp&lt;/code&gt; to &lt;code&gt;**/*.py&lt;/code&gt; if you are doing Python development.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SPDX-FileCopyrightText: 2023 Carl Schwan &amp;lt;carl@carlschwan.eu&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; file in **/*.cpp **/*.h&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;year&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;git log --format&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;format:%ci&amp;#34;&lt;/span&gt; --reverse &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -n1 &lt;span class="p"&gt;|&lt;/span&gt; cut -d &lt;span class="s2"&gt;&amp;#34;-&amp;#34;&lt;/span&gt; -f 1&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; git shortlog -n -e -s -- &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cut -f &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r author
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reuse annotate --copyright &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$author&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --year &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$year&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --license &lt;span class="s2"&gt;&amp;#34;GPL-2.0-or-later&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is not perfect and you want to run &lt;code&gt;git diff&lt;/code&gt; before commiting the result, to check if the change made are fine.&lt;/p&gt;</description></item><item><title>Debugging the keyboard navigation in your QML application</title><link>https://carlschwan.eu/2023/07/30/debugging-the-keyboard-navigation-in-your-qml-application/</link><pubDate>Sun, 30 Jul 2023 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/07/30/debugging-the-keyboard-navigation-in-your-qml-application/</guid><description>&lt;p&gt;A neat trick to debug the keyboard navigation in your QML application is to put
the following code snippet in your &lt;code&gt;main.qml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApplicationWindow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;title:&lt;/span&gt; &lt;span class="nx"&gt;activeFocusItem&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeFocusItem&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;activeFocusItem.Accessible.name :&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Afterward, then you open your application, you will see the following in your title
bar:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/30/debugging-the-keyboard-navigation-in-your-qml-application/intro.png" data-size="719x153"&gt;
&lt;img src="https://carlschwan.eu/2023/07/30/debugging-the-keyboard-navigation-in-your-qml-application/intro.png" width="719" height="153" loading="lazy"
alt="Title bar containing the following text: “PrivateActionToolButton Sort - Merkuro Calendar”"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Title bar containing the following text: “PrivateActionToolButton Sort - Merkuro Calendar”&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;PrivateActionToolButton_QMLTYPE_XX(0x00000000)&amp;rdquo; is the type and address of the
actively focused item and &amp;ldquo;Sort&amp;rdquo; is the accessible name of this item.&lt;/p&gt;
&lt;p&gt;If you tab around your application, you will be able to identify multiple issues quickly:&lt;/p&gt;
&lt;h2 id="missing-accessiblename"&gt;Missing Accessible.name&lt;/h2&gt;
&lt;p&gt;You might notice that some elements only display their type and address but
accessible name. It is an issue because this means the screen reader user
won&amp;rsquo;t have any clues about what the button does.&lt;/p&gt;
&lt;p&gt;If this is the case, ensure that your control has a &lt;code&gt;text&lt;/code&gt; property set even if
the control itself overrides the contentItem or is an &lt;a class="link" href="https://carlschwan.eu/2023/04/06/accessible-icon-only-buttons-in-qml/" target="_blank" rel="noopener"
&gt;icon only
button&lt;/a&gt;.
If you can&amp;rsquo;t use the &lt;code&gt;text&lt;/code&gt; property, try to set the &lt;code&gt;Accessible.Name&lt;/code&gt;
property instead.&lt;/p&gt;
&lt;h2 id="html-code-in-your-text"&gt;HTML code in your text&lt;/h2&gt;
&lt;p&gt;If you see some HTML code appear in your toolbar, it is an issue. Screen
readers will be confused by the random XML tags in your text, so make sure to
remove them from the &lt;code&gt;Accessible.Name&lt;/code&gt;. A simple fix which works in some simple
cases is to do the following to clean up your text:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;QQC2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Hello &amp;lt;b&amp;gt;World&amp;lt;/b&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;Accessible.name:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;\/?b&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="not-clear-accessiblename"&gt;Not clear Accessible.Name&lt;/h2&gt;
&lt;p&gt;By default, your &lt;code&gt;Accessible.name&lt;/code&gt; of your controls is bound to their &lt;code&gt;text&lt;/code&gt;,
which is a good default but you shouldn&amp;rsquo;t hesitate to add some mode details in your
&lt;code&gt;Accessible.Name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;One example, I had in Merkuro (formally known as Kalendar) was in the month view. There each day had as &lt;code&gt;text&lt;/code&gt; the month number. In this case, it&amp;rsquo;s better to expose the whole date to the accessibility API, because without, context a number is not that useful.&lt;/p&gt;
&lt;h2 id="focused-but-not-visible"&gt;Focused but not visible&lt;/h2&gt;
&lt;p&gt;Some items are focused but are not visible on the screen. This often happens
when using a PathView/ListView with only one item visible at the time, since
the previous and next item are still loaded. If this is the case, ensure that
&lt;code&gt;activeFocusOnTab&lt;/code&gt; is set to false, when the item is not visible.&lt;/p&gt;
&lt;h2 id="focused-but-no-visual-indicator"&gt;Focused but no visual indicator&lt;/h2&gt;
&lt;p&gt;Some items are focused but they don&amp;rsquo;t have a focus indicator. This is an issue
since the user won&amp;rsquo;t able to know which item is currently focused and then pressing
space they might trigger a random action.&lt;/p&gt;
&lt;p&gt;There is an helpful property in &lt;code&gt;QQC2.Control&lt;/code&gt; named &lt;code&gt;visualFocus&lt;/code&gt; which
should be true when the user uses keyboard navigation to focus on a specific item.
So make sure to add for example, a special border when
&lt;code&gt;QQC2.Control.visualFocus&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; to your custom controls.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// MyButton.qml
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;QQC2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background:&lt;/span&gt; &lt;span class="nx"&gt;Rectangle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;border&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;width:&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visualFocus&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color:&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highlightColor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The type information can gives you a valuable information to find which item is
currently selected and has the broken focus.&lt;/p&gt;
&lt;h2 id="focus-loop"&gt;Focus loop&lt;/h2&gt;
&lt;p&gt;In some cases, when pressing tab multiple times, you will end up in a focus loop
with no way to escape it. This might be caused by broken usage of the
&lt;code&gt;KeyNavigation.tab&lt;/code&gt; property, dynamic reparenting of elements and other creative
ways to break the tab navigation. In case, please fix it!&lt;/p&gt;
&lt;h2 id="unfocusable-elements"&gt;Unfocusable elements&lt;/h2&gt;
&lt;p&gt;Make sure all your interactable elements in your application are focusable by
default. You can enable or disable this behavior, with the &lt;code&gt;activeFocusOnTab&lt;/code&gt;
property of &lt;code&gt;Ìtem&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Mastodon and Matrix link in your AppStream file</title><link>https://carlschwan.eu/2023/07/25/mastodon-and-matrix-link-in-your-appstream-file/</link><pubDate>Tue, 25 Jul 2023 21:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/07/25/mastodon-and-matrix-link-in-your-appstream-file/</guid><description>&lt;p&gt;Quick reminder, that you can include a Mastodon or Matrix link to your AppStream file and that the link will then be displayed in your application page at &lt;a class="link" href="https://apps.kde.org" target="_blank" rel="noopener"
&gt;apps.kde.org&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;custom&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;KDE::matrix&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;#merkuro:kde.org&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;KDE::mastodon&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;https://kde.social/@merkuro&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/custom&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/25/mastodon-and-matrix-link-in-your-appstream-file/app-details.png" data-size="333x252"&gt;
&lt;img src="https://carlschwan.eu/2023/07/25/mastodon-and-matrix-link-in-your-appstream-file/app-details.png" width="333" height="252" loading="lazy"
alt="Details on apps.kde.org showing the link to the mastodon account and matrix channel"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Details on apps.kde.org showing the link to the mastodon account and matrix channel&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Akademy 2023</title><link>https://carlschwan.eu/2023/07/24/akademy-2023/</link><pubDate>Mon, 24 Jul 2023 18:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/07/24/akademy-2023/</guid><description>&lt;p&gt;Last week, I went to Akademy, the yearly &lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;KDE&lt;/a&gt; conference, in
Thessaloniki. This is now my third in-person Akademy and fifth Akademy in
total. As always, this is the occasion to meet old and new friends, learn
about what others are hacking on and enjoy good food.&lt;/p&gt;
&lt;h2 id="friday"&gt;Friday&lt;/h2&gt;
&lt;p&gt;I arrived Friday afternoon, taking my flight from Nuremberg with a few others.
Getting out of the airport, we could immediately feel the heat. After leaving
our stuff at the hotel, we went to join the rest of the KDE folks at the
welcome event in a frindly and cozy bar.&lt;/p&gt;
&lt;h2 id="saturday"&gt;Saturday&lt;/h2&gt;
&lt;p&gt;On Saturday, I talked about the current state of the Accessibility (a.k.a
KDE For All) goal. In short, we are making progress on that and fixing
bugs everywhere in the stack, but we desperately need more community
involvement as the task is big. Hopefully, the Selemiun AT-SPI bridge can be
helpful, as it makes it possible to write integration tests using the
accessibility API which require some accessibility support in your application
to work. (&lt;a class="link" href="https://conf.kde.org/event/5/contributions/146/attachments/89/115/accessibility.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/24/akademy-2023/talk.jpg" data-size="5470x3656"&gt;
&lt;img src="https://carlschwan.eu/2023/07/24/akademy-2023/talk.jpg" width="5470" height="3656" loading="lazy"
alt="Me in a pannel with Joseph and Nate"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Me in a pannel with Joseph and Nate&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I also presented the Fundraising WG report. We had two successful
fundraising campaigns this year: Kdenlive and the end of the year campaign.
(&lt;a class="link" href="https://conf.kde.org/event/5/contributions/144/attachments/100/126/Fundraising%20WG%20Report.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;My highlight for the day was the KDE Embedded talk from Andreas. The
talk explained the progress in getting the whole KDE software packaged as
Yocto layers, which already allows us to build images for RISC-V and ARM devices
with Plasma BigScreen. There is definitively a lot of potential there
to get KDE technologies in a wide varieties of devices. A recent example is
KWin being integrated inside Mercedes cars&amp;rsquo; infotainment system, resulting in
various upstream improvements.
(&lt;a class="link" href="https://conf.kde.org/event/5/contributions/131/attachments/70/86/talk_embedded_final.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="sunday"&gt;Sunday&lt;/h2&gt;
&lt;p&gt;On Sunday, I had a talk with Tobias Fella about Matrix and Mastodon and how we
can integrate these protocols inside our applications in various ways. First,
by providing clients for these protocols: NeoChat (Matrix) and Tokodon (Mastodon),
but also with less oblivious ways like using Mastodon as a &lt;a class="link" href="https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/" target="_blank" rel="noopener"
&gt;comment system on your blog&lt;/a&gt;
or by using Matrix to share your KDE Itinerary live data end-to-end encrypted
with your friend without requiring a centralized server.
(&lt;a class="link" href="https://conf.kde.org/event/5/contributions/134/attachments/91/107/Akademy2023SlideTemplate.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;My highlight for Sunday was the Selenium GUI Testing talk from Harald,
which is closely related to my Accessibility goal and Automatisation goal from Nate.
(&lt;a class="link" href="https://conf.kde.org/event/5/contributions/137/attachments/79/95/selenium.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The lightning talks were also excellent. Kai showed off his integration of
&lt;a class="link" href="https://github.com/kbroulik/qalphacloud" target="_blank" rel="noopener"
&gt;AlphaCloud inside Plasma&lt;/a&gt; to get his
solar pannel stats. (&lt;a class="link" href="https://conf.kde.org/event/5/contributions/150/attachments/95/112/Fun%20with%20charts.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/kbroulik/qalphacloud/raw/master/artwork/Screenshot_infocenter.png?raw=true" alt="KInfoCenter module showing both live and historic photovoltaic information" /&gt;&lt;/p&gt;
&lt;p&gt;Jean-Baptiste Kempf (from VLC) presented his cross-platform
remote control software stack in Rust, which promises very low latency.&lt;/p&gt;
&lt;p&gt;Fabian Kosmale from the Qt &lt;s&gt;Company&lt;/s&gt; Group presented the qmllint tool,
which should really helps improve the quality of our software and reduces the
risk to accidentally create regressions in our QML applications. I&amp;rsquo;m looking
forward to using it on my applications and to building custom plugins for it.
(&lt;a class="link" href="https://conf.kde.org/event/5/contributions/151/attachments/82/98/qmllintwhat.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Finally, Nate presented the new Plasma Welcome wizard, which is great!
(&lt;a class="link" href="https://conf.kde.org/event/5/contributions/148/attachments/88/104/Akademy%202023%20presentation%20Welcome%20Center.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="bofs"&gt;Bofs&lt;/h2&gt;
&lt;p&gt;The rest of the week, we had “Birds of a Feather” (or BoF) which were particularly
productive. I (co-)hosted multiple BoFs this year: Plasma Mobile, NeoChat, KDE PIM
and Accessibility, but I also enjoyed a few other BoFs: the fundraising BoF where Victoria gave us a
lot of advice on how to do fundraising for non-profit and the KDE e.V. board office
hour or the Selemiun BoFs from Harald and a few others.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/24/akademy-2023/bof.jpeg" data-size="1500x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/07/24/akademy-2023/bof.jpeg" width="1500" height="1000" loading="lazy"
alt="Myself hacking on my computer during a bof"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Myself hacking on my computer during a bof&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="day-trip"&gt;Day trip&lt;/h2&gt;
&lt;p&gt;We went to Mount Olympus, Dion, and a beach for our day trip. As a ancient Greece nerd,
I enjoyed it! Here are some photos from the trip:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/24/akademy-2023/park.jpg" data-size="1000x750"&gt;
&lt;img src="https://carlschwan.eu/2023/07/24/akademy-2023/park.jpg" width="1000" height="750" loading="lazy"
alt="Dion archelogical park"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Dion archelogical park&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/24/akademy-2023/museum.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/07/24/akademy-2023/museum.jpg" width="750" height="1000" loading="lazy"
alt="Dion Museum of archelogie"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Dion Museum of archelogie&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/24/akademy-2023/olympus.jpg" data-size="864x1152"&gt;
&lt;img src="https://carlschwan.eu/2023/07/24/akademy-2023/olympus.jpg" width="864" height="1152" loading="lazy"
alt="Mount Olympus"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Mount Olympus&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/24/akademy-2023/beer.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2023/07/24/akademy-2023/beer.jpg" width="750" height="1000" loading="lazy"
alt="Beach beer"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Beach beer&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="trainings"&gt;Trainings&lt;/h2&gt;
&lt;p&gt;We also had training at Akademy. I participated to the one from Nuno (thanks,
KDAB for offering it) about &lt;a class="link" href="https://www.qt.io/product/ui-design-tools" target="_blank" rel="noopener"
&gt;Qt Design Studio&lt;/a&gt;,
a very powerful QML visual editor and with a bridge to
Figma. Unfortunately, I couldn&amp;rsquo;t attempt this training fully because it
conflicted with a BoF I was hosting, but the bit I saw there was very interesting.
It would be great if we could integrate Kirigami components inside this tool.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/24/akademy-2023/nuno.jpg" data-size="1000x750"&gt;
&lt;img src="https://carlschwan.eu/2023/07/24/akademy-2023/nuno.jpg" width="1000" height="750" loading="lazy"
alt="Nuno presenting his training"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Nuno presenting his training&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Kirigami Addons 0.9.0</title><link>https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/</link><pubDate>Mon, 03 Jul 2023 06:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/</guid><description>&lt;p&gt;It&amp;rsquo;s time for a new Kirigami Addons release. This new release contains the work
of Joshua Goins, Laurent Montel, Thiago Sueto, Volker Krause, Shubham Arora,
James Graham, Rishi Kumar and myself. Thanks for contributing!&lt;/p&gt;
&lt;h2 id="new-features"&gt;New features&lt;/h2&gt;
&lt;h3 id="mobileformformgridcontainer"&gt;MobileForm.FormGridContainer&lt;/h3&gt;
&lt;p&gt;FormGridContainer makes it possible to make the information exposed with
MobileForm more compact by putting multiple small cards in the same row. The
number of columns by default is 3 on desktop unless the total number of cards
makes it more sensible only to use 2 columns (e.g when there are only 2 or 4
elements).&lt;/p&gt;
&lt;p&gt;On mobile the number of columns is reduced to only 2.&lt;/p&gt;
&lt;p&gt;This new component was initially made by Rishi Kumar for the info grid that Tokodon, with some further improvement from myself. Here is how this looks with
Tokodon.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/form-grid-container.png" data-size="1230x821"&gt;
&lt;img src="https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/form-grid-container.png" width="1230" height="821" loading="lazy"
alt="Info Grid"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Info Grid&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;And the current API:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigamiaddons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mobileform&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MobileForm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;MobileForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormGridContainer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;infoCards:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;MobileForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormGridContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InfoCard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;42&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;subtitle:&lt;/span&gt; &lt;span class="nx"&gt;i18nc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@info:Number of Posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;MobileForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormGridContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InfoCard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;42&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;MobileForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormGridContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InfoCard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Clickable card
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Details&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;action:&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;onClicked:&lt;/span&gt; &lt;span class="nx"&gt;pageStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Details.qml&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="mobileformaboutpage-now-can-contains-extra-content"&gt;MobileForm.AboutPage now can contains extra content&lt;/h3&gt;
&lt;p&gt;Applications can now extend the &lt;code&gt;AboutPage&lt;/code&gt; to add extra content to it. This
was driven by the need of Itinerary which need to expose the license
information about the open data it uses.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/about.png" data-size="613x840"&gt;
&lt;img src="https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/about.png" width="613" height="840" loading="lazy"
alt="About page of itinerary"&gt;
&lt;/a&gt;
&lt;figcaption&gt;About page of itinerary&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id="validor-support-in-mobileformformtextfielddelegate"&gt;Validor support in MobileForm.FormTextFieldDelegate&lt;/h3&gt;
&lt;p&gt;Application can now add a Validor to their textfield. This was driven by the
need of Keysmith rewrite to use MobileForm.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/keysmith.png" data-size="570x821"&gt;
&lt;img src="https://carlschwan.eu/2023/07/03/kirigami-addons-0.9.0/keysmith.png" width="570" height="821" loading="lazy"
alt="About new 2fa with Keysmith"&gt;
&lt;/a&gt;
&lt;figcaption&gt;About new 2fa with Keysmith&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="important-bugfixes"&gt;Important bugfixes&lt;/h2&gt;
&lt;p&gt;The BasicTreeItem component now uses correct spacing between items. This was caused by a regression when adding right to left support to the KirigamiAddons TreeView.&lt;/p&gt;
&lt;p&gt;The native Android date/time pickers now works correctly even if multiple instance of it are loaded at the same time.&lt;/p&gt;
&lt;p&gt;Shubham and James fixed various bugs with the maximized image and video component which is displayed in NeoChat and Tokodon when viewing media files.&lt;/p&gt;</description></item><item><title>My generic Open Source Project FAQ</title><link>https://carlschwan.eu/2023/06/19/my-generic-open-source-project-faq/</link><pubDate>Mon, 19 Jun 2023 08:30:00 +0000</pubDate><guid>https://carlschwan.eu/2023/06/19/my-generic-open-source-project-faq/</guid><description>&lt;p&gt;People are often asking the same questions again and again about some of my
projects, so it might be a good opportunity to write a small FAQ.&lt;/p&gt;
&lt;p&gt;If you get redirected here, don’t take it personally; I am getting asked these
questions very often, and I feel people often misunderstand how open-source
projects work.&lt;/p&gt;
&lt;h2 id="why-does-x-not-have-feature-y"&gt;Why does X not have feature Y?&lt;/h2&gt;
&lt;p&gt;The most likely reason is that it still needs to be implemented. It doesn’t
mean that I or other maintainers are against this feature. It is just that X is
a purely non-commercial project, and I and others are currently working on it
during our free time. Unfortunately, Free time is a very limited and precious
resource. Between our day jobs or university projects, sleeping, eating, and
other social activities, little time and energy is left.&lt;/p&gt;
&lt;p&gt;We definitively might implement the feature in the future, but the best way to
ensure this gets implemented promptly is to get involved and implement it
yourself. Feel free to join our development channel beforehand and confirm that
the feature is something we would like to have in the application.&lt;/p&gt;
&lt;p&gt;We are happy to guide you to make the onboarding experience as good as possible
and to help you familiarize yourself with the code base. Getting involved with
open-source projects is also an excellent opportunity to learn how to program.
Check out the &lt;a class="link" href="https://summerofcode.withgoogle.com/" target="_blank" rel="noopener"
&gt;Google Summer of Code&lt;/a&gt;,
&lt;a class="link" href="https://www.outreachy.org/" target="_blank" rel="noopener"
&gt;Outreachy&lt;/a&gt;, or
&lt;a class="link" href="https://season.kde.org/" target="_blank" rel="noopener"
&gt;Season of KDE&lt;/a&gt; for special mentoring programs, but
getting involved outside these programs is also possible. On a personal note, I
learned most of my programming skills by contributing to KDE, so I am very
thankful for that.&lt;/p&gt;
&lt;h2 id="when-will-you-implement-feature-y-is-y-on-your-roadmap"&gt;When will you implement feature Y? Is Y on your roadmap?&lt;/h2&gt;
&lt;p&gt;Similarly to the previous question, X is a non-commercial project implemented
during my and others’ free time. We can’t say precisely when we will be done
with a particular feature as it depends on many factors: how much energy do we
currently have to work on this project after our day job/university projects,
are there more pressing issues, are there a technical blocker which needs to be
solved first, it is fun to implement…&lt;/p&gt;
&lt;p&gt;Again the best way to speed this up is to get involved.&lt;/p&gt;
&lt;h2 id="what-is-your-roadmap"&gt;What is your roadmap?&lt;/h2&gt;
&lt;p&gt;We are a non-commercial project which is purely volunteer based. We are just a
bunch of developers and designers; we do not have managers, stakeholders, or
clients influencing our work directly by setting deadlines or by asking &lt;strong&gt;and
paying&lt;/strong&gt; for specific features. Part of the reason we enjoy working on this
project is that we have a lot of freedom in terms of what we want to work on
and are also very flexible, allowing us to switch to a different task whenever
we want.&lt;/p&gt;
&lt;p&gt;Again the best way to influence the direction of this project is to get
involved.&lt;/p&gt;</description></item><item><title>Announcing Arianna 1.1</title><link>https://carlschwan.eu/2023/06/10/announcing-arianna-1.1/</link><pubDate>Sat, 10 Jun 2023 11:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/06/10/announcing-arianna-1.1/</guid><description>&lt;p&gt;I&amp;rsquo;m happy to announce the 1.1 release of Arianna. Arianna is a small ePub
reader application I started with Niccolo some time ago. Like most of my open
source applications, it is built on top of Qt and Kirigami.&lt;/p&gt;
&lt;p&gt;Arianna is both an ePub viewer and a library management app. Internally,
Arianna uses Baloo to find your existing ePub files in your device and
categorize them.&lt;/p&gt;
&lt;h2 id="new-features"&gt;New features&lt;/h2&gt;
&lt;p&gt;Arianna can now display the table of content of a book. This supports complex
hierarchies of headings.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/06/10/announcing-arianna-1.1/table-of-content.png" data-size="1300x969"&gt;
&lt;img src="https://carlschwan.eu/2023/06/10/announcing-arianna-1.1/table-of-content.png" width="1300" height="969" loading="lazy"
alt="A table of content displayed as a right sidebar with a tree structure"&gt;
&lt;/a&gt;
&lt;figcaption&gt;A table of content displayed as a right sidebar with a tree structure&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Arianna now provides you with the metadata about your books.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/06/10/announcing-arianna-1.1/book-detail.png" data-size="1030x699"&gt;
&lt;img src="https://carlschwan.eu/2023/06/10/announcing-arianna-1.1/book-detail.png" width="1030" height="699" loading="lazy"
alt="Dialog showing the title, author, description, license information about a book"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Dialog showing the title, author, description, license information about a book&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Additionally, you can now disable the reading progress on the library page if
it distracts you.&lt;/p&gt;
&lt;h2 id="bug-fixes"&gt;Bug fixes&lt;/h2&gt;
&lt;p&gt;You can now read books without requiring an internet connection. We also fixed
various crashes happening when indexing your books.&lt;/p&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;If you are interested in helping, don&amp;rsquo;t hesitate to reach out in the Arianna
matrix channel (&lt;a class="link" href="https://matrix.to/#/#arianna:kde.org" target="_blank" rel="noopener"
&gt;#arianna:kde.org&lt;/a&gt;) and I
will be happy to guide you.&lt;/p&gt;
&lt;p&gt;I also regularly post about my progress on Arianna (and other KDE apps) on my
&lt;a class="link" href="https://floss.social/@carlschwan" target="_blank" rel="noopener"
&gt;Mastodon account&lt;/a&gt;, so don&amp;rsquo;t hesitate to
follow me there ;) We also now have an official Mastodon account for Arianna
&lt;a class="link" href="https://kde.social/@arianna" target="_blank" rel="noopener"
&gt;@arianna@kde.social&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And in case you missed it, as a member of KDE&amp;rsquo;s fundraising working group, I
need to remind you that KDE e.V., the non-profit behind the KDE community
accepts &lt;a class="link" href="https://kde.org/fundraisers/yearend2022/" target="_blank" rel="noopener"
&gt;donations&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/arianna/arianna-1.1.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Health of the KDE community (2023 Update)</title><link>https://carlschwan.eu/2023/04/28/health-of-the-kde-community-2023-update/</link><pubDate>Fri, 28 Apr 2023 18:05:35 +0000</pubDate><guid>https://carlschwan.eu/2023/04/28/health-of-the-kde-community-2023-update/</guid><description>&lt;p&gt;It&amp;rsquo;s already two years since I last looked at KDE git history. As I decribed in the &lt;a class="link" href="https://carlschwan.eu/2021/04/29/kde-contributions-health/" &gt;latest edition&lt;/a&gt;, this is inspired by the work of &lt;a class="link" href="https://hpjansson.org/blag/2020/12/16/on-the-graying-of-gnome/" target="_blank" rel="noopener"
&gt;Hans Petter Jansson for GNOME&lt;/a&gt;
and use the tool he made (&lt;a class="link" href="https://github.com/hpjansson/fornalder" target="_blank" rel="noopener"
&gt;fornalder&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fornalder&lt;/strong&gt; is a formidable tool. It is easy to use and the documentation in the readme is great. I don&amp;rsquo;t know if this is because it was programmed in Rust but
&lt;strong&gt;fornalder&lt;/strong&gt; was also blazingly fast and most of the time spent during this
analysis was spent on cloning the repos.&lt;/p&gt;
&lt;p&gt;These stats include all the extragear, plasma, frameworks and release service
repository as well as most of the KDE websites and a few KDE playground
projects I had on my hard drive. For example, it doesn&amp;rsquo;t includes most of the
unmaintained projects (e.g. kdepimlibs, koffice, plasma-mediacenter,
&amp;hellip;). Also important to note, is that this doesn&amp;rsquo;t include translations at
all, since they are stored in SVN and added in the git repository with a script
which remove authorship information.&lt;/p&gt;
&lt;p&gt;I also removed manually all the scripted commits and I merged the contributions
from &amp;ldquo;Laurent Montel&amp;rdquo; with &amp;ldquo;Montel Laurent&amp;rdquo; as well as the one from various
contributors whose name changed.&lt;/p&gt;
&lt;h2 id="active-contributors"&gt;Active Contributors&lt;/h2&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/28/health-of-the-kde-community-2023-update/graph-year-author.png" data-size="2560x1200"&gt;
&lt;img src="https://carlschwan.eu/2023/04/28/health-of-the-kde-community-2023-update/graph-year-author.png" width="2560" height="1200" loading="lazy"
alt="Number of contributors by years"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Number of contributors by years&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The explaination for the colors is described by Hans Petter Janssson in his
blog post by:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The stacked histogram above shows the number of contributors who touched the project on a yearly basis. Each contributor is assigned to a generational cohort based on the year of their first contribution. The cohorts tend to shrink over time as people leave.&lt;/p&gt;
&lt;p&gt;There’s a special “drive-by” cohort (in a fetching shade of off-white) for contributors who were only briefly involved, meaning all their activity fits in a three-month window. It’s a big group. In a typical year, it numbers 200-400 persons who were not seen before or since. Most of them contribute a single commit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In 2022, the number of active decreased slightly compared to 2021 and 2020, which might be due to the fact that the pandemic ended and people spend less time on their PC contributing to open source projects (which provided an huge boost in 2019/2020).&lt;/p&gt;
&lt;h2 id="commits-count"&gt;Commits Count&lt;/h2&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/28/health-of-the-kde-community-2023-update/graph-year-commits.png" data-size="2560x1200"&gt;
&lt;img src="https://carlschwan.eu/2023/04/28/health-of-the-kde-community-2023-update/graph-year-commits.png" width="2560" height="1200" loading="lazy"
alt="Number of commits by years"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Number of commits by years&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;In 2022, the number of commits decreased as well, which can be attributed to the smaller
number of overall contributors and a bit less activity from some long time contributors.&lt;/p&gt;
&lt;p&gt;But I would not worry too much about these numbers as we are still above the 2019
level (pre-gitlab and pandemic) and looking already at the numbers for the first
few months of 2023 it&amp;rsquo;s increasing again.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I would say that KDE is in relatively good health. This is particularly impressive
for a project with little corporate backing and with mostly volunteers.&lt;/p&gt;
&lt;p&gt;In 2023, we are also finalizing the transition to Qt6 and KF6 with a first release
of KF6 and Plasma 6 around the end of this year, begining of next year.&lt;/p&gt;
&lt;p&gt;There is no better time to &lt;a class="link" href="community.kde.org/Get_Involved/development" &gt;get involved&lt;/a&gt;
or to consider making a &lt;a class="link" href="http://kde.org/fundraisers/yearend2022" target="_blank" rel="noopener"
&gt;donation&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;You can play with raw data in the form of &lt;a class="link" href="db.sqlite" &gt;a sqlite dabase&lt;/a&gt; (&amp;gt;200Mb)&lt;/p&gt;</description></item><item><title>Arianna 1.0.1</title><link>https://carlschwan.eu/2023/04/22/arianna-1.0.1/</link><pubDate>Sat, 22 Apr 2023 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/04/22/arianna-1.0.1/</guid><description>&lt;p&gt;I&amp;rsquo;m happy to announce the first bugfix release of Arianna. This release contains a lot of improvements to the accessibility of Arianna. Thanks a lot to &lt;a class="link" href="https://linuxrocks.online/@bgtlover" target="_blank" rel="noopener"
&gt;@bgtlover@linuxrocks.online&lt;/a&gt; who reported many accessibility issues. Aside from fixing some bugs in Arianna, this also resulted in fixes in Kirigami, Kirigami Addons and Qt, which are on a seperate release schedule so not every bug fixes is already available. I&amp;rsquo;ll write a seperate blog post which will go into the technical details.&lt;/p&gt;
&lt;p&gt;Aside from the accessibility fixes, this release fixes a few crashes when parsing some books. Thanks to those who tried Arianna and reported bugs (with backtrace!).&lt;/p&gt;
&lt;p&gt;And finally, this release also improve the translations coverage, with for example Galician being one of the new supported language.&lt;/p&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;If you are interested in helping, don&amp;rsquo;t hesitate to reach out in the Arianna
matrix channel (&lt;a class="link" href="https://matrix.to/#/#arianna:kde.org" target="_blank" rel="noopener"
&gt;#arianna:kde.org&lt;/a&gt;) and I
will be happy to guide you.&lt;/p&gt;
&lt;p&gt;I also regularly post about my progress on Arianna (and other KDE apps) on my
&lt;a class="link" href="https://floss.social/@carlschwan" target="_blank" rel="noopener"
&gt;Mastodon account&lt;/a&gt;, so don&amp;rsquo;t hesitate to
follow me there ;)&lt;/p&gt;
&lt;p&gt;And in case, you missed it, as a member of KDE&amp;rsquo;s fundraising working group, I
need to remind you that KDE e.V., the non-profit behind the KDE community
accepts &lt;a class="link" href="https://kde.org/fundraisers/yearend2022/" target="_blank" rel="noopener"
&gt;donations&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/arianna/arianna-1.0.1.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kirigami Addons 0.8.0</title><link>https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/</link><pubDate>Thu, 13 Apr 2023 18:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/</guid><description>&lt;p&gt;My second release of the day: Kirigami Addons 0.8.0. This release contains a
few new components.&lt;/p&gt;
&lt;h2 id="abstractmaximizecomponent"&gt;AbstractMaximizeComponent&lt;/h2&gt;
&lt;p&gt;This is part of the &lt;code&gt;org.kde.kirigamiaddons.labs.components&lt;/code&gt; module and is a
popup that covers the entire window to show some items. This is already used in
NeoChat and Tokodon to magnify image and videos.&lt;/p&gt;
&lt;p&gt;Thanks James Graham for developing the initial version and upstreaming it to
Kirigami-addons.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/maximized.png" data-size="973x1115"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/maximized.png" width="973" height="1115" loading="lazy"
alt="Maximized image in NeoChat"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Maximized image in NeoChat&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="convergent-spinbox"&gt;Convergent SpinBox&lt;/h2&gt;
&lt;p&gt;Another new component is the convergent &lt;code&gt;SpinBox&lt;/code&gt; from the
&lt;code&gt;org.kde.kirigamiaddons.labs.mobileform&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;This is just a normal spinbox on desktop.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/spinbox-desktop.png" data-size="553x276"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/spinbox-desktop.png" width="553" height="276" loading="lazy"
alt="Spinbox on desktop with small touch targets"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Spinbox on desktop with small touch targets&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;But on Plasma Mobile/Android the touch target becomes bigger.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/spinbox-mobile.png" data-size="335x303"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/kirigami-addons-0.8.0/spinbox-mobile.png" width="335" height="303" loading="lazy"
alt="Spinbox on desktop with larger touch targets"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Spinbox on desktop with larger touch targets&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="others"&gt;Others&lt;/h2&gt;
&lt;p&gt;Aside from this two components this release contains some small bugfixes and
other minor API improvememts.&lt;/p&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;If you are interested in helping, don&amp;rsquo;t hesitate to reach out in the Plasma Mobile
matrix channel (&lt;a class="link" href="https://matrix.to/#/#plasmamobile:kde.org" target="_blank" rel="noopener"
&gt;#plasmamobile:kde.org&lt;/a&gt;) and I
will be happy to guide you.&lt;/p&gt;
&lt;p&gt;And in case, you missed it, as a member of KDE&amp;rsquo;s fundraising working group, I
need to remind you that KDE e.V., the non-profit behind the KDE community
accepts &lt;a class="link" href="https://kde.org/fundraisers/yearend2022/" target="_blank" rel="noopener"
&gt;donations&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/kirigami-addons/kirigami-addons-0.8.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Announcing Arianna 1.0</title><link>https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/</link><pubDate>Thu, 13 Apr 2023 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/</guid><description>&lt;p&gt;I&amp;rsquo;m happy to announce the initial release of Arianna. Arianna is a small ePub
reader application I started with Niccolo a few months ago. Like most of my
open source applications, it is built on top of Qt and Kirigami.&lt;/p&gt;
&lt;p&gt;Arianna is both an ePub viewer and a library management app. Internally,
Arianna uses Baloo to find your existing ePub files in your device and
categorize them.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/library-view.png" data-size="1300x969"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/library-view.png" width="1300" height="969" loading="lazy"
alt="Library view"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Library view&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The library view will keep track of your reading progress and find new books as
soon as you download them.&lt;/p&gt;
&lt;p&gt;If your book library is particularly big, you can either use the internal
search functionality, or look through the various categories and find books
grouped by genre, publisher or author.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/library-search.png" data-size="390x425"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/library-search.png" width="390" height="425" loading="lazy"
alt="Library search popup showing a few search results"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Library search popup showing a few search results&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/library-author.png" data-size="1300x969"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/library-author.png" width="1300" height="969" loading="lazy"
alt="Library grouped by authors"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Library grouped by authors&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The actual reader is quite basic as its only function is to show the content of
the book. That said, it does have features like a progress bar to keep track of
your reading progress and also lets you navigate within the book.&lt;/p&gt;
&lt;p&gt;It is also fully navigable with the keyboard.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/reader.png" data-size="1300x969"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/reader.png" width="1300" height="969" loading="lazy"
alt="Ebook reader showing the book content"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Ebook reader showing the book content&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Another feature allows you to search within a book for a specific word.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/reader-search.png" data-size="1300x969"&gt;
&lt;img src="https://carlschwan.eu/2023/04/13/announcing-arianna-1.0/reader-search.png" width="1300" height="969" loading="lazy"
alt="Ebook reader showing the book content"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Ebook reader showing the book content&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="get-it"&gt;Get it&lt;/h2&gt;
&lt;p&gt;Arianna will soon be available in Flathub (once the submittion is accepted).
Please also ask your distribution to package Arianna.&lt;/p&gt;
&lt;h2 id="credits"&gt;Credits&lt;/h2&gt;
&lt;p&gt;This application would have not been possible without the previous work carried
out by Foliate, from whom I copied and adapted the epub.js integration, and
Peruse from whom I copied and adapted the library management code. Finally I
would like to thank Šimon Rataj who made numerous contribution and fixed
multiple bugs.&lt;/p&gt;
&lt;p&gt;Arianna is also translated in multiple languages, thanks to some wonderful
translators. Here is the alphabethically sorted list: Basque, British English,
Catalan, Czech, Dutsch, Finish, French, German, Georgian, Hungarian,
Interlingua, Mandarin, Portuguese, Slovak, Spanish, Turkish, Ukrainian and
Valencian.&lt;/p&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;If you are interested in helping, don&amp;rsquo;t hesitate to reach out in the Arianna
matrix channel (&lt;a class="link" href="https://matrix.to/#/#arianna:kde.org" target="_blank" rel="noopener"
&gt;#arianna:kde.org&lt;/a&gt;) and I
will be happy to guide you.&lt;/p&gt;
&lt;p&gt;I also regularly post about my progress on Arianna (and other KDE apps) on my
&lt;a class="link" href="https://floss.social/@carlschwan" target="_blank" rel="noopener"
&gt;Mastodon account&lt;/a&gt;, so don&amp;rsquo;t hesitate to
follow me there ;)&lt;/p&gt;
&lt;p&gt;And in case, you missed it, as a member of KDE&amp;rsquo;s fundraising working group, I
need to remind you that KDE e.V., the non-profit behind the KDE community
accepts &lt;a class="link" href="https://kde.org/fundraisers/yearend2022/" target="_blank" rel="noopener"
&gt;donations&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on
&lt;a class="link" href="https://download.kde.org/stable/arianna/arianna-1.0.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Accessible icon-only buttons in QML</title><link>https://carlschwan.eu/2023/04/06/accessible-icon-only-buttons-in-qml/</link><pubDate>Thu, 06 Apr 2023 16:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/04/06/accessible-icon-only-buttons-in-qml/</guid><description>&lt;p&gt;Buttons are a fundamental element in user interfaces, but it&amp;rsquo;s easy to make some
accessibility mistakes when using icon-only buttons in QML.&lt;/p&gt;
&lt;p&gt;First, please avoid icon-only buttons and only use then, when the icon is very
well known (e.g. arrows, delete or favorite icons) and the space is limited.&lt;/p&gt;
&lt;p&gt;In case you still want to use an icon-only button. Make sure to set the &lt;code&gt;text:&lt;/code&gt;
property and that it is also translatable. Otherwise, a screen reader won&amp;rsquo;t know
that to say about the button. This is because the &lt;code&gt;text:&lt;/code&gt; property is used as
default value for the &lt;code&gt;Accessible.name:&lt;/code&gt; property, so when it is not set
Accessible.name is empty and the screen reader can only say that the currently
focused control is a button. The trick to have both the &lt;code&gt;text:&lt;/code&gt; property set and
an icon-only button is to use the &lt;code&gt;display:&lt;/code&gt; property and assign it to the
&lt;code&gt;AbstractButton.IconOnly&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This gives us the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt; &lt;span class="mf"&gt;2.15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt; &lt;span class="mf"&gt;2.15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Favorite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;icon.name:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;favorite&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display:&lt;/span&gt; &lt;span class="nx"&gt;AbstractButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IconOnly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, another essential part is that an icon-only button requires a
tooltip. We need the tooltip in case the user is unsure about the meaning of
the icon and we need more details.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt; &lt;span class="mf"&gt;2.15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt; &lt;span class="mf"&gt;2.15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text:&lt;/span&gt; &lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Favorite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;icon.name:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;favorite&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display:&lt;/span&gt; &lt;span class="nx"&gt;AbstractButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IconOnly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;ToolTip.text:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;ToolTip.delay:&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Units&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolTipDelay&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;ToolTip.visible:&lt;/span&gt; &lt;span class="nx"&gt;hovered&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this used the ToolTip attached property instead of a separate item
&lt;code&gt;ToolTip {}&lt;/code&gt; as it is more memory efficient. With the attached property, we
share the tooltip instance across the entire application instead of instanciating
a ToolTip popup for each button.&lt;/p&gt;</description></item><item><title>Digital Market Act workshop in Brussels</title><link>https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/</link><pubDate>Thu, 02 Mar 2023 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/</guid><description>&lt;p&gt;This Monday, I was in Brussels to attend a stakeholder workshop for the Digital Market Act (DMA) organized by the European Commission. For those who don&amp;rsquo;t know that is the DMA, it&amp;rsquo;s a new law that the European Parliament voted on recently and one of its goals of it is to force some interoperability between messaging services by allowing small players to able to communicate with users from the so-called Gatekeepers (e.g., WhatsApp).&lt;/p&gt;
&lt;p&gt;I attended this meeting as a representative of KDE and NeoChat. NeoChat is a client for the Matrix protocol (a decentralized and end-to-end encrypted chat protocol).
I started developing it with Tobias Fella a few years ago during the covid lockdown.&lt;/p&gt;
&lt;p&gt;I learned about this workshop thanks to NLNet, who funded previous work on NeoChat (end-to-end encryption). They put Tobias Fella and me in contact with Jean-Luc Dorel, the program officer for NGI0 for the European Commission. I would never have imagined sitting in a conference room in Brussels, thanks to my contribution to open-source projects,&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/visitor-pass.jpg" data-size="4000x3000"&gt;
&lt;img src="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/visitor-pass.jpg" width="4000" height="3000" loading="lazy"
alt="Visitor pass"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Visitor pass&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Regarding the workshop itself, this was quite enlighting for me, who is just a minor player there and only work on NeoChat and many other KDE application in my free time as a volunteer. I expected a room full of lawyers and lobbyists, which was partially true. A considerable part of the room was people who were silent during the entire workshop, representing big companies and mostly taking notes.&lt;/p&gt;
&lt;p&gt;Fortunately, a few good folks with more technical knowledge were also in the room. With, for example, people from Element/Matrix.org, XMPP, OpenMLS, Open Source Initiative (OSI), NlNet, European Digital Rights (EDRi), and consumer protection associations.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/room.jpg" data-size="4000x3000"&gt;
&lt;img src="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/room.jpg" width="4000" height="3000" loading="lazy"
alt="Photo of the room"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Photo of the room&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The workshop consisted of three panels. The first was more general, and the latter two more technical.&lt;/p&gt;
&lt;p&gt;In the first panel, the topic was the scope, the trade-offs, and the potential challenges of the Article 7 of the DMA. This panel was particularly well represented by a consumer protection organization, European Digital Rights, and a university professor, who were all in favor of the DMA and the interoperability part of it. Regarding the scope, one topic of discussion started by Simon Phipps was if gatekeepers like Meta should be forced to also interop with small self-hosted XMPP or Matrix instances or if this would only be about relatively big players. Unfortunately, I also learned that, while it was once part of the draft of the DMA, social networks are not required to interop. If Elon had bought Twitter earlier, this would have probably been part of the final text too. From this panel, I particularly appreciated the remarks of Jan Penfrat from the EDRi, who mentionned that this is not a technical or standardization problem and pointed out that some possible solutions like XMPP or Matrix already exist for a long time. There were also some questions left unanswered, like how to force gatekeepers to cooperate, as some people in the audience fear that they would make it needlessly difficult to interoperate.&lt;/p&gt;
&lt;p&gt;After this panel, we had a short lunch, and this was the occasion for me to connect a bit with the Matrix, XMPP and NlNet folks in the room.&lt;/p&gt;
&lt;p&gt;The second panel was more technical and was about end-to-end encryption. This panel had people from both sides of the debate. Paul Rösler, a cryptography researcher, tried to explain how end-to-end encryption works for the non-technical people in the audience, which I think was done quite well. Next, we had Eric Rescorla, the CTO of Mozilla, who also gave some additional insight into end-to-end encryption.&lt;/p&gt;
&lt;p&gt;Cisco was also there, and they presented their relative success integrating other platforms with Webex (e.g. Teams and Slack) with for example, the possibility of creating Webex calls from Slack. This &amp;lsquo;interoperability&amp;rsquo; between big players is definitively different from the direction of interoperability I want to see. But this is also a good example showing that when two big corporations want to integrate togethers, there are suddenly no technical difficulties anymore. They are also working on a new messaging standard (with reminds me a bit of &lt;a class="link" href="https://xkcd.com/927/" target="_blank" rel="noopener"
&gt;xkcd 927&lt;/a&gt;) as part of the MIMI working group of the IETF and they already deployed that in production.&lt;/p&gt;
&lt;p&gt;Afterward, it was the turn of Matrix, and Matthew Hodgson, the CEO/CTO at Element. Matthew showed a live demo of client-side bridging. This is their proposed solution to bridging end-to-end-encrypted messages across protocols without having to unencrypt the content inside a third-party server. This solution would be a temporary solution; ideally, services would converge to an open standard protocol like Matrix, XMPP, or something new. He pointed out that Apple was already doing that with iMessage and SMS. I found this particularly clever.&lt;/p&gt;
&lt;p&gt;Last, Meta sent a lawyer to represent them. The lawyer was reading a piece of paper in a very blank tone. He spent the entirety of his allocated time, telling the commission that interoperability represents a very clear risk for their users who trust Meta to keep their data safe and end-to-end encrypted. He ignored Matthew&amp;rsquo;s previous demo and told us that bridging would break their encryption. He also envisioned a clear opt-in policy to interoperability so that the users are aware that this will weaken their security and express a clear need for consent popups when interacting with users of other networks. It is quite ironic coming from Meta who in the context of the GDPR and data protection, was arguing against an opt-in policy and against consent. And as someone pointed out in the audience, while Whatsapp is end-to-end-encrypted, this isn&amp;rsquo;t the case for Messengers and Instagram conversations, which are both also products of Meta. The lawyer quickly dismissed that and explained that he only represented Whatsapp here and couldn&amp;rsquo;t answer this question for other Meta products. As you might have guessed, the audience wasn&amp;rsquo;t convinced by these arguments. Still, something to note is that Meta had at least the courage to have something in front of the audience, unless other big gatekeepers like Microsoft, Apple and Google who were also in the room but didn&amp;rsquo;t participate at all in the debate.&lt;/p&gt;
&lt;p&gt;Finally, after another small coffee break, the last panel was about abuse prevention, identity management, and discovery. With Meta in the panel again, consent was again a hot subject of discussion. Some argued that each time someone from another server joins a room, each user should consent to this new server can read their messages. This sounds very impractical to me, but I guess this goal is to make interoperability impractical. It also reminds me very much of the GDPR popup, which privacy-invading services try to optimize using dark patterns so that the users click on the &amp;ldquo;Allow&amp;rdquo; button, just that in this case, this will try to convince the user to click on the &amp;ldquo;Don&amp;rsquo;t connect with this user coming from this untrusted and scary third party server&amp;rdquo; button.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/slides.jpg" data-size="4000x3000"&gt;
&lt;img src="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/slides.jpg" width="4000" height="3000" loading="lazy"
alt="Slides about efficient design for effective interoperability"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Slides about efficient design for effective interoperability&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There was also some discussion about whether this was the server&amp;rsquo;s role in deciding if they allow connection from a third-party server or the user&amp;rsquo;s role. The former would mean that big providers would only allow access to their service for other big providers and block access to small self-hosted instances, and the latter would give users a choice. Another topic was the identifier, which, as someone from the audience pointed out, phone numbers used by Whatsapp, Signal and Telegram are currently not perfect as they are not unique across services and might require some standardization.&lt;/p&gt;
&lt;p&gt;In the end, the European Commission tried to summarize all the information they got today and sounded quite happy that so many technical folks were in the room and active in the conversation.&lt;/p&gt;
&lt;p&gt;And finally, after the last panels, I went to a bar next to the conference building with a few people from XMPP, EDRi, NlNet and OpenMLS to get beers and Belgium fries.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/outside.jpg" data-size="4608x3456"&gt;
&lt;img src="https://carlschwan.eu/2023/03/02/digital-market-act-workshop-in-brussels/outside.jpg" width="4608" height="3456" loading="lazy"
alt="Me outside of the building"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Me outside of the building&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Tokodon 23.02.0 release</title><link>https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/</link><pubDate>Fri, 17 Feb 2023 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/</guid><description>&lt;p&gt;I am happy to announce Tokodon 23.02. This release contains around one month&amp;rsquo;s worth of improvements, and while it is only one month, this release is feature packed! This was an exciting month for me in general, I started working at &lt;a class="link" href="https://www.kdab.com/" target="_blank" rel="noopener"
&gt;KDAB&lt;/a&gt;, went to FOSDEM in Brusels where I meet a bunch of other KDE folks, and spend time enjoying my new life in Berlin.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/fosdem.jpeg" data-size="1247x1041"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/fosdem.jpeg" width="1247" height="1041" loading="lazy"
alt="Me at the FOSDEM booth"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Me at the FOSDEM booth&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Fortunately I still found some time to contribute to Tokodon, and Joshua Goins has also been a very active contributor. The number of contributors is also growing with a bunch of new faces: Aakarsh MJ, Harshil Jani, Mohit Marathe, Raphael Gaschiganrd, Rishi Kumar who also contributed some code in this release.&lt;/p&gt;
&lt;p&gt;So what is Tokodon? Is is basically a client for the federated social network &lt;a class="link" href="https://joinmastodon.org/" target="_blank" rel="noopener"
&gt;Mastodon&lt;/a&gt;. It is built with &lt;a class="link" href="https://develop.kde.org/frameworks/kirigami/" target="_blank" rel="noopener"
&gt;Kirigami&lt;/a&gt; with the goal to have a great integration with Plasma Desktop and Plasma Mobile, but it also works on other desktop environments, and even Windows and macOS.&lt;/p&gt;
&lt;h2 id="get-it"&gt;Get it!&lt;/h2&gt;
&lt;p&gt;You can download Tokodon 23.02.0 from &lt;a class="link" href="https://beta.flathub.org/apps/details/org.kde.tokodon" target="_blank" rel="noopener"
&gt;Flathub&lt;/a&gt; and from F-Droid using the &lt;a class="link" href="https://community.kde.org/Android/FDroid" target="_blank" rel="noopener"
&gt;KDE repo&lt;/a&gt;. Currently we don&amp;rsquo;t offer binaries for Windows and macOS but if you want to help with that, please get in touch on &lt;a class="link" href="https://matrix.to/#/#tokodon:kde.org" target="_blank" rel="noopener"
&gt;Matrix&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="post-composer-rewamp"&gt;Post composer rewamp&lt;/h2&gt;
&lt;p&gt;As part of this release, we reworked the post composer considerably. This is the central area of your interaction with Mastodon and we wanted it to be more reliable.&lt;/p&gt;
&lt;p&gt;It is now possible to add an alternative description to images and videos sent via Tokodon. This is very important for accessibility as images without alternative description are invisible for screen readers. (Carl Schwan, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/98" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/alt-text.png" data-size="1120x879"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/alt-text.png" width="1120" height="879" loading="lazy"
alt="Alternative text"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Alternative text&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;It is also now possible to see the upload progress when uploading an attachment, giving you some visual feedback that something is happening. (Carl Schwan, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/98" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;An error message will be displayed when sending a message didn&amp;rsquo;t work. The post composer will only be closed once the message is sent, so if there is an network error the post you wrote is not lost and you can try posting it again. This should make Tokodon much more reliable for interacting with Mastodon. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/151" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/composer-error.png" data-size="635x451"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/composer-error.png" width="635" height="451" loading="lazy"
alt="Error displayed when an error occurred when sending a post"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Error displayed when an error occurred when sending a post&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Another big thing is that we are now supporting editing posts, so in case you made a typo you don&amp;rsquo;t need to delete your post and post it again. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/145" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;And finally Tokodon respects your default sensibility and visibility preferences when posting, and when replying to a message, the visibility of the message will be inherited in your reply. So if you are replying to a private message, the reply will automatically be set to be also private. (Carl Schwan, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/98" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="timeline-and-notification-view"&gt;Timeline and notification view&lt;/h2&gt;
&lt;p&gt;Aside from the improvements to the post editor, we continued to polish the timeline.&lt;/p&gt;
&lt;p&gt;The models for timelines has been completely refactored. Aside from making the code nicer to work with and a bit faster, it also made it possible to show the avatar of who boosted or liked a post. (Carl Schwan, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/134" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/boost-avatar.png" data-size="508x253"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/boost-avatar.png" width="508" height="253" loading="lazy"
alt="Avatar of the user who boosted a post is now displayed"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Avatar of the user who boosted a post is now displayed&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Tokodon now doesn&amp;rsquo;t only support image as attachments, but we now also have a prelimary support for videos and gifs. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/143" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;).&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;video src="video.mp4" muted="true" loop="true" autoplay&gt;&lt;/video&gt;
&lt;/div&gt;
&lt;p&gt;Additionally the previews for attachements should be better displayed with correct aspect ratio. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/131" target="_blank" rel="noopener"
&gt;Link 1&lt;/a&gt;, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/131" target="_blank" rel="noopener"
&gt;Link 2&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/atttachments.png" data-size="1120x879"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/atttachments.png" width="1120" height="879" loading="lazy"
alt="New attachment layout"&gt;
&lt;/a&gt;
&lt;figcaption&gt;New attachment layout&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Other ways to interact with a post are now available in an overflow menu for posts adding actions like a &amp;ldquo;Copy link to post&amp;rdquo; feature (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/105" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;), a bookmark feature (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/109" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;) and the previously mentioned edit feature (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/145" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/context-menu.png" data-size="441x334"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/context-menu.png" width="441" height="334" loading="lazy"
alt="Overflow menu showing additional actions"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Overflow menu showing additional actions&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There are now some new special pages available in the sidebar to display your bookmarks as well as your favorite posts. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/105" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/favoritetimeline.png" data-size="1120x879"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/favoritetimeline.png" width="1120" height="879" loading="lazy"
alt="Favorite timelines"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Favorite timelines&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We now handle notifications for poll updates (Riya Bisht, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/101" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;), and when a post you interacted with gets updated (Harshil Jani, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/97" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;There are now placeholders for when you currently don&amp;rsquo;t have any notifications or conversations instead of showing you an empty view (Mohit Marathe, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/148" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="search"&gt;Search&lt;/h2&gt;
&lt;p&gt;The search field introduced during the last released got a nice visual refresh&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/searchpopup.png" data-size="1120x879"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/searchpopup.png" width="1120" height="879" loading="lazy"
alt="Search popup"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Search popup&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip; And it now supports searching for hashtags. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/138" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="other-improvements"&gt;Other Improvements&lt;/h2&gt;
&lt;p&gt;It is now possible to configure the color scheme used by Tokodon independently from the Plasma color scheme. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/111" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt; but most of the code was copied in good tradition from NeoChat)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/colorscheme.png" data-size="1030x699"&gt;
&lt;img src="https://carlschwan.eu/2023/02/17/tokodon-23.02.0-release/colorscheme.png" width="1030" height="699" loading="lazy"
alt="Color Scheme"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Color Scheme&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We added the missing separators in the settings pages (Rishi Kumar, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/123" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;It is now possible to directly edit your account from your profile page (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/115" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The user&amp;rsquo;s avatar in the profile page now uses the standard &lt;code&gt;Kirigami.Avatar&lt;/code&gt; component (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/104" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The behavior of selectable text fields is now consistent (Aakarsh MJ, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/133" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="important-bugfixes"&gt;Important Bugfixes&lt;/h2&gt;
&lt;p&gt;A bug that broke loading the timeline in some conditions has been fixed (Carl Schwan, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/113" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;We fixed rendering of some attachments caused by a bug in the blurhash decoding. (Joshua Goins, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/108" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Storing the account settings is now using the account username and instance hostname as a unique identifier. This fixes situations where someone had multiple accounts with the same username on a different instance. (Raphael Gaschiganrd, &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/149" target="_blank" rel="noopener"
&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="technical-details"&gt;Technical Details&lt;/h2&gt;
&lt;p&gt;Aside from the many new features, there were several large re-factorings of the code base to cleanup some code that younger-me wrote. The C++ code base now has a coverage of 36%, this is a small increase compared to the previous release. Rishi Kumar is also working on integration tests as part of his Season of KDE project.&lt;/p&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;If you are interested in helping, don&amp;rsquo;t hesitate to reach out in the Tokodon matrix channel (#tokodon:kde.org) and I will be happy to guide you.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also regularly posting about my progress on Tokodon (and other KDE apps) on my &lt;a class="link" href="https://floss.social/@carlschwan" target="_blank" rel="noopener"
&gt;Mastodon account&lt;/a&gt;, so don&amp;rsquo;t hesitate to follow me there ;)&lt;/p&gt;
&lt;p&gt;And in case, you missed it, as a member of the fundraising working group, I need to remind you that KDE e.V., the non-profit behind the KDE community accepts &lt;a class="link" href="https://kde.org/fundraisers/yearend2022/" target="_blank" rel="noopener"
&gt;donations&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on &lt;a class="link" href="https://download.kde.org/stable/tokodon/tokodon-23.02.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt; and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Tokodon 23.01.0 release</title><link>https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/</link><pubDate>Mon, 02 Jan 2023 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/</guid><description>&lt;p&gt;Happy new year! To get a good start in this new new year, I&amp;rsquo;m happy to announce that Tokodon 23.01.0 is out!
This is a new major release for Tokodon and while it&amp;rsquo;s been only 2 weeks since the last major release, this
release is packed with new features and improvements.&lt;/p&gt;
&lt;p&gt;Tokodon is a &lt;a class="link" href="https://joinmastodon.org/" target="_blank" rel="noopener"
&gt;Mastodon&lt;/a&gt;/Pleroma/&lt;a class="link" href="https://github.com/nextcloud/social" target="_blank" rel="noopener"
&gt;Nextcloud Social&lt;/a&gt; client built with &lt;a class="link" href="https://develop.kde.org/frameworks/kirigami/" target="_blank" rel="noopener"
&gt;Kirigami&lt;/a&gt; that I started back in spring 2021. Tokodon has a great integration with KDE Plasma and Plasma Mobile, but it also work on other desktop environments and even Windows and macOS.&lt;/p&gt;
&lt;h2 id="get-it"&gt;Get it!&lt;/h2&gt;
&lt;p&gt;You can download Tokodon 23.01.0 from &lt;a class="link" href="https://beta.flathub.org/apps/details/org.kde.tokodon" target="_blank" rel="noopener"
&gt;Flathub&lt;/a&gt; and from the F-Droid using the &lt;a class="link" href="https://community.kde.org/Android/FDroid" target="_blank" rel="noopener"
&gt;KDE repo&lt;/a&gt;. Currently we don&amp;rsquo;t offer binaries for Windows and macOS but if you want to help with that, please get in touch.
build&lt;/p&gt;
&lt;h2 id="new-timeline-design"&gt;New timeline design&lt;/h2&gt;
&lt;p&gt;The first thing you will notice, when trying out this new version, is the new layout of the timelines.&lt;/p&gt;
&lt;p&gt;I updated it to follow more closely the design of Mastodon web-UI and I also spent some time adjusting the spacing and typographie to be more consistent.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/main.png" data-size="1340x1041"&gt;
&lt;img src="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/main.png" width="1340" height="1041" loading="lazy"
alt="Tokodon main view"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Tokodon main view&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="search"&gt;Search&lt;/h2&gt;
&lt;p&gt;It is now possible to search for posts, accounts and tags directly from within Tokodon.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/search.png" data-size="1340x1041"&gt;
&lt;img src="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/search.png" width="1340" height="1041" loading="lazy"
alt="Search field inside tokodon"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Search field inside tokodon&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="hashtag-handling"&gt;Hashtag Handling&lt;/h2&gt;
&lt;p&gt;Tokodon now handles hashtags better. Previously, when clicking on a hashtag, Tokodon would redirect you to the website where you could see the posts that included the selected hashtag. Now you don&amp;rsquo;t need to leave Tokodon anymore, and the posts are displayed inside the app like any other timeline.&lt;/p&gt;
&lt;h2 id="conversation-view"&gt;Conversation View&lt;/h2&gt;
&lt;p&gt;Tokodon now includes a basic conversation view. This shows the list of your last direct conversations. This makes it possible to see your conversation in a much nicer way than with the latest mentions view.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/conversation.png" data-size="1367x991"&gt;
&lt;img src="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/conversation.png" width="1367" height="991" loading="lazy"
alt="Conversation in Tokodon"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Conversation in Tokodon&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I plan to improve this feature further in the future, as I will be adding support for conversation markers and maybe offer a real chat-like view for conversations (which I will shamelessly steal from NeoChat).&lt;/p&gt;
&lt;h2 id="poll-support"&gt;Poll Support&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s now possible to view and interact with polls. This supports both multiple choice and single choice polls, but creating new polls is still not possible.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/poll.png" data-size="536x830"&gt;
&lt;img src="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/poll.png" width="536" height="830" loading="lazy"
alt="Poll in Tokodon"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Poll in Tokodon&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="account-editor"&gt;Account Editor&lt;/h2&gt;
&lt;p&gt;I also added an account editor. The merge request was actually 10 months old and I started it during a stream at the last FOSDEM. As you might imagine, it was not that fun to rebase. But it is finally possible to edit your account metadata and preferences directly from Tokodon.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/account-editor.png" data-size="612x977"&gt;
&lt;img src="https://carlschwan.eu/2023/01/02/tokodon-23.01.0-release/account-editor.png" width="612" height="977" loading="lazy"
alt="Account editor"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Account editor&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I also submitted a &lt;a class="link" href="https://github.com/mastodon/mastodon/pull/22790" target="_blank" rel="noopener"
&gt;pull request in Mastodon itself&lt;/a&gt; to expose more settings in the API.&lt;/p&gt;
&lt;h2 id="real-time-timeline-update"&gt;Real-Time Timeline Update&lt;/h2&gt;
&lt;p&gt;Tokodon was already handling some real-time events coming from the server, but until now this only included notifications. Now Tokodon will also handle new incoming messages and add them to the timeline. It will also delete messages as soon as the server asks for them to be deleted.&lt;/p&gt;
&lt;h2 id="technical-details"&gt;Technical Details&lt;/h2&gt;
&lt;p&gt;Aside from the many new features, there was several large re-factorings of the code base to cleanup some code that younger-me wrote. The C++ code base now has a coverage of 35%, and I am pretty happy with this increase from 12% of the previous release.&lt;/p&gt;
&lt;p&gt;Coming next, I want to focus more on the post editor. The back-end also needs to be cleaned up and several important features are still missing (choosing the language, posting polls and adding alternative textual descriptions to images). I already created a branch for that and you can follow the work on &lt;a class="link" href="https://invent.kde.org/network/tokodon/-/merge_requests/98" target="_blank" rel="noopener"
&gt;this merge request&lt;/a&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;And for those who prefer a full changelog, here it is:&lt;/summary&gt;
&lt;ul&gt;
&lt;li&gt;Update year&lt;/li&gt;
&lt;li&gt;Update to 23.01&lt;/li&gt;
&lt;li&gt;Cleanup notification model&lt;/li&gt;
&lt;li&gt;Cleanup loading handling&lt;/li&gt;
&lt;li&gt;Fix timezone handling&lt;/li&gt;
&lt;li&gt;Remove dead code&lt;/li&gt;
&lt;li&gt;Remove refreshing button now that refreshing happens automatically&lt;/li&gt;
&lt;li&gt;implemented update notifications&lt;/li&gt;
&lt;li&gt;Tweak appstream summary&lt;/li&gt;
&lt;li&gt;timelinemodel fix crash if json array is empty&lt;/li&gt;
&lt;li&gt;Fix unit tests&lt;/li&gt;
&lt;li&gt;Fix loading timelines&lt;/li&gt;
&lt;li&gt;Correctly handle streaming event about new and deleted posts&lt;/li&gt;
&lt;li&gt;Remove debug output&lt;/li&gt;
&lt;li&gt;Release new version 22.12.0&lt;/li&gt;
&lt;li&gt;Cleanup and fix account model&lt;/li&gt;
&lt;li&gt;Fix search on mobile&lt;/li&gt;
&lt;li&gt;Reduce number of warning in logs&lt;/li&gt;
&lt;li&gt;TootComposer.qml fix qmlscene issue &amp;quot;missing &amp;quot;:&amp;quot;&amp;quot;&lt;/li&gt;
&lt;li&gt;Run clang format&lt;/li&gt;
&lt;li&gt;Fix rendering of poll when no vote has been submitted&lt;/li&gt;
&lt;li&gt;Handle pressing back button with image preview open&lt;/li&gt;
&lt;li&gt;Remove dead code&lt;/li&gt;
&lt;li&gt;AuthorizationPage.qml authorize token URL should wrap&lt;/li&gt;
&lt;li&gt;Add more tests&lt;/li&gt;
&lt;li&gt;Cleanup backend code for profile edition&lt;/li&gt;
&lt;li&gt;Port to mobileform&lt;/li&gt;
&lt;li&gt;Account editor (second try)&lt;/li&gt;
&lt;li&gt;fix: No Preview for Hashtag links&lt;/li&gt;
&lt;li&gt;Dont&amp;#39;t show account switcher when no account is loaded&lt;/li&gt;
&lt;li&gt;Fix clazy issues&lt;/li&gt;
&lt;li&gt;Fix some compile warning&lt;/li&gt;
&lt;li&gt;More testing&lt;/li&gt;
&lt;li&gt;Add tests for threading&lt;/li&gt;
&lt;li&gt;Improve thread UI&lt;/li&gt;
&lt;li&gt;Fix opening thread&lt;/li&gt;
&lt;li&gt;Clang format&lt;/li&gt;
&lt;li&gt;Add more tests and fix canFetchMore behavior&lt;/li&gt;
&lt;li&gt;Fix marking post as pinned&lt;/li&gt;
&lt;li&gt;More refactoring&lt;/li&gt;
&lt;li&gt;Remove fetching and fully replace by loading&lt;/li&gt;
&lt;li&gt;Tag handling&lt;/li&gt;
&lt;li&gt;Ship our own icon&lt;/li&gt;
&lt;li&gt;Update icon name&lt;/li&gt;
&lt;li&gt;Fix checkable state&lt;/li&gt;
&lt;li&gt;Add unit test&lt;/li&gt;
&lt;li&gt;Use breeze icons&lt;/li&gt;
&lt;li&gt;Add conversation view&lt;/li&gt;
&lt;li&gt;Remove dead code&lt;/li&gt;
&lt;li&gt;Handle case where no spelled is installed&lt;/li&gt;
&lt;li&gt;Fix crash in notification view when interacting with post&lt;/li&gt;
&lt;li&gt;Propagate visibility in replies&lt;/li&gt;
&lt;li&gt;Don&amp;#39;t reset timeline when switching account&lt;/li&gt;
&lt;li&gt;Fix accountModel returned by follow activity&lt;/li&gt;
&lt;li&gt;Display blurhash when loading image&lt;/li&gt;
&lt;li&gt;Don&amp;#39;t show link preview on mobile&lt;/li&gt;
&lt;li&gt;Fix replies count&lt;/li&gt;
&lt;li&gt;Improve default debug output&lt;/li&gt;
&lt;li&gt;Optimize tooltip in PostDelegate&lt;/li&gt;
&lt;li&gt;Fix fetching the global timeline&lt;/li&gt;
&lt;li&gt;Fix replying to someone add yourself as mention&lt;/li&gt;
&lt;li&gt;Improve spacing of text field&lt;/li&gt;
&lt;li&gt;Improve overall spacing&lt;/li&gt;
&lt;li&gt;Add more unit tests&lt;/li&gt;
&lt;li&gt;Implement searching for accounts and posts&lt;/li&gt;
&lt;li&gt;Add a NetworkProxy page to settings&lt;/li&gt;
&lt;li&gt;Port AuthorizationPage to MobileForm&lt;/li&gt;
&lt;li&gt;Improve layout of already voted polls&lt;/li&gt;
&lt;li&gt;Add more tests&lt;/li&gt;
&lt;li&gt;Implement polls in the frontend&lt;/li&gt;
&lt;li&gt;Add poll implementation in backend&lt;/li&gt;
&lt;li&gt;Fix test&lt;/li&gt;
&lt;li&gt;Reduce spacing between action and author&lt;/li&gt;
&lt;li&gt;Update post layout&lt;/li&gt;
&lt;li&gt;Remove duplicate headers between cpp/h files&lt;/li&gt;
&lt;li&gt;Workaround kirigami regression&lt;/li&gt;
&lt;li&gt;Don&amp;#39;t show drawer handle when showing full screen image&lt;/li&gt;
&lt;li&gt;Fix crash when loading own account&lt;/li&gt;
&lt;li&gt;Fix appdata version&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;If you are interested in helping, don&amp;rsquo;t hesitate to reach out in the Tokodon matrix channel (#tokodon:kde.org) and I would be happy to guide you.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also regularly posting about my progress on Tokodon (and other KDE apps) on my &lt;a class="link" href="https://floss.social/@carlschwan" target="_blank" rel="noopener"
&gt;Mastodon account&lt;/a&gt;, so don&amp;rsquo;t hesitate to follow me ;)&lt;/p&gt;
&lt;p&gt;And in case, you missed it, as a member of the fundraising working group, I need to remind you that KDE is currently running an &lt;a class="link" href="https://kde.org/fundraisers/yearend2022/" target="_blank" rel="noopener"
&gt;end of the year campaign&lt;/a&gt;. Don&amp;rsquo;t hesitate to donate!&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on &lt;a class="link" href="https://download.kde.org/stable/tokodon/tokodon-23.01.0.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt; and it has been signed with my &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Tokodon 22.11.2 release</title><link>https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/</link><pubDate>Sun, 18 Dec 2022 19:00:00 +0000</pubDate><guid>https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/</guid><description>&lt;p&gt;I&amp;rsquo;m happy to announce the release of Tokodon 22.11.2 (and 22.11.1 who I released earlier this month and forgot to properly announce). These releases contain mostly bug fixes but also some welcome interface improvements.&lt;/p&gt;
&lt;p&gt;First this adds an account switcher (similar to the one Tobias Fella implemented in &lt;a class="link" href="https://apps.kde.org/neochat" target="_blank" rel="noopener"
&gt;NeoChat&lt;/a&gt;). Very usefully when you need to manage multiple accounts and want to quickly switch between them.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/tokodon.png" data-size="1340x1041"&gt;
&lt;img src="https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/tokodon.png" width="1340" height="1041" loading="lazy"
alt="Tokodon"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Tokodon&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;This also change the image preview from appearing in a separate full screen window to be contained inside the window. This follow the similar change from from James Graham in NeoChat.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/preview.png" data-size="1341x1017"&gt;
&lt;img src="https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/preview.png" width="1341" height="1017" loading="lazy"
alt="Preview full window mode"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Preview full window mode&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Joshua Goins improved the loading of media attachment and made it possible to now hide sensitive image by default using the blurhash effect. This is also using the already existing implementation of blurhash from Tobias in NeoChat and you might start to see a pattern in this release. ;)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/blurhash.png" data-size="831x570"&gt;
&lt;img src="https://carlschwan.eu/2022/12/18/tokodon-22.11.2-release/blurhash.png" width="831" height="570" loading="lazy"
alt="Blurhash post"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Blurhash post&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Finally I added support for custom emojis in many places inside the UI. Perfect if you want to show you true verified checkmark in your profile :)&lt;/p&gt;
&lt;p&gt;Aside from the nice new improvements, I improved the spacing in the app and while not perfect yet, I hope this makes Tokodon more enjoyable to use. Joshua Goins has also made various improvements to our internal networking code and this should offer better reliability and less prone to crash code. And I fixed an important crash on start-up that was affecting a lot of users&lt;/p&gt;
&lt;p&gt;Finally I started adding unit tests in Tokodon and added the infrastructure to mock a Mastodon server. We now have reached 12% unit tests coverage and I hope this number will grow after each release.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;And for those who prefer a full changelog, here it is:&lt;/summary&gt;
&lt;ul&gt;
&lt;li&gt;Remember selected account&lt;/li&gt;
&lt;li&gt;Update metadata&lt;/li&gt;
&lt;li&gt;Fix rebasing issue&lt;/li&gt;
&lt;li&gt;Attempt to fix Qt complaining about incomplete Post type&lt;/li&gt;
&lt;li&gt;Add parents to replies made by Account::get and similar requests&lt;/li&gt;
&lt;li&gt;More fixes&lt;/li&gt;
&lt;li&gt;Move away from shared_ptr for Post&lt;/li&gt;
&lt;li&gt;Fix double-free bug when viewing certain timeline pages&lt;/li&gt;
&lt;li&gt;Add qtkeychain to .kde-ci.yml&lt;/li&gt;
&lt;li&gt;Fix hide image icon missing on Android&lt;/li&gt;
&lt;li&gt;View your own profile via account switcher&lt;/li&gt;
&lt;li&gt;Add emoji support to page headings and profile bios&lt;/li&gt;
&lt;li&gt;Fix translation extraction&lt;/li&gt;
&lt;li&gt;Fix replying from the notification timeline&lt;/li&gt;
&lt;li&gt;Fix notification list&lt;/li&gt;
&lt;li&gt;Fix fetching the timeline twice&lt;/li&gt;
&lt;li&gt;Release 22.11.2&lt;/li&gt;
&lt;li&gt;Fix showing view-sensitive button too often&lt;/li&gt;
&lt;li&gt;Don&amp;#39;t have text autocomplete on login form&lt;/li&gt;
&lt;li&gt;Add missing release info&lt;/li&gt;
&lt;li&gt;Release 21.11.1&lt;/li&gt;
&lt;li&gt;Remove usage of anchors in layout&lt;/li&gt;
&lt;li&gt;Use blur hash for loading images and sensitive media&lt;/li&gt;
&lt;li&gt;Improve hover effect on the card&lt;/li&gt;
&lt;li&gt;Fix qt6 build&lt;/li&gt;
&lt;li&gt;Fix dependency in the ci&lt;/li&gt;
&lt;li&gt;Put accessible description at the bottom&lt;/li&gt;
&lt;li&gt;Improve the look of cards&lt;/li&gt;
&lt;li&gt;Use Kirigami.ActionToolBar&lt;/li&gt;
&lt;li&gt;Allow download images&lt;/li&gt;
&lt;li&gt;Full screen image like neochat&lt;/li&gt;
&lt;li&gt;Add m_original_post_id for use in timeline fetch&lt;/li&gt;
&lt;li&gt;Propertly reset pageStack when switching account&lt;/li&gt;
&lt;li&gt;Polish NotificationPage&lt;/li&gt;
&lt;li&gt;Improve layout of follow notification&lt;/li&gt;
&lt;li&gt;Fix crash when switching account in the notification view&lt;/li&gt;
&lt;li&gt;Fix translation catalog loading&lt;/li&gt;
&lt;li&gt;Post: Fix memory leak&lt;/li&gt;
&lt;li&gt;Fix off by one error in notification model&lt;/li&gt;
&lt;li&gt;Posibly fix crash (second try)&lt;/li&gt;
&lt;li&gt;Remove debug leftover&lt;/li&gt;
&lt;li&gt;Posibly fix crash at startup&lt;/li&gt;
&lt;li&gt;Improve account switcher&lt;/li&gt;
&lt;li&gt;Make tap and text selection work together in PostDelegate&lt;/li&gt;
&lt;li&gt;Fix wrong header url&lt;/li&gt;
&lt;li&gt;Fix handling of empty displayName&lt;/li&gt;
&lt;li&gt;Improve layout of PostDelegate&lt;/li&gt;
&lt;li&gt;Add InteractionButton component for likes, boosts and replies&lt;/li&gt;
&lt;li&gt;Add Qt 6 CI for FreeBSD and Android&lt;/li&gt;
&lt;li&gt;Fix custom emoji in account lists&lt;/li&gt;
&lt;li&gt;Port LoginPage to mobile form&lt;/li&gt;
&lt;li&gt;Add runtime dependency for org.kde.sonnet&lt;/li&gt;
&lt;li&gt;More cleanup and add autotests&lt;/li&gt;
&lt;li&gt;Properly use getter and use displayNameHtml in more places&lt;/li&gt;
&lt;li&gt;Implement custom emojis&lt;/li&gt;
&lt;li&gt;Fix coverage badge&lt;/li&gt;
&lt;li&gt;Add a refresh button for desktop devices&lt;/li&gt;
&lt;li&gt;Reset message handler properly to prevent threads overwriting each other&lt;/li&gt;
&lt;li&gt;Fix setInstanceUri early exit, preventing client id from being fetched&lt;/li&gt;
&lt;li&gt;Add coverage badge&lt;/li&gt;
&lt;li&gt;Fix reuse&lt;/li&gt;
&lt;li&gt;Add a qDebug filter to remove confidential data&lt;/li&gt;
&lt;li&gt;Add model tests&lt;/li&gt;
&lt;li&gt;Add basic test&lt;/li&gt;
&lt;li&gt;Split account in an abstract class without networking&lt;/li&gt;
&lt;li&gt;Remot stray qDebug leaking token&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;You can find the package on &lt;a class="link" href="https://download.kde.org/stable/tokodon/tokodon-22.11.2.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt; and it has been signed with my &lt;a class="link" href="carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kirigami Addons 0.5 release</title><link>https://carlschwan.eu/2022/10/23/kirigami-addons-0.5-release/</link><pubDate>Sun, 23 Oct 2022 11:40:35 +0000</pubDate><guid>https://carlschwan.eu/2022/10/23/kirigami-addons-0.5-release/</guid><description>&lt;p&gt;Just a small note that Kirigami Addons 0.5 was released yesterday.
This update only contains some small cleanups here and there (e.g.
moving some implementation details from the public api to the private one).&lt;/p&gt;
&lt;p&gt;The package is available on &lt;a class="link" href="http://download.kde.org/stable/kirigami-addons/kirigami-addons-0.5.tar.xz.mirrorlist" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;
and was signed with my new &lt;a class="link" href="https://carlschwan.eu/gpg-02325448204e452a/" &gt;GPG key&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Promo sprint in Saumur</title><link>https://carlschwan.eu/2022/09/19/promo-sprint-in-saumur/</link><pubDate>Mon, 19 Sep 2022 10:00:00 +0000</pubDate><guid>https://carlschwan.eu/2022/09/19/promo-sprint-in-saumur/</guid><description>&lt;p&gt;Last month, I was in Saumur (France) to attempt a KDE Promo sprint. This was my first sprint
since the pandemic, actually this might even be my first ever official KDE sprint as before
the pandemic I primarely attended conferences (Akademy, Fosdem, Libre Graphics Meeting, Linux
App Summit, &amp;hellip;) but no official sprint.&lt;/p&gt;
&lt;p&gt;The sprint took place during the weekend and was a great occassion to meet Allon, Aron and
Neophytos for the first time. Aside from them I also meet Claudio, Paul, Joseph and Aniqua
would I had already had the chance to meet before.&lt;/p&gt;
&lt;p&gt;While the sprint was only 2 days long, I think we had some really productive discussions
about the general strategie we should take and also how to move forward with some stuck
tasks.&lt;/p&gt;
&lt;p&gt;Personally I was quite happy to unblock one of my previous idea of creating
&amp;ldquo;KDE for&amp;rdquo;-webpages. I already created a &lt;a class="link" href="https://kde.org/for/kids" target="_blank" rel="noopener"
&gt;KDE for Kids&lt;/a&gt; page
a long time ago but never managed to find the time and motivation to create more of them.
So during the sprint, we started to brainstorm a bit for a &amp;ldquo;KDE for Creators&amp;rdquo; page, you
can already take a look at the wip prototype &lt;a class="link" href="api.carlschwan.eu/for/creators/" &gt;here&lt;/a&gt; and
if you have suggestions and want to help we have a &lt;a class="link" href="https://phabricator.kde.org/T15770" target="_blank" rel="noopener"
&gt;phab task&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Aside from all the productive discussions, Allon made us discover Saumur. It&amp;rsquo;s a really nice
city near the Loire. But I need to say that I was quite depressed at the level of water in the
Loire, it looked almost empty. Good reminder that climate change is real and human made.&lt;/p&gt;
&lt;p&gt;Aside from the sad state of the Loire, we also tasted a lot of good food. We had some
gallete bretones on Sunday evening and it was delicious. Allon also invited us
Saturday night at his place and he made fouée for us.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/09/19/promo-sprint-in-saumur/fouee.jpg" data-size="750x1000"&gt;
&lt;img src="https://carlschwan.eu/2022/09/19/promo-sprint-in-saumur/fouee.jpg" width="750" height="1000" loading="lazy"
alt="bread oven with fouée"&gt;
&lt;/a&gt;
&lt;figcaption&gt;bread oven with fouée&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a local speciallity and it was really good and I was so full at the end of the day.
Thank you Allon and your family for being such wonderful hosts!&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/09/19/promo-sprint-in-saumur/sprint.jpg" data-size="1183x854"&gt;
&lt;img src="https://carlschwan.eu/2022/09/19/promo-sprint-in-saumur/sprint.jpg" width="1183" height="854" loading="lazy"
alt="Sprint photo"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Sprint photo&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Daily driving the steam deck</title><link>https://carlschwan.eu/2022/09/17/daily-driving-the-steam-deck/</link><pubDate>Sat, 17 Sep 2022 10:00:00 +0000</pubDate><guid>https://carlschwan.eu/2022/09/17/daily-driving-the-steam-deck/</guid><description>&lt;p&gt;Tuesday night, I managed to break the screen of my laptop. This is particular annoying when you don&amp;rsquo;t have any external screen at home and need to work. Fortunately the scren wasn&amp;rsquo;t completely broken, and I managed to survice Wednesday, with half of the screen working.&lt;/p&gt;
&lt;p&gt;From half of the screen working on Wednesday, the situation got worse on Tursday and I was forced to find another solution. My only other Linux powered devices at home were a PinePhone and my SteamDeck. Performance wise, the choice was easy and I choose to try to use my SteamDeck. And so my workstation on Tursday and Friday ended up like this:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/09/17/daily-driving-the-steam-deck/steamdeck-workstation.jpg" data-size="4000x3000"&gt;
&lt;img src="https://carlschwan.eu/2022/09/17/daily-driving-the-steam-deck/steamdeck-workstation.jpg" width="4000" height="3000" loading="lazy"
alt="Steam deck workstation"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Steam deck workstation&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;I connected the dev environment of my laptop with SSH and it kinda worked. I did some commit. I even managed to do call and share my screen for a demo/presentation at work. But still the experience on the small screen wasn&amp;rsquo;t that great.&lt;/p&gt;
&lt;p&gt;Fortunately, I ordered a new laptop (Thinkpad E14 gen 4) and it arrived on Friday afternoon. The new laptop has a working screen but no mainline wifi drivers. So took me a bit more time than expected to build and sign the out-of-tree drivers with secure boot enabled.&lt;/p&gt;
&lt;p&gt;I love hardware.&lt;/p&gt;</description></item><item><title>KDE PIM in May and June</title><link>https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/</link><pubDate>Mon, 04 Jul 2022 12:00:35 +0000</pubDate><guid>https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/</guid><description>&lt;p&gt;KDE PIM is the set of applications that helps you manage your email, contacts,
appointments, tasks and more.&lt;/p&gt;
&lt;p&gt;In the months since the &lt;a class="link" href="https://volkerkrause.eu/2021/05/01/kde-pim-march-april-2021.html" target="_blank" rel="noopener"
&gt;KDE PIM March-April report&lt;/a&gt;
there have been two patch releases for Kontact, and over 1300 changes made by
more than 30 contributors have been integrated. Here are some of the highlights.&lt;/p&gt;
&lt;h2 id="general-improvements"&gt;General Improvements&lt;/h2&gt;
&lt;p&gt;Laurent continued working on the Qt6 support. The KDE PIM packages are now compiling
with Qt6.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/qt6.png" alt="KAdressBook with Qt6" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;KAdressBook with Qt6&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/akregator-qt6.png" alt="AKregator with Qt6" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;AKregator with Qt6&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/kleopatra-qt6.png" alt="Kleopatra with Qt6" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Kleopatra with Qt6&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/sieveeditor-qt6.png" alt="Sieve Editor with Qt6" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Sieve Editor with Qt6&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="kleopatra"&gt;Kleopatra&lt;/h2&gt;
&lt;p&gt;Ingo worked mainly on improving the usability and accessibility of the certificate manager
&lt;a class="link" href="https://apps.kde.org/kleopatra" target="_blank" rel="noopener"
&gt;Kleopatra&lt;/a&gt; over the last two months:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generating a new OpenPGP certificate is now possible on a Full HD display with 400
% magnification. (&lt;a class="link" href="https://dev.gnupg.org/T5969" target="_blank" rel="noopener"
&gt;T5969&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Generating a new OpenPGP certificate has been made more accessible by replacing
the QWizard-based dialog with a few separate dialogs. (&lt;a class="link" href="https://dev.gnupg.org/T5832" target="_blank" rel="noopener"
&gt;T5832&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/2022-0506-Certificate%20Creation%20-%20Step%201.png" alt="Certificate Creation - Step 1" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Certificate Creation - Step 1&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/2022-0506-Certificate%20Creation%20-%20Step%202.png" alt="Certificate Creation - Step 2" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Certificate Creation - Step 2&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/2022-0506-Certificate%20Creation%20-%20Step%203.png" alt="Certificate Creation - Step 3" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Certificate Creation - Step 3&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;ul&gt;
&lt;li&gt;The tool bar in the main window is now accessible with the keyboard. (&lt;a class="link" href="https://dev.gnupg.org/T6026" target="_blank" rel="noopener"
&gt;T6026&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Links embedded in text labels now behave like normal links you would find on the
web. This means that accessibility tools (such as screen readers) will read them as such,
instead of like selected text. (&lt;a class="link" href="https://dev.gnupg.org/T6034" target="_blank" rel="noopener"
&gt;T6034&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Labels that receive the keyboard focus are now marked with a focus frame. (&lt;a class="link" href="https://dev.gnupg.org/T6036" target="_blank" rel="noopener"
&gt;T6036&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The accessibility of the Certificate Details dialog has been improved. (&lt;a class="link" href="https://dev.gnupg.org/T5843" target="_blank" rel="noopener"
&gt;T5843&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Moreover, new features were added:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Certificate Details dialog now allows refreshing an individual certificate from
the configured keyserver and via Web Key Directory. (&lt;a class="link" href="https://dev.gnupg.org/T5903" target="_blank" rel="noopener"
&gt;T5903&lt;/a&gt;)&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/2022-0506-Certificate-Details.png" data-size="860x648"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/2022-0506-Certificate-Details.png" width="860" height="648" loading="lazy"
alt="Certificate Details dialog with an accessible focus indicator"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Certificate Details dialog with an accessible focus indicator&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Administrators can now specify the minimum and maximum validity of newly generated
keys. (&lt;a class="link" href="https://dev.gnupg.org/T5864" target="_blank" rel="noopener"
&gt;T5864&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And a few smaller things were added or fixed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A few operations that are not possible for keys stored on smart cards, e.g. creating a
backup of the secret key or changing the passphrase used to protect the secret key, are
now disabled if a smart card key is selected to avoid weird error messages.
(&lt;a class="link" href="https://dev.gnupg.org/T5956" target="_blank" rel="noopener"
&gt;T5956&lt;/a&gt;, &lt;a class="link" href="https://dev.gnupg.org/T5958" target="_blank" rel="noopener"
&gt;T5958&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Felix Tiede added a feature to publish a GPG key at a WKS-enabled mail
provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As this requires Kleopatra to operate with Akonadi to evaluate a mail-
transport for a GPG key user id and then send a MIME mail message, this
feature is only available on systems where Akonadi is installed prior to
building Kleopatra. On all other systems it is unavailable.&lt;/p&gt;
&lt;h2 id="kontact"&gt;Kontact&lt;/h2&gt;
&lt;p&gt;Glen fixed a few issues in the settings for the Summary View of Kontact:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Special Dates section obeys the check boxes for showing birthdays
and wedding anniversaries of contacts, and for showing anniversaries in
other calendars.&lt;/li&gt;
&lt;li&gt;The check box for hiding open-ended to-dos in the Pending To-dos
section is checked correctly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="korganizer"&gt;KOrganizer&lt;/h2&gt;
&lt;p&gt;Glen also worked on the task view of KOrganizer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Right-clicking on a task&amp;rsquo;s start date now displays a date editor
widget which you can use to change the start date. Right-clicking the
due date still edits the due date. If a change to either date would
cause the start date to be later than the due date, the other date is
adjusted. Additionally marking a task as complete in the Summary View
of Kontact, will no longer cause a crash.&lt;/li&gt;
&lt;li&gt;KOrganizer now uses the &amp;ldquo;stand-alone&amp;rdquo; form of month names in appropriate
places (&amp;ldquo;June&amp;rdquo;, as opposed to &amp;ldquo;June 1&amp;rdquo;). In some languages the two forms
are different.&lt;/li&gt;
&lt;li&gt;In the Incidence Editor, unchecking the &amp;ldquo;Due&amp;rdquo; check box of a recurring
to-do now accomplishes something.&lt;/li&gt;
&lt;li&gt;If a yearly-recurring item has exceptions, the Item Viewer lists the
exceptional years.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="kmail"&gt;KMail&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Laurent fixed a few memory leaks in KMail and he started working on integrating
&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc8098" target="_blank" rel="noopener"
&gt;Message Disposition Notification (MDN)&lt;/a&gt;
directly into the viewer instead of in a separate dialog. This will allow sending
read receipts more easily.&lt;/li&gt;
&lt;li&gt;He also fixed marking more than 1000 emails as read at the same time
(&lt;a class="link" href="https://bugs.kde.org/453969" target="_blank" rel="noopener"
&gt;BUG 453969&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Sandro Knauß worked on refactoring the key expiry checker in preparation for
showing it in a non-blocking dialog in the future.&lt;/li&gt;
&lt;li&gt;They also made it possible to
overwrite the encoding of the PGP inline messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="pim-data-exporter"&gt;Pim-data-exporter&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The data importer and exporter received a lot of bug fixes that will
improve the import of maildir resource and the collectio attribute.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="calendaring"&gt;Calendaring&lt;/h2&gt;
&lt;p&gt;There are now Akonadi and Android plugins for KCalendarCore&amp;rsquo;s platform
calendar access API. This isn&amp;rsquo;t aimed at calendaring applications like
Kalendar or KOrganizer, but at apps for which just basic calendaring access is
required. See the separate blog post for details:
&lt;a class="link" href="https://volkerkrause.eu/2022/06/18/kf5-android-platform-calendar-access.html" target="_blank" rel="noopener"
&gt;Android Platform Calendar Access&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="kde-itinerary"&gt;KDE Itinerary&lt;/h2&gt;
&lt;p&gt;Please see the dedicated summary blog post: &lt;a class="link" href="https://volkerkrause.eu/2022/06/04/kde-itinerary-april-may-2022.html" target="_blank" rel="noopener"
&gt;KDE Itinerary April May&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="kalarm"&gt;KAlarm&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;David Jarvie fixed a crash in the font chooser after deselecting a default font
(&lt;a class="link" href="https://bugs.kde.org/453193" target="_blank" rel="noopener"
&gt;BUG 453193&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The fade controls won&amp;rsquo;t be displayed if the current phonon backend
doesn&amp;rsquo;t support fade. And sound files are now correctly played when played
previously with fade.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="kalendar"&gt;Kalendar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Carl worked on an address book integration for Kalendar. This now includes a contact
viewer that more or less displays all the contact information, a basic contact
editor and also handling for contact groups.&lt;/li&gt;
&lt;li&gt;Also included is a Plasma applet and QR code for sharing contacts. More on this can be read
on my previous &lt;a class="link" href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/" target="_blank" rel="noopener"
&gt;blog post&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-view.png" alt="Contact Viewer" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Contact Viewer&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/search-applet.png" alt="Plasma Applet searching for a contact" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Plasma Applet searching for a contact&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/applet.png" alt="Plasma Applet" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Plasma Applet&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-editor.png" alt="Editor" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Editor&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;ul&gt;
&lt;li&gt;Claudio made it possible to show the parent and sub-tasks in the incidence drawer.
This allows you to navigate between related tasks in the normal calendar views,
and not only the task view.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/kalendar-tasks.png" data-size="1607x1180"&gt;
&lt;img src="https://carlschwan.eu/2022/07/04/kde-pim-in-may-and-june/kalendar-tasks.png" width="1607" height="1180" loading="lazy"
alt="Kalendar task"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Kalendar task&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claudio also put a lot of effort into reducing the technical debt in
Kalendar. He simplified the model for the month view, reorganized our QML files
into subfolders. This should help us when adding more features in the future.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="help-us-make-kontact-even-better"&gt;Help us make Kontact even better!&lt;/h2&gt;
&lt;p&gt;Check out some of our open junior jobs! They are simple, mostly programming-focused
tasks, but they don’t require any deep knowledge or understanding of Kontact, so
anyone can work on them. Feel free to pick any task from the list, then get in touch
with us! We’ll be happy to guide you and answer all your questions.
&lt;a class="link" href="https://www.dvratil.cz/2018/08/kde-pim-junior-jobs-are-opened/" target="_blank" rel="noopener"
&gt;Read more here&amp;hellip;&lt;/a&gt;&lt;/p&gt;
&lt;script src="https://carlschwan.eu/swiper-bundle.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
document.querySelectorAll('.swiper').forEach((swiperElement) =&gt; {
console.log('centerd', swiperElement.dataset.centered ?? true)
let swiper = new Swiper(swiperElement, {
centeredSlides: swiperElement.dataset.centered ?? true,
slidesPerView: 'auto',
spaceBetween: 30,
loop: swiperElement.dataset.centered ?? false,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
})
&lt;/script&gt;</description></item><item><title>Kalendar Contact Book - Current Progress</title><link>https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/</link><pubDate>Tue, 14 Jun 2022 07:00:35 +0000</pubDate><guid>https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/</guid><description>&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-view.png" alt="Featured image of post Kalendar Contact Book - Current Progress" /&gt;&lt;p&gt;During my long train trip to the &lt;a class="link" href="https://linuxappsummit.org/" target="_blank" rel="noopener"
&gt;Linux App Summit 2022&lt;/a&gt;, I started
working on a contact book feature in Kalendar. There was already a small contact integration in
the event editor to select attendees for an event and I wanted to extend it with a simple contact
info viewer and editor.&lt;/p&gt;
&lt;p&gt;When I started it, I was full of hope that this would be a simple task and would be easy to
finish. Unfortunately more than one month later, it&amp;rsquo;s not finished but there is a lot of progress
that I can already show off.&lt;/p&gt;
&lt;h2 id="the-contact-view"&gt;The Contact View&lt;/h2&gt;
&lt;p&gt;The contact view is the most immediate visual change that users will notice when starting Kalendar.
It&amp;rsquo;s a new view available in the sidebar and it will display all your contacts synchornized with
Kalendar. It&amp;rsquo;s also feature a search field, to easily find a contact very helpful when you have
many hundreds contats.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-view.png" data-size="1428x945"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-view.png" width="1428" height="945" loading="lazy"
alt="The contact view showing a few contacts"&gt;
&lt;/a&gt;
&lt;figcaption&gt;The contact view showing a few contacts&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Currently not all the properties that an vcard can contains are displayed, but it is easy to add
more of them later on.&lt;/p&gt;
&lt;p&gt;Internally, the contact view uses an
&lt;a class="link" href="https://api.kde.org/kdepim/akonadi/html/classAkonadi_1_1ItemMonitor.html" target="_blank" rel="noopener"
&gt;Akonadi::ItemMonitor&lt;/a&gt;
so that the changes to the contact are immediately reflected in the view, even if the changes
happened in KAddressBook or were synced in the background from an online service.&lt;/p&gt;
&lt;h2 id="contact-book-settings"&gt;Contact Book Settings&lt;/h2&gt;
&lt;p&gt;Kalendar has access to the same sources as KAddressBook with for example WebDav (e.g. Nextcloud),
Etesync, Microsoft Exchange Server and local vCard files.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The Google contact provider is still broken due to a massive API change in Google API. It&amp;rsquo;s a
good reminder that open standards are better for the users and the developers
&lt;a href="https://invent.kde.org/pim/libkgapi/-/merge_requests/22"&gt;sanity&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/settings.png" data-size="1449x925"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/settings.png" width="1449" height="925" loading="lazy"
alt="Contact book source settings"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Contact book source settings&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="qr-code-sharing"&gt;QR Code Sharing&lt;/h2&gt;
&lt;p&gt;From the contact view, it is also possible generate a QR code. This makes it easy to share one contact
to your phone. If you want to shares and synchronize multiples contacts, it&amp;rsquo;s better to use a
CardDav-based solution like &lt;a class="link" href="https://nextcloud.com" target="_blank" rel="noopener"
&gt;Nextcloud&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/qrcode.png" data-size="1428x898"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/qrcode.png" width="1428" height="898" loading="lazy"
alt="QR code sharing"&gt;
&lt;/a&gt;
&lt;figcaption&gt;QR code sharing&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="plasma-applet"&gt;Plasma Applet&lt;/h2&gt;
&lt;p&gt;After implementing the contact view, with Claudio we decided to try to keep the codebase for the
calendar and contact support mostly seperated from each others. To to so we created a QML plugin
that contains all the contact utility and that can simply be imported with &lt;code&gt;import org.kde.kalendar.contact 1.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This code seperation helped us develop a Plasma applet integrated inside the system try for the
contact book.&lt;/p&gt;
&lt;p&gt;The applet provides an easy way to search for a contact and send them an email or start a call
using KDE Connect.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/search-applet.png" data-size="635x525"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/search-applet.png" width="635" height="525" loading="lazy"
alt="Searching in the Plasma applet"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Searching in the Plasma applet&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/applet.png" data-size="635x525"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/applet.png" width="635" height="525" loading="lazy"
alt="An contact book Plasma applet"&gt;
&lt;/a&gt;
&lt;figcaption&gt;An contact book Plasma applet&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also possible to share with a QR code directly from the Plasma applet.&lt;/p&gt;
&lt;h2 id="contact-editor"&gt;Contact Editor&lt;/h2&gt;
&lt;p&gt;The contact editor turned out more complicated than &lt;a class="link" href="https://twitter.com/CarlKDE/status/1528482513085161475" target="_blank" rel="noopener"
&gt;planned&lt;/a&gt;
and is still missing a lot of features.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-editor.png" data-size="1030x699"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-editor.png" width="1030" height="699" loading="lazy"
alt="The contact editor"&gt;
&lt;/a&gt;
&lt;figcaption&gt;The contact editor&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Currently, it only allows to edit the name, the phone numbers and emails of a contact. When editing the
name you also have the choice to set each components of the name separately.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-editor-advanced.png" data-size="1030x699"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contact-editor-advanced.png" width="1030" height="699" loading="lazy"
alt="Advanced name options"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Advanced name options&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There is also handling for the case there the contact was edited in another Akonadi-powered
editor (like KAddressBook), asking the user what to do when detecting multiple concurrent
editing of the same contact.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/change-detection.png" data-size="1030x699"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/change-detection.png" width="1030" height="699" loading="lazy"
alt="Change detection"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Change detection&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="contact-group"&gt;Contact Group&lt;/h2&gt;
&lt;p&gt;Kalendar also has support for contact groups. This allows to create a group of contacts with
an associated email address. It&amp;rsquo;s quite helpful when you want to often send mails to a
group of contact.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contactgroup.png" data-size="1428x898"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contactgroup.png" width="1428" height="898" loading="lazy"
alt="Contact Group"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Contact Group&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;You can also edit them, add more contacts and concurrent editing detection is also built-in
the contact group editor.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contactgroupeditor.png" data-size="1030x699"&gt;
&lt;img src="https://carlschwan.eu/2022/06/14/kalendar-contact-book-current-progress/contactgroupeditor.png" width="1030" height="699" loading="lazy"
alt="Contact Group editing"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Contact Group editing&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="future"&gt;Future&lt;/h2&gt;
&lt;p&gt;There is still a lot of features missings left to implement. For example, contact deletion,
moving/copying a contact to another contact book but also a lot of contact properties need
to be implemented in the contact book.&lt;/p&gt;
&lt;p&gt;These features are relatively straigh forward to implement now that the base is here and if
you want to help with the implementation, join our &lt;a class="link" href="https://matrix.to/#/#kalendar:kde.org" target="_blank" rel="noopener"
&gt;Matrix room&lt;/a&gt;.
We would be happy to guide you.&lt;/p&gt;
&lt;p&gt;Hopefully this will all be ready before the 22.08 release.&lt;/p&gt;
&lt;p&gt;The Kalendar team is also working on another big component for Kalendar, stay tunned.&lt;/p&gt;</description></item><item><title>More KDE apps</title><link>https://carlschwan.eu/2021/12/18/more-kde-apps/</link><pubDate>Sat, 18 Dec 2021 10:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/12/18/more-kde-apps/</guid><description>&lt;img src="https://carlschwan.eu/2021/12/18/more-kde-apps/talk.png" alt="Featured image of post More KDE apps" /&gt;&lt;p&gt;KDE is all about the Apps is one of the current goals of the KDE community. Since this goal was choosen and announced in 2019 (remember it’s last year that was ‘normal’), I got a new hobby and it’s improving our apps infrastructure (e.g. apps.kde.org) and developing new KDE apps.&lt;/p&gt;
&lt;p&gt;I already talked previously about NeoChat (a Matrix client), Kontrast (a contrast checker), Koko (an image viewer) and Pikasso (a simple Rust-powered drawing app for the Plasma Mobile).&lt;/p&gt;
&lt;p&gt;Since then, I have developed a few new applications.&lt;/p&gt;
&lt;h2 id="tokodon"&gt;Tokodon&lt;/h2&gt;
&lt;p&gt;I’ve been using Mastodon since years and very much like the idea behind it and the fediverse in general. It’s a social network that is not controlled by a single big organization with dubious ethics but instead is controlled by many and everyone can decide to host their own instance.&lt;/p&gt;
&lt;p&gt;Tokodon is a simple client, I developed for this protocol. This app is still in progress and currently it allows to browser the different timelines (home, federated, global), to look at user profiles, send posts with images and multiple accounts support.&lt;/p&gt;
&lt;p&gt;The current most prominent missing features are the notification view, account settings and support for some types of post (e.g. videos, polls, …).&lt;/p&gt;
&lt;p&gt;If someone is interested to contribute, I will try to mentor a season of KDE project with the goal to implement a notification view to Tokodon (or any of the other big missing features).&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/12/18/more-kde-apps/tokodon-login.png" data-size="357x524"&gt;
&lt;img src="https://carlschwan.eu/2021/12/18/more-kde-apps/tokodon-login.png" width="357" height="524" loading="lazy"
alt="Tokodon"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Tokodon&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/12/18/more-kde-apps/tokodon-home.png" data-size="378x644"&gt;
&lt;img src="https://carlschwan.eu/2021/12/18/more-kde-apps/tokodon-home.png" width="378" height="644" loading="lazy"
alt="Tokodon mobile view"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Tokodon mobile view&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/12/18/more-kde-apps/tokodon-desktop.png" data-size="990x720"&gt;
&lt;img src="https://carlschwan.eu/2021/12/18/more-kde-apps/tokodon-desktop.png" width="990" height="720" loading="lazy"
alt="Tokodon desktop view"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Tokodon desktop view&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://invent.kde.org/network/tokodon" target="_blank" rel="noopener"
&gt;Link to the code&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="hash-o-matic"&gt;Hash-o-matic&lt;/h2&gt;
&lt;p&gt;Hash-o-matic is a straightforward GUI that allows the user to calculate checksums of files without using a terminal utility. It’s a simple application that I mainly developed in two evenings. It allows comparing two files, checking if a file matches a checksum and generating checksums for files.&lt;/p&gt;
&lt;p&gt;Aside from that, special care was made to integrate it neatly with KDE. A global menu is exposed to global menu users, in Dolphin you can use a right click action to open a file with Hash-o-matic, the app remembers it’s size and position like most KDE apps and finally, drag-and-dropping files from other apps also works!&lt;/p&gt;
&lt;p&gt;I would say that the app is now mostly done, and the only remaining feature that might be added is to allow customizing the hashing algorithms used. Currently, only md5, sha1 and sha512 are available.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/12/18/more-kde-apps/hashomatic.png" data-size="1386x796"&gt;
&lt;img src="https://carlschwan.eu/2021/12/18/more-kde-apps/hashomatic.png" width="1386" height="796" loading="lazy"
alt="Hash-o-matic"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Hash-o-matic&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://invent.kde.org/carlschwan/hash-o-matic" target="_blank" rel="noopener"
&gt;Link to the code&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="whale"&gt;Whale&lt;/h2&gt;
&lt;p&gt;Whale is actually a very simple file explorer using Kirigami, that I have been building for fun. It’s pretty rudimentary. Currently it allows browsing directories and I forked the plasma folder view to implement a right-click menu and file selections. It still needs a lot of work, but immediate plan is too at some point reuse the code I’m writing for Whale and reuse it for Koko (the Plasma Mobile image viewer) folder view.&lt;/p&gt;
&lt;p&gt;If someone wants to help or just look at the code, you can find it on my &lt;a class="link" href="https://invent.kde.org/carlschwan/whale/" target="_blank" rel="noopener"
&gt;invent profile&lt;/a&gt;.&lt;/p&gt;
&lt;figure style="max-width:600px;margin-left:auto;margin-right:auto" class="embed-responsive embed-responsive-16by9"&gt;&lt;video src="https://carlschwan.eu/2021/12/18/more-kde-apps/whale.mp4" loop="" autoplay="" muted=""&gt;&lt;/video&gt;&lt;figcaption style="margin-top:3rem"&gt;Whale, selection&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="kalendar"&gt;Kalendar&lt;/h2&gt;
&lt;p&gt;This summer, I mentored for GSoC for extending a prototype I built earlier this year of a Kirigami-based calendar app using Akonadi as backend. You can read more about it on Claudio Cambra’s blog, who is now maintaining the app.&lt;/p&gt;
&lt;p&gt;&lt;img src="kalendar.png" alt="Kalendar month view" /&gt;&lt;/p&gt;
&lt;h2 id="pelikan"&gt;Pelikan&lt;/h2&gt;
&lt;p&gt;In a similar fashion as Kalendar, I also started working on a small mail client using Kirigami and Akonadi. It currentl kinda works for viewing emails, but unfortunately many aspects of KDE PIM libraries related to emails depend too much on QtWidgets and are as such difficult to integrate in a QML application. I started separating the UI code from the buisness logic, but this is an hardeous task and I’m missing time and motivation.&lt;/p&gt;
&lt;p&gt;Here is the current state.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/12/18/more-kde-apps/pelikan.png" data-size="1120x879"&gt;
&lt;img src="https://carlschwan.eu/2021/12/18/more-kde-apps/pelikan.png" width="1120" height="879" loading="lazy"
alt="Pelikan"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Pelikan&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://invent.kde.org/carlschwan/quickmail/" target="_blank" rel="noopener"
&gt;Link to code&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="nextcloud-talk"&gt;Nextcloud talk&lt;/h2&gt;
&lt;p&gt;I have been using Nextcloud Talk a lot in recent months, so I started writing yet another chat client in Kirigami for it. It’s currently a soft-fork of the sailfishOS client written by a colleague.&lt;/p&gt;
&lt;p&gt;The current feature set is very basic and most of the QML frontend code has been based on the code we wrote with Tobias for NeoChat.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/12/18/more-kde-apps/talk.png" data-size="1360x879"&gt;
&lt;img src="https://carlschwan.eu/2021/12/18/more-kde-apps/talk.png" width="1360" height="879" loading="lazy"
alt="Nextcloud Talk"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Nextcloud Talk&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There is still a lot of work required to fix all the issues and get it to a state where it’s usable for daily usage.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/CarlSchwan/talk-desktop" target="_blank" rel="noopener"
&gt;Link to the code&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="supporting"&gt;Supporting&lt;/h2&gt;
&lt;p&gt;If you want to support the development of these applications, I strongly encourage you to donate to the KDE community. These donations help us keep our infrastructure running, including our GitLab instance, our websites, and more. You can donate at &lt;a class="link" href="https://kde.org/community/donations/" target="_blank" rel="noopener"
&gt;https://kde.org/community/donations/&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>KDE PIM in September and October</title><link>https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/</link><pubDate>Fri, 29 Oct 2021 10:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/</guid><description>&lt;img src="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/month-view-deco.png" alt="Featured image of post KDE PIM in September and October" /&gt;&lt;p&gt;In the months since the &lt;a class="link" href="https://www.davidcbryant.net/wp/2021/08/29/july-august-in-kde-pim/" target="_blank" rel="noopener"
&gt;KDE PIM July-August report&lt;/a&gt;
there have been two patch releases for Kontact, and over 1400 changes made by
more than 40 contributors have been integrated. Here are some of the highlights.&lt;/p&gt;
&lt;h2 id="korganizer"&gt;KOrganizer&lt;/h2&gt;
&lt;p&gt;A lot has happened over the last two months in KOrganizer development. First of
all, Friedrich has been working on reviving the calendar decoration feature.
This feature was introduced in 2007, but has since suffered from a lack of
maintenance and multiple regressions.&lt;/p&gt;
&lt;p&gt;Decorations allow you to display additional information inside the week view.
Here is what it looked like back in 2007 when it was introduced.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://commit-digest.kde.org/issues/2007-06-17/files/korganizer_themed_decorations.png" alt="KOrganizer back in 2007" /&gt;&lt;/p&gt;
&lt;p&gt;As part of this effort, the plugin that allowed KOrganizer to display the
current &amp;ldquo;Picture of the day&amp;rdquo; from the English Wikipedia, was ported to
Wikipedia&amp;rsquo;s new API, and now works again. (KDE Gear 21.12)&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/picture-of-day.png" data-size="987x207"&gt;
&lt;img src="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/picture-of-day.png" width="987" height="207" loading="lazy"
alt="Picture of the day"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of the day&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Friedrich is also working on a new plugin that shows the moon phase inside the
week view. Pretty neat if you like astronomy. This feature should hopefully also
land in KDE Gear 21.12.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/moon-phase.png" data-size="573x410"&gt;
&lt;img src="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/moon-phase.png" width="573" height="410" loading="lazy"
alt="Moon Phase"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Moon Phase&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The work on the Moon Phase plugin prompted Friedrich to add this feature to
KOrganizer&amp;rsquo;s month view too. This feature is still under review but should
hopefully land in KDE Gear 21.12 as well.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/month-view-deco.png" data-size="553x399"&gt;
&lt;img src="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/month-view-deco.png" width="553" height="399" loading="lazy"
alt="Month View Decoration"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Month View Decoration&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There are also ongoing discussions about making this plugin more generic, and,
in the future, adding support for these calendar decorations to &lt;a class="link" href="https://apps.kde.org/kalendar" target="_blank" rel="noopener"
&gt;Kalendar&lt;/a&gt;
and the &lt;a class="link" href="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/" target="_blank" rel="noopener"
&gt;Plasma Calendar applet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Glen Ditchfield has also been busy fixing many issues inside KOrganizer.&lt;/p&gt;
&lt;p&gt;The event editor no longer creates duplicate tags (&lt;a class="link" href="https://bugs.kde.org/441846" target="_blank" rel="noopener"
&gt;Bug 441846&lt;/a&gt;),
and the list used in the Event View and the &lt;em&gt;Find&lt;/em&gt; dialog do not automatically
re-sort themselves by starting date when the event editor alters an event
(&lt;a class="link" href="https://bugs.kde.org/441530" target="_blank" rel="noopener"
&gt;441530&lt;/a&gt;). The user&amp;rsquo;s sorting preferences for
these lists are preserved, just like other preferences.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Find&lt;/em&gt; dialog also got better labels and tooltips. When an event displayed
in it gets modified, this gets immediately updated in the UI.&lt;/p&gt;
&lt;p&gt;Glen also made several improvements to the &lt;em&gt;Month View&lt;/em&gt;. In general, the layout
is now more compact (&lt;a class="link" href="https://bugs.kde.org/435667" target="_blank" rel="noopener"
&gt;Bug 435667&lt;/a&gt;). Multiple-day
holidays are now correctly displayed as one multiple-day event instead of
several single-day events. A more visible change is that the current day is now
highlighted with your system theme&amp;rsquo;s highlight color instead of grey (&lt;a class="link" href="https://bugs.kde.org/420515" target="_blank" rel="noopener"
&gt;420515&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src="korganizer-month-view.png" alt="Month view" /&gt;&lt;/p&gt;
&lt;p&gt;Lastly, the sidebar item viewer displays the completion date and time of
completed to-dos, rather than just the date (&lt;a class="link" href="https://bugs.kde.org/374774" target="_blank" rel="noopener"
&gt;Bug 374774&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Volker Krause removed the procedure and email alarms that allowed events to
execute commands and send emails due to the security risk they pose in
combination with shared calendars.&lt;/p&gt;
&lt;p&gt;In term of documentation, David Bryant started cleaning up KOrganizer&amp;rsquo;s docs,
like he previously did with KMail.&lt;/p&gt;
&lt;h2 id="kholidays"&gt;KHolidays&lt;/h2&gt;
&lt;p&gt;Glen also updated the Canadian holidays inside KHolidays, and Ghalib Ahmed added
holiday information for Pakistan.&lt;/p&gt;
&lt;h2 id="kmail"&gt;KMail&lt;/h2&gt;
&lt;p&gt;Laurent Montel improved the emoji support in KMail. It is now easier to find
emojis thanks to a new emoji search feature, as well as a new recently-used
emojis tab.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/emoji.png" data-size="404x302"&gt;
&lt;img src="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/emoji.png" width="404" height="302" loading="lazy"
alt="Emoji search"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Emoji search&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Search is generally easier since multiple search fields now perform a
case-insensitive search.&lt;/p&gt;
&lt;p&gt;In the email editor, warnings about too many recipients are now indicated by an
inline message (instead of a popup dialog), conforming to KDE&amp;rsquo;s HIG.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/warning-email.png" data-size="1162x209"&gt;
&lt;img src="https://carlschwan.eu/2021/10/29/kde-pim-in-september-and-october/warning-email.png" width="1162" height="209" loading="lazy"
alt="Warning"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Warning&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Additionally, a few bugs related to the formatting in the email editor were
fixed by Laurent Montel (Bug &lt;a class="link" href="https://bugs.kde.org/419978" target="_blank" rel="noopener"
&gt;419978&lt;/a&gt;,
&lt;a class="link" href="https://bugs.kde.org/442416" target="_blank" rel="noopener"
&gt;442416&lt;/a&gt; and
&lt;a class="link" href="https://bugs.kde.org/443108" target="_blank" rel="noopener"
&gt;443108&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Volker Krause made sure that KMail connects to an SMTP servers with TLS enabled
even when no login is required (&lt;a class="link" href="https://bugs.kde.org/423423" target="_blank" rel="noopener"
&gt;bug 423423&lt;/a&gt;). He
also fixed an infinite SSL warning dialog loop that appeared when a connection
to an IMAP server was rejected with an invalid certificate (&lt;a class="link" href="https://bugs.kde.org/423424" target="_blank" rel="noopener"
&gt;bug 423424&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Sandro Knauß also helped solve the above security vulnerabilities and started
working on supporting the Autocrypt-Draft-State header. This header is needed
when you want to store a draft and need to indicate whether it should be sent as
an encrypted mail or not.&lt;/p&gt;
&lt;p&gt;Ingo added filtering by paths to folder selection. This is useful when you need
to copy or move a message to a folder, or when you need to jump to a folderas
you just type, say, &amp;ldquo;foo/bar&amp;rdquo; and you will narrow down the list of folders to
those containing the string &amp;ldquo;bar&amp;rdquo; that are subfolders of folders. This change
was funded by Intevation GmbH. (&lt;a class="link" href="https://bugs.kde.org/show_debug.cgi?id=443791" target="_blank" rel="noopener"
&gt;Bug 443791&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src="kmail-filter-by-path.png" alt="Filtering by paths" /&gt;&lt;/p&gt;
&lt;p&gt;Finally, David Bryant is working on updating the credits sections in both the
KMail documentation and the &lt;em&gt;About&lt;/em&gt; dialog, since most of the people who started
contributing to KMail after 2010 aren&amp;rsquo;t yet mentioned in the credits.&lt;/p&gt;
&lt;h2 id="kleopatra"&gt;Kleopatra&lt;/h2&gt;
&lt;p&gt;Kleopatra is KDE&amp;rsquo;s certificate manager and makes it possible to manage X.509 and
OpenPGP certificats and smartcards. In the last two months, Ingo Klöcker worked
on improving the accessibility of the decryption and encryption of files. He
also created an AppImage for Kleopatra.&lt;/p&gt;
&lt;h2 id="kalarm"&gt;KAlarm&lt;/h2&gt;
&lt;p&gt;David Jarvie fixed a few issues KAlarm was having when handling application
themes. The time edit spinbox now works correctly when using the Breeze theme
(&lt;a class="link" href="https://bugs.kde.org/443062" target="_blank" rel="noopener"
&gt;bug 443062&lt;/a&gt;) and, similarly, the sub-daily
recurrence editor should now be usable with the Fusion theme.&lt;/p&gt;
&lt;p&gt;He also worked on using the translated forms of AM/PM when displaying
times This will make it more appropriate for certain locales.&lt;/p&gt;
&lt;h2 id="kalendar"&gt;Kalendar&lt;/h2&gt;
&lt;p&gt;Kalendar is also making tons of progress. Clau aded a new week view and enabled
filtering by tags, Carl (me) worked on a QtQuick version of KCommandBar, Devin
Lin ported the mobile version to Kirigami.NavigationTabBar and Felipe Kinoshita
made the sidebar collapsible. This is just the tip of the iceberg, you can find
a more comprehensive list of all the changes in &lt;a class="link" href="https://claudiocambra.com/" target="_blank" rel="noopener"
&gt;Clau Cambra&amp;rsquo;s blog&lt;/a&gt;.&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;video src="https://claudiocambra.com/wp-content/uploads/2021/10/simplescreenrecorder-2021-10-24_14.50.20.mp4" alt="Week view in Kalendar" loop autoplay muted&gt;&lt;/video&gt;
&lt;/div&gt;
&lt;p&gt;Kalendar also passed the KDE Review process and you can expect a first release
soon!&lt;/p&gt;
&lt;h2 id="kde-itinerary"&gt;KDE Itinerary&lt;/h2&gt;
&lt;p&gt;KDE Itinerary, the ultimate companion app for all your trips, also received many
new features. Itinerary now provides navigation guides for walking and for
transfer sections of public transport elements, a new barcode scan mode, and
rental vehicle information.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.volkerkrause.eu/assets/posts/98/kde-itinerary-transfer-navigation-instructions.png" alt="KDE itinerary transfer navigation instructions" /&gt;&lt;/p&gt;
&lt;p&gt;You can find more information about all of Itinerary&amp;rsquo;s changes in Volker
Krause&amp;rsquo;s latest &lt;a class="link" href="https://volkerkrause.eu/2021/10/01/kde-itinerary-august-september-2021.html" target="_blank" rel="noopener"
&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="deprecation-work"&gt;Deprecation work&lt;/h2&gt;
&lt;p&gt;A large focus remains on preparing for the upcoming migration to Qt6 and KF6.
This mainly consists of porting away from deprecated functionality in Qt and KDE
Frameworks, as well as moving to more modern build systems or C++ constructs.
While this does not have any visible impact yet, this will allow for a smoother
transition down the line. This work was largely done by Laurent Montel,
Alexander Lohnau, Friedrich W. H. Kossebau, Nicolas Fella and a few others.&lt;/p&gt;
&lt;h2 id="help-us-make-kontact-even-better"&gt;Help us make Kontact even better!&lt;/h2&gt;
&lt;p&gt;Check out some of our open junior jobs! They are simple, mostly
programming-focused tasks, but they don’t require any deep knowledge or
understanding of Kontact, so anyone can work on them. Feel free to pick any task
from the list, then get in touch with us! We’ll be happy to guide you and answer
all your questions. &lt;a class="link" href="https://www.dvratil.cz/2018/08/kde-pim-junior-jobs-are-opened/" target="_blank" rel="noopener"
&gt;Read more here&amp;hellip;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks to Claudio Cambra and Paul Brown for the proofreading.&lt;/p&gt;</description></item><item><title>Spellchecking with QML</title><link>https://carlschwan.eu/2021/10/25/spellchecking-with-qml/</link><pubDate>Mon, 25 Oct 2021 10:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/10/25/spellchecking-with-qml/</guid><description>&lt;p&gt;QML is a nice technology but it sometimes feels that some parts of KDE Frameworks aren’t neatly integrated with it. For example, until recently &lt;a class="link" href="https://volkerkrause.eu/2021/09/18/kf5-notifications-in-qml.html" target="_blank" rel="noopener"
&gt;KNotification didn’t have QML bindings&lt;/a&gt;, which was the same for &lt;a class="link" href="https://volkerkrause.eu/2021/09/04/kf5-syntax-highlighting-in-qml.html" target="_blank" rel="noopener"
&gt;KSyntaxHighlighting&lt;/a&gt;. Thankfully Volker Krause took care of both.&lt;/p&gt;
&lt;p&gt;Another part of the often-used KDE Frameworks but had missing QML bindings was Sonnet. Sonnet is a very nice KDE framework powering KDE text areas with spell checking.&lt;/p&gt;
&lt;p&gt;The good news, Sonnet will, in the next KF5 release, supports QML apps too!&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/25/spellchecking-with-qml/in-action.png" data-size="1588x930"&gt;
&lt;img src="https://carlschwan.eu/2021/10/25/spellchecking-with-qml/in-action.png" width="1588" height="930" loading="lazy"
alt="Spell checking in NeoChat"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Spell checking in NeoChat&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There are two ways to add Sonnet supports in your application.&lt;/p&gt;
&lt;h2 id="the-easy-way-kirigami-and-qqc2-desktop-theme-integration"&gt;The easy way: Kirigami and qqc2-desktop-theme integration&lt;/h2&gt;
&lt;p&gt;If you use Kirigami and are fine with the default behavior, this only needs one single line in your &lt;code&gt;QtQuick.Controls.TextArea&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;import org.kde.kirigami 2.18 as Kirigami&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt; &lt;span class="mf"&gt;2.15&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;QQC2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;QQC2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TextArea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;Kirigami.SpellChecking.enabled:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This line is just a hint for the QtQuick Controls 2 theme to enable spell checking for this TextArea. Currently, only the qqc2-desktop-theme uses this hint. For other themes, like qqc2-breeze-style or custom themes, this will unfortunately do nothing.&lt;/p&gt;
&lt;p&gt;This hint is required since we decided to disable spell checking by default. This is because of multiple reasons: this might cause some breakage for apps that are already doing its own TextArea modification.&lt;/p&gt;
&lt;h2 id="the-hard-but-powerful-way"&gt;The hard but powerful way&lt;/h2&gt;
&lt;p&gt;The second way to add spellchecking with Sonnet is to use the bindings directly. This makes it possible to configure the exact behavior of the spellchecking in your app. This is the API that qqc2-desktop-theme is using internally.&lt;/p&gt;
&lt;p&gt;For that, you will then need to add the SpellcheckHighlighter directly to your &lt;code&gt;TextArea&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kde&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kirigami&lt;/span&gt; &lt;span class="mf"&gt;2.18&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Template&lt;/span&gt; &lt;span class="mf"&gt;2.15&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TextArea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: controlRoot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Sonnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SpellcheckHighlighter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;id: spellcheckhighlighter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;document:&lt;/span&gt; &lt;span class="nx"&gt;controlRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textDocument&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;cursorPosition:&lt;/span&gt; &lt;span class="nx"&gt;controlRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cursorPosition&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;selectionStart:&lt;/span&gt; &lt;span class="nx"&gt;controlRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionStart&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;selectionEnd:&lt;/span&gt; &lt;span class="nx"&gt;controlRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionEnd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;misspelledColor:&lt;/span&gt; &lt;span class="nx"&gt;Kirigami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;negativeTextColor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;onChangeCursorPosition:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;controlRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cursorPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;controlRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moveCursorSelection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextEdit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SelectCharacters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Sonnet.SpellcheckHighlighter&lt;/code&gt; exposes a few interesting methods that make it possible to get the spellchecker’s suggestions, add words to the dictionary, ignore some words and more. For those interested, I would recommend looking inside &lt;a class="link" href="https://invent.kde.org/frameworks/qqc2-desktop-style/-/blob/master/org.kde.desktop/private/TextFieldContextMenu.qml" target="_blank" rel="noopener"
&gt;qqc2-desktop-theme&lt;/a&gt; and see how Sonnet is used.
Settings&lt;/p&gt;
&lt;p&gt;Sonnet now also exports the spell-checking options to QML. You can find an example of how to use the exposed config object in &lt;a class="link" href="https://invent.kde.org/network/neochat/-/blob/work/sonnet-config/imports/NeoChat/Settings/SonnetConfigPage.qml" target="_blank" rel="noopener"
&gt;NeoChat&lt;/a&gt;. I hope to move this code at some point in Kirigami Addons, so that not every app will need to implement its own setting config page.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/25/spellchecking-with-qml/config.png" data-size="1030x699"&gt;
&lt;img src="https://carlschwan.eu/2021/10/25/spellchecking-with-qml/config.png" width="1030" height="699" loading="lazy"
alt="NeoChat Spellchecking options"&gt;
&lt;/a&gt;
&lt;figcaption&gt;NeoChat Spellchecking options&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;This change opens the way to also port the global spell-checking options in Plasma System Settings to QML in the future.
Next plans&lt;/p&gt;
&lt;p&gt;My next plan to make QML powerful in KDE is to figure out a way to upstream the nice KXMLGui/KConfigWidgets that I build for Kalendar into a separate component/library. This would make it possible to use a command bar, global menu bar, normal menu bar, configurable shortcuts and possibly more in a QML/Kirigami app.&lt;/p&gt;
&lt;p&gt;This one is a bit tricky since dependency wise. Since all these features depends on QAction that depends directly on QtWidgets in Qt5. In Qt6 this now only depends on QtGUI but is is sill annoying that QAction can’t be used in QML but instead we have a QQuickAction that is part of the private QtQuick Controls 2 API.&lt;/p&gt;</description></item><item><title>This week in NeoChat</title><link>https://carlschwan.eu/2021/10/22/this-week-in-neochat/</link><pubDate>Fri, 22 Oct 2021 10:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/10/22/this-week-in-neochat/</guid><description>&lt;p&gt;Last Saturday we had an improvised NeoChat mini development sprint in a small hotel room in Berlin in the occasion of the 25th anniversary of KDE. In a good KDE tradition, Carl spent this time on improving NeoChat settings. He ported both the NeoChat general settings and the specific room settings to the new &lt;code&gt;Kirigami.CategorizedSetting&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;Tobias fixed a lot of papercuts and now the power level should be fetched correctly, we show the number of joined users instead of joined+invited users in the room information pane, the user search is now case insensitive.&lt;/p&gt;
&lt;p&gt;Nicolas focused on fixing our Android build by making the spellchecking feature compile on Android.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/22/this-week-in-neochat/general.png" data-size="1327x811"&gt;
&lt;img src="https://carlschwan.eu/2021/10/22/this-week-in-neochat/general.png" width="1327" height="811" loading="lazy"
alt="General settings"&gt;
&lt;/a&gt;
&lt;figcaption&gt;General settings&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/22/this-week-in-neochat/roomSetting.png" data-size="1327x811"&gt;
&lt;img src="https://carlschwan.eu/2021/10/22/this-week-in-neochat/roomSetting.png" width="1327" height="811" loading="lazy"
alt="Roon settings"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Roon settings&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/22/this-week-in-neochat/sprint.jpg" data-size="1152x864"&gt;
&lt;img src="https://carlschwan.eu/2021/10/22/this-week-in-neochat/sprint.jpg" width="1152" height="864" loading="lazy"
alt="Picture of the hotel room where we did the improved spring"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Picture of the hotel room where we did the improved spring&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Aside from the mini-sprint, we also made a few more improvements during the week. Tobias fixed the flicking of the timeline on mobile and Carl made it possible for the user to resize the room information drawer.&lt;/p&gt;</description></item><item><title>KQuickImageEditor 0.2 released</title><link>https://carlschwan.eu/2021/10/02/kquickimageeditor-0.2-released/</link><pubDate>Sat, 02 Oct 2021 10:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/10/02/kquickimageeditor-0.2-released/</guid><description>&lt;p&gt;I’m happy to announce the 0.2 release of KQuickImageEditor. KQuickImageEditor is a QML library providing basic image editing functionality. It is currently used by &lt;a class="link" href="https://apps.kde.org/koko" target="_blank" rel="noopener"
&gt;Koko&lt;/a&gt;, &lt;a class="link" href="https://apps.kde.org/neochat" target="_blank" rel="noopener"
&gt;NeoChat&lt;/a&gt; and &lt;a class="link" href="https://apps.kde.org/pix" target="_blank" rel="noopener"
&gt;Maui Pix&lt;/a&gt;.&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;video class="embed-responsive-item" src="crop.webm" controls allowfullscreen&gt;&lt;/video&gt;
&lt;/div&gt;
&lt;p&gt;In this release, Noah Davis worked on improving the usability and design of the the existing croping feature. It now features more touch friendly handles that are consistently looking with the one from Spectacle.&lt;/p&gt;
&lt;p&gt;The old ReziseRectangle component is now deprecated and will be removed in the next version of KQuickImageEditor.&lt;/p&gt;
&lt;p&gt;KQuickImageEditor 0.2 also brings a new functionality also contributed by Noah: image resizing.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/10/02/kquickimageeditor-0.2-released/resize.png" data-size="1120x879"&gt;
&lt;img src="https://carlschwan.eu/2021/10/02/kquickimageeditor-0.2-released/resize.png" width="1120" height="879" loading="lazy"
alt="Image rezising component"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Image rezising component&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="packager-section"&gt;Packager section&lt;/h2&gt;
&lt;p&gt;A tarball of this release is available at &lt;a class="link" href="https://download.kde.org/stable/kquickimageeditor/kquickimageeditor-0.2.0.tar.xz" target="_blank" rel="noopener"
&gt;download.kde.org/stable/kquickimageeditor/kquickimageeditor-0.2.0.tar.xz&lt;/a&gt;. The package is signed with my gpg key &lt;a class="link" href="https://carlschwan.eu/gpg-key" target="_blank" rel="noopener"
&gt;14B0ED91B5783415D0AA1E0A06B35D38387B67BE&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>NeoChat 1.2</title><link>https://carlschwan.eu/2021/06/02/neochat-1.2/</link><pubDate>Wed, 02 Jun 2021 10:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/06/02/neochat-1.2/</guid><description>&lt;p&gt;The NeoChat is happy to announce the 3 major version of NeoChat.
This release is the product of more than 3 months worth of work.&lt;/p&gt;
&lt;h2 id="bubbles-everywhere-"&gt;Bubbles everywhere 🗨️&lt;/h2&gt;
&lt;p&gt;The first thing you will see then opening NeoChat 1.2 is that we
are now using message bubbles.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/bubbles.png" data-size="1349x834"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/bubbles.png" width="1349" height="834" loading="lazy"
alt="Bubbles 🗨️ everywhere 🗨️"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Bubbles 🗨️ everywhere 🗨️&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;We also replaced the not so great green line used as read marker
with a better looking separator.&lt;/p&gt;
&lt;h2 id="advanced-chatbar"&gt;Advanced Chatbar&lt;/h2&gt;
&lt;p&gt;The text input component was completely redesigned.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/editing.png" data-size="491x120"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/editing.png" width="491" height="120" loading="lazy"
alt="Editing a message"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Editing a message&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/replying.png" data-size="491x106"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/replying.png" width="491" height="106" loading="lazy"
alt="Replying to someone"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Replying to someone&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/attachment.png" data-size="495x232"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/attachment.png" width="495" height="232" loading="lazy"
alt="Adding an attachment"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Adding an attachment&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also now possible to get autocompletion of commands:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/commands.png" data-size="496x142"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/commands.png" width="496" height="142" loading="lazy"
alt="Use commands the easy way!"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Use commands the easy way!&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Speaking of commands, it&amp;rsquo;s now possible to send customized reactions
when replying to a message with &lt;code&gt;/react I love NeoChat&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/custom_reaction.png" data-size="274x98"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/custom_reaction.png" width="274" height="98" loading="lazy"
alt="Custom reactions"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Custom reactions&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There was also improvements in the keyboard shortcuts. It&amp;rsquo;s now possible
to use &lt;kbd&gt;⬆️&lt;/kbd&gt; to jump in edit mode for the last message.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/quickedit.gif" data-size="822x806"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/quickedit.gif" width="822" height="806" loading="lazy"
alt="Quick Edit"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Quick Edit&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Another way to edit the last message is to use the &lt;code&gt;s/text/replacement&lt;/code&gt;
syntax inspired by &lt;code&gt;sed&lt;/code&gt;. It&amp;rsquo;s not enabled by default but you can find
it in the settings.&lt;/p&gt;
&lt;h2 id="matrix-uri-support"&gt;Matrix URI support&lt;/h2&gt;
&lt;p&gt;NeoChat now support matrix uris and when opening &lt;code&gt;matrix:&lt;/code&gt; link
in your browser you will then be proposed to open it with NeoChat.
As a side effect, supporting Matrix URIs helped us unify how we handle
opening and joining rooms accross our codebase.&lt;/p&gt;
&lt;h2 id="inline-reply"&gt;Inline reply&lt;/h2&gt;
&lt;p&gt;We added inline replies support in our notification. So it&amp;rsquo;s now possible
to reply to messages directly from the notification.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/inlinereply.png" data-size="393x173"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/inlinereply.png" width="393" height="173" loading="lazy"
alt="Inline reply"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Inline reply&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="fancy-effects"&gt;Fancy effects&lt;/h2&gt;
&lt;p&gt;Miss the fancy fireworks or snow effects from Element? We too. So Alexey Andreyev
added them in NeoChat.&lt;/p&gt;
&lt;section class="swiper d-flex mb-5" aria-label="" role="list"&gt;
&lt;div class="swiper-wrapper d-flex my-3" role="listitem"&gt;
&lt;div class="swiper-slide swiper-slide-active"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/firework.png" alt="Fireworks" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Fireworks&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/snow.png" alt="Snow" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Snow&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div class="swiper-slide"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/tada.png" alt="Confetti" lazy class="rounded swipe-img"&gt;
&lt;figcaption&gt;Confetti&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="swiper-pagination" style="bottom: 0"&gt;&lt;/div&gt;
&lt;div class="swiper-button-prev"&gt;&lt;/div&gt;
&lt;div class="swiper-button-next"&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;h2 id="mobile-improvements"&gt;Mobile Improvements&lt;/h2&gt;
&lt;p&gt;On mobile the context menu are implemented as bottom drawer.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/menu-mobile.png" data-size="359x675"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/menu-mobile.png" width="359" height="675" loading="lazy"
alt="User Information Dialog on Mobile"&gt;
&lt;/a&gt;
&lt;figcaption&gt;User Information Dialog on Mobile&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="account-switcher"&gt;Account switcher&lt;/h2&gt;
&lt;p&gt;NeoChat supports multiple accounts since day one but it was not really practical
to switch between accounts. We now implemented an account switcher, available
at the bottom of the room list.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/06/02/neochat-1.2/accountswitcher.png" data-size="308x363"&gt;
&lt;img src="https://carlschwan.eu/2021/06/02/neochat-1.2/accountswitcher.png" width="308" height="363" loading="lazy"
alt="Account Switcher"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Account Switcher&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id="other"&gt;Other&lt;/h2&gt;
&lt;p&gt;Aside from the many new big features this release brings, NeoChat 1.2 contains
a lot of bugfixes and small usability improvements. We closed aproximately
100 issues (bugs and feature request) and this doesn&amp;rsquo;t count the huge number
of bugs fixed without an corresponding issues. Some hightlight are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scrolling on mobile now is smoother. It still not perfect but we are
working on making it better.&lt;/li&gt;
&lt;li&gt;Joining a room now makes it appears in the room list without having to
restart NeoChat and the room will be automatically opened&lt;/li&gt;
&lt;li&gt;Show the location of hyperlinked text on hover.&lt;/li&gt;
&lt;li&gt;Add an indicator for lack of internet connectivity&lt;/li&gt;
&lt;li&gt;NeoChat doesn&amp;rsquo;t hang anymore when loading a room&lt;/li&gt;
&lt;li&gt;Disable the chatbox if we&amp;rsquo;re not allowed to send messages&lt;/li&gt;
&lt;/ul&gt;
&lt;script src="https://carlschwan.eu/swiper-bundle.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
document.querySelectorAll('.swiper').forEach((swiperElement) =&gt; {
console.log('centerd', swiperElement.dataset.centered ?? true)
let swiper = new Swiper(swiperElement, {
centeredSlides: swiperElement.dataset.centered ?? true,
slidesPerView: 'auto',
spaceBetween: 30,
loop: swiperElement.dataset.centered ?? false,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
})
&lt;/script&gt;</description></item><item><title>Tech report of fund.krita.org</title><link>https://carlschwan.eu/2021/05/25/tech-report-of-fund.krita.org/</link><pubDate>Tue, 25 May 2021 09:35:35 +0000</pubDate><guid>https://carlschwan.eu/2021/05/25/tech-report-of-fund.krita.org/</guid><description>&lt;p&gt;A few weeks ago, the Krita project announced the Krita Dev Fund. This project
scope was the same as the &lt;a class="link" href="https://fund.blender.org" target="_blank" rel="noopener"
&gt;Blender Dev Fund&lt;/a&gt;. Provide
a stable way to fund the development of Krita via recurring donations.&lt;/p&gt;
&lt;p&gt;Since I was the one who helped with the technical bits on the website and I
heard that were are interested FOSS communities that want to deploy something
similar, I decided to write a small tech report about how we did it in Krita.&lt;/p&gt;
&lt;p&gt;Luckily for us, when we started researching how to create something similar,
we rapidly discovered that the Blender Fund website itself is
&lt;a class="link" href="https://developer.blender.org/source/blender-dev-fund/repository/master/" target="_blank" rel="noopener"
&gt;open source&lt;/a&gt;
and licensed under the same license as Blender: GPL. It made it easy
to reuse the code.&lt;/p&gt;
&lt;p&gt;The system is written with Django for the backend and uses Braintree as
a payment provider. The code was built with Blender need in mind and not
as a reusable project that I could use without any change, so a
fork was inevitable. To make the maintenance more manageable, I decided
to try to stay as near as possible to upstream in our Python code so that
it&amp;rsquo;s easy to rebase the Krita fork when needed.&lt;/p&gt;
&lt;p&gt;The code is hosted &lt;a class="link" href="https://invent.kde.org/websites/fund-krita-org" target="_blank" rel="noopener"
&gt;on invent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Blender Fund is using its own authentification system (&lt;a class="link" href="https://id.blender.org" target="_blank" rel="noopener"
&gt;Blender ID&lt;/a&gt;)
using OAuth2. To connect to Blender Id, a separate Django module for
authentification exists &lt;a class="link" href="https://gitlab.com/blender-institute/blender-id-oauth-client" target="_blank" rel="noopener"
&gt;(blender-id-oauth-client)&lt;/a&gt;.
This could be replaced by a custom implementation using &lt;a class="link" href="https://docs.djangoproject.com/en/3.2/topics/auth/default/" target="_blank" rel="noopener"
&gt;default Django auth module&lt;/a&gt;
or one of the many third-party Django ready-to-use authentification modules. In
the case of Krita I just used blender-id-oauth-client with a few
oauth2 parameters changed.&lt;/p&gt;
&lt;p&gt;The most significant modifications I made were mainly around the HTML templates and
CSS files. Most of these modifications were about replacing Blender with Krita,
and updating the look of the homepage. We couldn&amp;rsquo;t just use Blender&amp;rsquo;s branding!&lt;/p&gt;
&lt;p&gt;There was also quite a bit of work with the layouts of the page
since Krita main CSS theme is using Bootstrap 3 and the Blender Fund website is
using Bootstrap 4 and were was many subtle differences between both versions.&lt;/p&gt;
&lt;p&gt;Updating only the templates made it possible to customize the website&amp;rsquo;s look
and edit the content of the emails sends by the system to use Krita billing
information instead of Blender one.&lt;/p&gt;
&lt;p&gt;In terms of Python code changed, there was also a few instances of &amp;lsquo;Blender&amp;rsquo;
that got replaced by &amp;lsquo;Krita&amp;rsquo;. But all the other changes than were minors.
For example, I added back the progress bar on the landing page since
Blender removed it from fund.blender.org since it wasn&amp;rsquo;t needed anymore.
Another small change was adding to the homepage the amount of the current donation
from the old system.&lt;/p&gt;
&lt;p&gt;A few weeks before launching &lt;a class="link" href="https://fund.krita.org" target="_blank" rel="noopener"
&gt;fund.krita.org&lt;/a&gt;,
I got a pleasant surprise as I wanted to rebase my prototype on top of
upstream before moving Krita Fund to production. I discovered that the
Blender team moved their internal library that handles all the payments
to a separate library. It instantly made the amount of critical code in
Krita&amp;rsquo;s fork smaller and my life much simpler.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! So if you want to support Krita and help them build the best painting
application in the world and available for everyone, make sure to check out the
&lt;a class="link" href="https://fund.krita.org/" target="_blank" rel="noopener"
&gt;Krita Fund&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And if you are enjoying my work all around KDE (Plasma Mobile, NeoChat, websites,
&amp;hellip;), feel free to check out my &lt;a class="link" href="https://liberapay.com/Carl" target="_blank" rel="noopener"
&gt;Liberapay account&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>LAS 2021 and improvements in the applications infrastructure</title><link>https://carlschwan.eu/2021/05/18/las-2021-and-improvements-in-the-applications-infrastructure/</link><pubDate>Tue, 18 May 2021 13:05:35 +0000</pubDate><guid>https://carlschwan.eu/2021/05/18/las-2021-and-improvements-in-the-applications-infrastructure/</guid><description>&lt;p&gt;Last week I attended and even gave a small talk to the Linux App Submit (LAS).
LAS is a cross-distro and cross-desktop event around Linux applications. It&amp;rsquo;s
a good place to learn about all the new cool thing making it easier to
build and distribute Linux applications. This motivated me to improve a bit
more the presence of Plasma Mobile applications in Flathub, but also make
various improvements to &lt;a class="link" href="https://apps.kde.org" target="_blank" rel="noopener"
&gt;apps.kde.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="plasma-mobile-and-flathub"&gt;Plasma Mobile and Flathub&lt;/h2&gt;
&lt;p&gt;Jonah, Devin and myself started packaging more Plasma Mobile applications
to Flathub. Currently we added &lt;a class="link" href="https://flathub.org/apps/details/org.kde.koko" target="_blank" rel="noopener"
&gt;Koko&lt;/a&gt;,
&lt;a class="link" href="https://flathub.org/apps/details/org.kde.index" target="_blank" rel="noopener"
&gt;Index&lt;/a&gt;,
&lt;a class="link" href="https://flathub.org/apps/details/org.kde.vvave" target="_blank" rel="noopener"
&gt;Vvave&lt;/a&gt;,
&lt;a class="link" href="https://flathub.org/apps/details/org.kde.alligator" target="_blank" rel="noopener"
&gt;Alligator&lt;/a&gt; and
&lt;a class="link" href="https://flathub.org/apps/details/org.kde.pix" target="_blank" rel="noopener"
&gt;Pix&lt;/a&gt; to Flathub and more
pull request are already open and hopefully soon merged.&lt;/p&gt;
&lt;h2 id="appskdeorg"&gt;Apps.kde.org&lt;/h2&gt;
&lt;p&gt;&lt;a class="link" href="https://apps.kde.org" target="_blank" rel="noopener"
&gt;Apps.kde.org&lt;/a&gt; also got a few updates. Volker fixed
a regression caused by my &lt;a class="link" href="https://carlschwan.eu/2021/04/18/speeding-up-apps.kde.org/" target="_blank" rel="noopener"
&gt;latest rewrite of the website&lt;/a&gt;
that was preventing the application page to show links to F-Droid and the
Play Store when available. This prompted me to also add additional links
to Flathub, but also to the nightly Windows build.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/05/18/las-2021-and-improvements-in-the-applications-infrastructure/compris.png" data-size="864x590"&gt;
&lt;img src="https://carlschwan.eu/2021/05/18/las-2021-and-improvements-in-the-applications-infrastructure/compris.png" width="864" height="590" loading="lazy"
alt="GCompris is really everywhere"&gt;
&lt;/a&gt;
&lt;figcaption&gt;GCompris is really everywhere&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Another small improvements and not yet visible in the UI is that it&amp;rsquo;s now
possible to list all applications available on specific platforms. For
example to see all the applications available on Android, you can use
&lt;a class="link" href="https://apps.kde.org/platforms/android/" target="_blank" rel="noopener"
&gt;apps.kde.org/platforms/android/&lt;/a&gt;.
Similarly to see all the applications available on the Microsoft Store,
you can use &lt;a class="link" href="https://apps.kde.org/platforms/windows/" target="_blank" rel="noopener"
&gt;apps.kde.org/platforms/windows/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the Android page, some applications have been removed from the store
and we should probably update the metadata accordingly or bring them back.
Hint: It&amp;rsquo;s a good oportunity to get involved!&lt;/p&gt;
&lt;p&gt;The next step is to expose these links in the homepage of the website.&lt;/p&gt;
&lt;h2 id="release-tooling"&gt;Release tooling&lt;/h2&gt;
&lt;p&gt;A while ago, I added support for release information in apps.kde.org.
Unfortunately the &lt;code&gt;&amp;lt;release&amp;gt;&lt;/code&gt; tag isn&amp;rsquo;t often added in KDE applications
for new release. Currently only a handful of applications like
Krita, KDevelop, NeoChat or Calindori are using them. It&amp;rsquo;s a bit sad because
it allows to link to the release announcements, but also link to the
tarball and other artifacts (&lt;code&gt;.exe&lt;/code&gt;, &lt;code&gt;.dmg&lt;/code&gt;, &lt;code&gt;.AppImage&lt;/code&gt;, &amp;hellip;).
Additionally this information are also displayed on Flathub and Discover.&lt;/p&gt;
&lt;p&gt;To improve a bit the situation, for at least the package released by
the release service, I started a &lt;a class="link" href="https://invent.kde.org/sysadmin/release-tools/-/merge_requests/16" target="_blank" rel="noopener"
&gt;merge request for the release-tools&lt;/a&gt;
that adds more information to the AppStream file. This includes bugs
fixed in the last version, a link to the tarball and the link to the
announcement. A solution for other types of artifacts still need to
be figured out, but since not all applications in the release service
provide AppImages or Windows/macOS package, it&amp;rsquo;s more complicated.&lt;/p&gt;
&lt;h2 id="discover"&gt;Discover&lt;/h2&gt;
&lt;p&gt;Another application related improvement I have been working is a revamp
of Discover homepage. This is still work in progress and the final visual
result might change, but here is how it currently looks:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/05/18/las-2021-and-improvements-in-the-applications-infrastructure/discover.png" data-size="1417x898"&gt;
&lt;img src="https://carlschwan.eu/2021/05/18/las-2021-and-improvements-in-the-applications-infrastructure/discover.png" width="1417" height="898" loading="lazy"
alt="Discover new applications with Discover"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Discover new applications with Discover&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;</description></item><item><title>Health of the KDE community</title><link>https://carlschwan.eu/2021/04/29/health-of-the-kde-community/</link><pubDate>Thu, 29 Apr 2021 18:05:35 +0000</pubDate><guid>https://carlschwan.eu/2021/04/29/health-of-the-kde-community/</guid><description>&lt;p&gt;Today, I decided to look at KDE git history and look at the project&amp;rsquo;s health as
a whole. It&amp;rsquo;s inspired by the work of &lt;a class="link" href="https://hpjansson.org/blag/2020/12/16/on-the-graying-of-gnome/" target="_blank" rel="noopener"
&gt;Hans Petter Jansson for GNOME&lt;/a&gt;
and use the tool he made (&lt;a class="link" href="https://github.com/hpjansson/fornalder" target="_blank" rel="noopener"
&gt;fornalder&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fornalder&lt;/strong&gt; is easy to use and the documentation in the readme was
beneficial. I don&amp;rsquo;t know if this is because it was programmed in Rust but
&lt;strong&gt;fornalder&lt;/strong&gt; was blazingly fast and most of the time spent during this
analysis was spent on cloning the repos.&lt;/p&gt;
&lt;p&gt;These stats include all the extragear, plasma, frameworks and release service
repository as well as most of the KDE websites and a few KDE playground
projects I had on my hard drive. For example, it doesn&amp;rsquo;t includes most of the
unmaintained projects (e.g. kdepimlibs, kdelibs, koffice, plasma-mediacenter,
&amp;hellip;). Also important to note, is that this doesn&amp;rsquo;t include translations at
all, since they are stored in SVN and added in the tarballs during the
releasing process.&lt;/p&gt;
&lt;h2 id="active-contributors"&gt;Active Contributors&lt;/h2&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/04/29/health-of-the-kde-community/graph.png" data-size="2560x1200"&gt;
&lt;img src="https://carlschwan.eu/2021/04/29/health-of-the-kde-community/graph.png" width="2560" height="1200" loading="lazy"
alt="Number of contributors by years"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Number of contributors by years&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The explaination for the colors is described by Hans Petter Janssson in his
blog post by:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The stacked histogram above shows the number of contributors who touched the project on a yearly basis. Each contributor is assigned to a generational cohort based on the year of their first contribution. The cohorts tend to shrink over time as people leave.&lt;/p&gt;
&lt;p&gt;There’s a special “drive-by” cohort (in a fetching shade of off-white) for contributors who were only briefly involved, meaning all their activity fits in a three-month window. It’s a big group. In a typical year, it numbers 200-400 persons who were not seen before or since. Most of them contribute a single commit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This first plot is interesting. There is clear sharp increase in contributors
from 1997 to 2009, but from 2010 to 2019, the number of active contributors was
mostly stable with the exception of 2012 probably due to the fall of Nokia.&lt;/p&gt;
&lt;p&gt;On the other side, 2020 attracted a great number of new contributors. I think
this is mostly due to the adoption of gitlab, making it way easier to submit
small patches, but the pandemic probably also played a role here.&lt;/p&gt;
&lt;h2 id="commits-count"&gt;Commits Count&lt;/h2&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/04/29/health-of-the-kde-community/graph-commits.png" data-size="2560x1200"&gt;
&lt;img src="https://carlschwan.eu/2021/04/29/health-of-the-kde-community/graph-commits.png" width="2560" height="1200" loading="lazy"
alt="Number of commits by years"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Number of commits by years&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;In this plot, the fall of Nokia is even more visible with a huge drop in
commits around that time. Fortunately, it seems in 2020 we were able to
recover and in terms of commits, we are at a new all-time high. The amount of
commits per year is particularly impressive. I first thought it was due
to a bug in the analyzer but just looking at the commits count in a few projects,
this makes perfect sense. There are 100 000 commits in Calligra, 55 000 commits
in Krita, 14 000 commits in Kdenlive, 22 000 commits in kde.org, 25 000 commits in KMail,
and a lot of other projects having other 5 000 commits probably explains the
numbers.&lt;/p&gt;
&lt;p&gt;If you are wondering who in the 1999 cohort made most of the contributions,
it&amp;rsquo;s Laurent Montel and who in the 2010 disappeared in 2018 it&amp;rsquo;s &amp;ldquo;Montel Laurent&amp;rdquo;.
Unfortunately I couldn&amp;rsquo;t find a nice way to merge both. In term of commits,
Laurent Montel is the biggest contributor with nearly 60 000 commits and the
second one is &amp;ldquo;Montel Laurent&amp;rdquo; with nearly 45 000 commits.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I would say that KDE is in relatively good health. This is good news
considering all the work ahead of us with the Qt6 and KF6 transition. I hope
these good trends continue in the future.&lt;/p&gt;
&lt;p&gt;You can play with &lt;a class="link" href="db.sqlite" &gt;the sqlite dabase&lt;/a&gt; (&amp;gt;200Mb)&lt;/p&gt;</description></item><item><title>Plasma Calendar Redesign</title><link>https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/</link><pubDate>Sat, 24 Apr 2021 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/</guid><description>&lt;p&gt;Over the last few weeks, I redesigned the default Plasma Calendar
bringing it more in line with the design we want for Breeze Evolution.
The new design removes the lines between the days, uses the default
Plasma highlighting element and uses the Plasma Header component
to provide a consistent look for the headers.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/calendar1.png" data-size="841x529"&gt;
&lt;img src="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/calendar1.png" width="841" height="529" loading="lazy"
alt="The new calendar design"&gt;
&lt;/a&gt;
&lt;figcaption&gt;The new calendar design&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;There were also improvements in term of usability. Most notably, it
is now easier to switch between the month, year and decade overview.
Before, you needed to discover that the month name was clickable but
now just clicking on the day, month or year button works.
Additionally, on a touch screen, you can also swipe the calendar left
or right.&lt;/p&gt;
&lt;figure style="max-width: 600px; margin-left: auto; margin-right: auto;" class="embed-responsive embed-responsive-16by9"&gt;
&lt;video src="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/calendar.mp4" loop autoplay muted&gt;&lt;/video&gt;
&lt;figcaption style="margin-top: 3rem;"&gt;Swiping with the finger or the mouse works now&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These improvements will be available with Plasma 5.22, but some will
land a bit earlier with KDE Framework 5.82.&lt;/p&gt;
&lt;h2 id="what-is-coming-next"&gt;What is coming next?&lt;/h2&gt;
&lt;p&gt;The next step in improving the calendar support in Plasma is improving
the event representation in the month view. I already have
a merge request ready for that, replacing the triangle with colored
dots. Don&amp;rsquo;t worry if you have a very busy shedules, it won&amp;rsquo;t display
more than five at the same time.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/calendar2.png" data-size="840x490"&gt;
&lt;img src="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/calendar2.png" width="840" height="490" loading="lazy"
alt="Calendar with dots for events, much more pleasing for my eyes :D"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Calendar with dots for events, much more pleasing for my eyes :D&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;You can take a look at it in &lt;a class="link" href="https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/243" target="_blank" rel="noopener"
&gt;frameworks/plasma-framework!243&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before the Plasma 5.22 feature freeze, I want to land a few more features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add a button to add events (by opening Korganizer in case it is
installed)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enable the event display by default when Korganizer and kdepim-addons
are installed. This would improve the visibility of the features and
make the calendar applet much more useful.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="update"&gt;Update&lt;/h2&gt;
&lt;p&gt;Since some people asked me about how it looks with a dark theme.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/darl.png" data-size="827x474"&gt;
&lt;img src="https://carlschwan.eu/2021/04/24/plasma-calendar-redesign/darl.png" width="827" height="474" loading="lazy"
alt="Dark theme"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Dark theme&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Also the MR for opening Korganizer &lt;a class="link" href="https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/814/diffs" target="_blank" rel="noopener"
&gt;is ready&lt;/a&gt;!&lt;/p&gt;</description></item><item><title>Translating Hugo based websites with Gettext</title><link>https://carlschwan.eu/2021/04/20/translating-hugo-based-websites-with-gettext/</link><pubDate>Tue, 20 Apr 2021 18:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/04/20/translating-hugo-based-websites-with-gettext/</guid><description>&lt;img src="https://carlschwan.eu/2021/04/20/translating-hugo-based-websites-with-gettext/translation.png" alt="Featured image of post Translating Hugo based websites with Gettext" /&gt;&lt;p&gt;In the Linux world, &lt;a class="link" href="https://en.wikipedia.org/wiki/Gettext" target="_blank" rel="noopener"
&gt;gettext&lt;/a&gt; is
the gold standard for translating content. It&amp;rsquo;s powerful; there is a significant
amount of tooling around it: there are editors like Lokalize, poedit, weblate
and many others, and also libraries and bindings for many languages. But in
the web development world, a unified internalization solution isn&amp;rsquo;t a solved
problem yet. Django uses gettext; many js frameworks are using JSON as a
key-value store of strings, but other formats exist and sometimes some frameworks
provide nothing and everything needs to be done from scratch.&lt;/p&gt;
&lt;p&gt;Unlike Jekyll, Hugo provides some built-in internalization support. This includes
the &lt;code&gt;i18n&lt;/code&gt; function for translating templates, translatable menus and a way to
translate markdown files by adding a translated copies next to the original English
file. Unfortunately, this is not enough. There is no way to automatically notify the
translators when and how a markdown file changed since a page sent to the translators
is the raw markdown file. The second problem is that the translations need to be
extracted and injected in three different places and various formats.
Hugo uses markdown files for the content, a YAML file for the strings in the HTML
templates and a YAML config file for the menu and site metadata translations
(e.g. site title). A third problem is that none of these formats are directly
usable for the KDE translation system and KDE translators that expect po files to work
with their usual tools and workflow.&lt;/p&gt;
&lt;h2 id="bridging-everything"&gt;Bridging everything&lt;/h2&gt;
&lt;p&gt;The solution was to build a bridge between these two worlds. So I created a python
script especially for kde.org using polib for manipulating the translation files.
The python script can extract the translations from the English content and
automatically create the required markdown and YAML files to generate the translated
Hugo website. The easy part was to handle the YAML files. It&amp;rsquo;s just extracting specific
values inside of them. The markdown handling was a bit more tricky. We obliviously
don&amp;rsquo;t want to send one big string with the entire content of the markdown file to
the translators. Instead, we want to split it as best as possible. Each paragraph
is extracted as a separate string, same with each list item and the script even
tries to extract Hugo shortcodes correctly. For example, this is how the following
text is extracted:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Hello
* List item 1
{{/*&amp;lt; img src=&amp;#34;...&amp;#34; alt=&amp;#34;Accessibily description&amp;#34; title=&amp;#34;My image title&amp;#34; &amp;gt;*/}}
{{/*&amp;lt; empty &amp;gt;*/}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;becomes&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#. type: Plain Text
#: content/myfile.md:1
msgid &amp;#34;Hello&amp;#34;
msgstr &amp;#34;&amp;#34;
#. type: List item
#: content/myfile.md:3
msgid &amp;#34;List item 1&amp;#34;
msgstr &amp;#34;&amp;#34;
#. type: Plain Text
#: content/myfile.md:5
msgid &amp;#34;Accessibily description&amp;#34;
msgstr &amp;#34;&amp;#34;
#. type: Plain Text
#: content/myfile.md:5
msgid &amp;#34;My image title&amp;#34;
msgstr &amp;#34;&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is probably the best result that can be reasonably archived. It doesn&amp;rsquo;t
support every markdown feature and especially not inline HTML, but we don&amp;rsquo;t need them in
KDE yet and usually, I prefer to separate the content in markdown from the HTML layout. This
makes it easier to update the theme or other aspects of the website in the future.&lt;/p&gt;
&lt;p&gt;The script is runing on kde.org for almost a year. Maturing along the way to serve the needs of
around 400 pages available for translations in KDE.org. But KDE.org is not the only KDE
website and the next step was to make it possible to translate more websites. At first, this
was archived by copy-pasting the scripts across a few repository, but this is obliviously not
something that scales and generally not a good practice.&lt;/p&gt;
&lt;p&gt;So Phu Nguyen and I (but mostly Phu Nguyen) worked on making the scripts a proper python
package with configuration options and documentation. The primary goal was to make it
easier to reuse it for the other KDE websites, but a nice side effect is that this also
makes it easier for everyone to use it. You can find the source code and documentation of
&lt;a class="link" href="https://invent.kde.org/websites/hugo-i18n" target="_blank" rel="noopener"
&gt;hugo-i18n in invent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Phu and I ported a few websites already. This includes &lt;a class="link" href="https://planet.kde.org" target="_blank" rel="noopener"
&gt;planet.kde.org&lt;/a&gt;,
&lt;a class="link" href="https://kate-editor.org" target="_blank" rel="noopener"
&gt;kate-editor.org&lt;/a&gt;, &lt;a class="link" href="https://timeline.kde.org" target="_blank" rel="noopener"
&gt;timeline.kde.org&lt;/a&gt;,
&lt;a class="link" href="https://elisa.kde.org" target="_blank" rel="noopener"
&gt;elisa.kde.org&lt;/a&gt;, &lt;a class="link" href="https://okular.kde.org" target="_blank" rel="noopener"
&gt;okular.kde.org&lt;/a&gt; and
&lt;a class="link" href="https://apps.kde.org" target="_blank" rel="noopener"
&gt;apps.kde.org&lt;/a&gt;, but help is always welcome to help us porting more of them,
and if you are interested, can join our &lt;a class="link" href="https://webchat.kde.org/#/room/#freenode_#kde-www:matrix.org" target="_blank" rel="noopener"
&gt;Matrix channel&lt;/a&gt;
or IRC channel #kde-www.&lt;/p&gt;
&lt;h2 id="why-all-of-the-trouble-isnt-the-english-version-enough"&gt;Why all of the trouble? Isn&amp;rsquo;t the English version enough?&lt;/h2&gt;
&lt;p&gt;English isn&amp;rsquo;t spoken in large parts of the world and translating websites (and our software)
help us reach more people that wouldn&amp;rsquo;t necessarily be able to use our software or learn about
it if it was only available in English. In short, this brings KDE a step forward konquering
the world. According to our web statistics, more than 10% of the visitors of kde.org are
browsing the translated versions and it is slowly growing.&lt;/p&gt;
&lt;p&gt;It is also an excellent occasion to remind people that translating KDE software, website and
documentation is a great way to get involved inside the KDE community even if you can&amp;rsquo;t
program. &lt;a class="link" href="https://community.kde.org/Get_Involved/translation" target="_blank" rel="noopener"
&gt;So get involved!&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Speeding up apps.kde.org</title><link>https://carlschwan.eu/2021/04/18/speeding-up-apps.kde.org/</link><pubDate>Sun, 18 Apr 2021 18:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/04/18/speeding-up-apps.kde.org/</guid><description>&lt;img src="https://carlschwan.eu/2021/04/18/speeding-up-apps.kde.org/apps.kde.org.png" alt="Featured image of post Speeding up apps.kde.org" /&gt;&lt;p&gt;&lt;a class="link" href="https://apps.kde.org" target="_blank" rel="noopener"
&gt;Apps.kde.org&lt;/a&gt; is a great website listing all the KDE applications and their addons. Under the hood, it&amp;rsquo;s using AppStream, a standard for adding metadata to Linux applications. The Linux application managers (Discover, GNOME Softwares, &amp;hellip;) are displaying them, so they stay up to date and are translated. There was only one problem, apps.kde.org has always been a bit slow to load. It is a problem since slow websites tend to be less visible on Google search results and for the users there aren&amp;rsquo;t a good browsing experience.&lt;/p&gt;
&lt;p&gt;There were many reasons why it was slow. We designed it earlier in a way that on each page load the backend load the data from json files. On the individual application pages, we only need to read one json file but on the homepage every single JSON files was loaded. The Linux kernel is probably caching every JSON file in the memory so that the IO load wasn&amp;rsquo;t that bad, but parsing the JSON files still needed to be done on each page load.&lt;/p&gt;
&lt;p&gt;Using PHP and a Symfony (a big PHP framework) for doing the rooting, templating and internalization (loading additional mo files) of the website adds an overhead. The final result was that the load time for just one html files, was between 300ms and 500ms.&lt;/p&gt;
&lt;p&gt;That doens&amp;rsquo;t sounds like a big deal, but it is one. Rendering a page takes more than just downloading one HTML file, the browser needs to load the other assets (images, CSS and javascript files); it needs to parse the HTML and CSS files and compute the layout. These operations are done in parallel, but the initial loading of the HTML files blocks every other operation. Also, these 300ms loading times can be a lot longer on bad internet connections.&lt;/p&gt;
&lt;p&gt;As often to solve the performance problem, my usual solution is to port the websites to static site generators. This isn&amp;rsquo;t always a solution that can be used but in this case, a generator was already generating the data as JSON files twice a day, so there was no technical reasons for dynamically generating the pages. I choose Hugo, because it is my preferred static site generator, it supports internalization and we have a shared theme for it in KDE so most of the layouts, CSS files and translations can be shared with the other KDE websites.&lt;/p&gt;
&lt;p&gt;Porting to Hugo was not difficult, I was already using a templating engine (TWIG) in the old websites and porting to the Hugo templating engine wasn&amp;rsquo;t complicated. I also wrote a &lt;a class="link" href="https://invent.kde.org/websites/apps-kde-org/-/blob/master/convert_appdata.py" target="_blank" rel="noopener"
&gt;small python script&lt;/a&gt;, converting the json files containing the AppStream metadata to markdown files in a way that Hugo can read it. The side-effect is that this makes the generation slower. Generating apps.kde.org is really pushing Hugo to its limits and even if it is one of the fastest static site generator, it now takes 20s for hugo to generate all the pages. This can be explained by the fact that it generates 240 pages in almost 30 languages.&lt;/p&gt;
&lt;p&gt;Fortunately while making it slower to generate in the CI, it made it faster to load for the users. It moved the loading time from ~300-500ms to under 100ms. This is already a nice speedup, but wasn&amp;rsquo;t enough for me. The second step was to improve the rendering time of homepage. Looking at Google Lighthouse, one of the reasons why the homepage was still slow was the large amount of DOM elements. It was over 2000, so I used some tricks to decrease the amount:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First one was to remove all the addons from the homepage, they aren&amp;rsquo;t really helpful for the visitors to know about them and 15% of the content was about them. Instead I moved these addons information to the applications they extend. This makes it easier to get a list of addons for each application without adding too much clutter to the homepage.&lt;/li&gt;
&lt;li&gt;The second trick was to port the application grid from a CSS flex element to grid. Flex has the disadvantage that it doesn&amp;rsquo;t allow to specify a gab between the items, so to create spacing, I needed to wrap every element in a div with padding. (Flex support gap but only on Firefox for now). &lt;code&gt;display: grid&lt;/code&gt; also has more advantages, for example I don&amp;rsquo;t need to specify how many elements I want per row on each screen sizes but instead can specify the width of an item and its spacing will be automatically adjusted. Here is the new css rule for &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;application-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;grid-gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; and the corresponding changes applied to the HTML. I also merged the two &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element together.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-patch" data-lang="patch"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;div class=&amp;#34;application-list row align-items-stretch&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;lt;div class=&amp;#34;application-list&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ $category := .Params.categoryName }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range where (where site.RegularPages &amp;#34;Section&amp;#34; &amp;#34;applications&amp;#34;) &amp;#34;.Params.appType&amp;#34; &amp;#34;!=&amp;#34; &amp;#34;addon&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ if and (eq $category .Params.MainCategory) (ne .Params.appType &amp;#34;addon&amp;#34;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;div class=&amp;#34;app text-center col-12 col-sm-6 col-md-4 col-lg-3 p-2&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;div class=&amp;#34;p-3 h-100&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;div aria-hidden=&amp;#34;true&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;a href=&amp;#34;{{ .Permalink }}&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;img width=&amp;#34;48&amp;#34; height=&amp;#34;48&amp;#34; src=&amp;#34;/app-icons/{{ .Params.icon }}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- loading=&amp;#34;lazy&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- alt=&amp;#34;{{ .Params.Name }}&amp;#34; title=&amp;#34;{{ .Params.name }}&amp;#34;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;a&amp;gt;&amp;lt;h3&amp;gt;{{ .Params.name }}&amp;lt;/h3&amp;gt;&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;p&amp;gt;{{ .Params.GenericName }}&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;lt;div class=&amp;#34;app text-center&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;lt;a href=&amp;#34;{{ .Permalink }}&amp;#34; class=&amp;#34;d-flex flex-column&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;lt;img width=&amp;#34;48&amp;#34; height=&amp;#34;48&amp;#34; aria-hidden=true class=&amp;#34;icon&amp;#34; src=&amp;#34;/app-icons/{{ .Params.icon }}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ loading=&amp;#34;lazy&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ alt=&amp;#34;{{ .Params.Name }}&amp;#34; title=&amp;#34;{{ .Params.name }}&amp;#34;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;lt;h3&amp;gt;{{ .Params.name }}&amp;lt;/h3&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;lt;p&amp;gt;{{ .Params.GenericName }}&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This change removed 2 DOM elements per item, again reducing the amount of items that the browser need to download, parse and render by another 5%.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m actually wondering if using &lt;code&gt;display: grid&lt;/code&gt; is also easier for the browser engine to render, since the improvement from my impression seemed faster than just 5%.&lt;/p&gt;
&lt;p&gt;The end result is that the homepage get a score of 96/100 on Google Lighthouse, this is a big improvement from 56/100 we were getting a few weeks ago. Hopefully Google PageRank algorithm also like the change.&lt;/p&gt;
&lt;p&gt;There is still a few improvements possibilities, mostly slimming down the CSS files. That should be doable thanks to the fact we can use Hugo to ship a customized version of the scss code shared with the other websites with less imported modules and just the one required for &lt;a class="link" href="https://apps.kde.org" target="_blank" rel="noopener"
&gt;apps.kde.org&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Documentation Improvements in KDE</title><link>https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/</link><pubDate>Fri, 26 Feb 2021 17:00:00 +0000</pubDate><guid>https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/</guid><description>&lt;p&gt;There was many changes over the last few months in KDE developer
documentation tooling. The hope is to make KDE development easier
to both newcomers but also long-time KDE contributors to use
KDE technologies to build cool stuff.&lt;/p&gt;
&lt;h2 id="api-documentation"&gt;Api documentation&lt;/h2&gt;
&lt;p&gt;The tooling for our generated documentation tooling improved. First
of all, KApiDox got a new theme with a cleaner appearance and a
better dark theme. But the improvement goes beyond just theming.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/api.png" data-size="1908x891"&gt;
&lt;img src="https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/api.png" width="1908" height="891" loading="lazy"
alt="API.kde.org new look"&gt;
&lt;/a&gt;
&lt;figcaption&gt;API.kde.org new look&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://invent.kde.org/sdk/doxyqml" target="_blank" rel="noopener"
&gt;Doxyqml&lt;/a&gt;, our documentation
bridge between QML and doxygen, got various improvements, thanks
to Olaf Mandel and Lasse Lopperi. Now QML enums are supported and
the lexer/parser got various bug fixes.&lt;/p&gt;
&lt;p&gt;Speaking of QML documentation, the Kirigami API documentation was
improved and now uses more correctly &lt;code&gt;@inherit&lt;/code&gt; tags and
&lt;code&gt;@property&lt;/code&gt; tags. There is still room for improvements, but the
current state is already a lot better. Most Components are now
showing all their properties correctly and the type of the
property is correct. (&lt;a class="link" href="https://invent.kde.org/frameworks/kirigami/-/commit/6ce80224a9fe46216cf47d29458e16f3519ec693" target="_blank" rel="noopener"
&gt;kirigami!239&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Another improvement is that the generated Kirigami documentation
now shows more accurate names: e.g. &lt;code&gt;Kirigami.Page&lt;/code&gt; instead of
&lt;code&gt;org::kde::kirigami::Page&lt;/code&gt;. This makes it easier to read and
navigate the documentation.&lt;/p&gt;
&lt;p&gt;There was also a bit of background work inside KApiDox, Jannet
added support for QDoc, allowing to use QDoc as an alternative
to Doxygen. This might be a better solution for generating
documentation for projects with a lot of QML.&lt;/p&gt;
&lt;h2 id="high-level-documentation"&gt;High level documentation&lt;/h2&gt;
&lt;p&gt;Since &lt;a class="link" href="https://develop.kde.org" target="_blank" rel="noopener"
&gt;develop.kde.org&lt;/a&gt; was announced
back in September 2020 just before Akademy, it received a steady
stream of updates. In terms of visuals, Jannet replaced the left
sidebar with a better-looking one.&lt;/p&gt;
&lt;p&gt;A Doxygen integration was also added in the Hugo based website,
allowing to link to the API documentation in an easy way (e.g.
&lt;code&gt;[Kirigami.Page](docs:kirigami2;Page)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/doxapi.png" data-size="438x211"&gt;
&lt;img src="https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/doxapi.png" width="438" height="211" loading="lazy"
alt="Link to API documentation"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Link to API documentation&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The Plasma documentation was massively improved. Zren moved his
excellent tutorial about creating Plasma Widgets to
&lt;a class="link" href="https://develop.kde.org/docs/plasma/widget/" target="_blank" rel="noopener"
&gt;/docs/plasma/widget&lt;/a&gt;.
I also moved the &lt;a class="link" href="https://develop.kde.org/docs/plasma/scripting/" target="_blank" rel="noopener"
&gt;Plasma Desktop Scripting tutorial&lt;/a&gt;
and the &lt;a class="link" href="https://develop.kde.org/docs/plasma/theme/" target="_blank" rel="noopener"
&gt;Plasma Theme Tutorial&lt;/a&gt;
from techbase to develop. This was a good occasion to update them to a
more recent version of Plasma since some parts were from the early
days of Plasma 5 and a few bits from KDE4 remained.&lt;/p&gt;
&lt;p&gt;Concerning the Framework documentation, most of the tutorials from
the Framework book, that was written a few years ago to develop.
Same as the Plasma documentation, I used this opportunity to update
the documentation and remove any mentions of deprecated APIS. I
also did the same for most of the old tutorials from &lt;a class="link" href="https://techbase.kde.org" target="_blank" rel="noopener"
&gt;techbase&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/features.png" data-size="1239x831"&gt;
&lt;img src="https://carlschwan.eu/2021/02/26/documentation-improvements-in-kde/features.png" width="1239" height="831" loading="lazy"
alt="List of features oriented tutorials"&gt;
&lt;/a&gt;
&lt;figcaption&gt;List of features oriented tutorials&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Tobias Fella imported the old &lt;a class="link" href="https://develop.kde.org/docs/d-bus/" target="_blank" rel="noopener"
&gt;DBus tutorial&lt;/a&gt;
from techbase and David Redondo wrote a tutorial about how to
&lt;a class="link" href="https://develop.kde.org/docs/sensor-faces/" target="_blank" rel="noopener"
&gt;create sensor faces&lt;/a&gt;
for the new Plasma Monitor.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://develop.kde.org/docs/sensor-faces/images/config.png" alt="Custom sensor faces" /&gt;&lt;/p&gt;
&lt;p&gt;Finally, Clau Cambra wrote a tutorial for getting started in
&lt;a class="link" href="https://develop.kde.org/docs/kirigami/" target="_blank" rel="noopener"
&gt;Kirigami&lt;/a&gt;. The tutorial
will guide you into creating a countdown counter using Kirigami
and QML. This work is part of its Season of KDE project.&lt;/p&gt;
&lt;p&gt;If you enjoy my work, you can sponsor me on &lt;a class="link" href="https://liberapay.com/Carl" target="_blank" rel="noopener"
&gt;Liberapay&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>NeoChat 1.1</title><link>https://carlschwan.eu/2021/02/23/neochat-1.1/</link><pubDate>Tue, 23 Feb 2021 20:00:00 +0000</pubDate><guid>https://carlschwan.eu/2021/02/23/neochat-1.1/</guid><description>&lt;p&gt;Exactly 2 months after &lt;a class="link" href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/" &gt;NeoChat 1.0&lt;/a&gt;, the NeoChat team is happy to announce a new release of NeoChat. NeoChat is a native client for the decentralized communication network Matrix.&lt;/p&gt;
&lt;p&gt;Aside from the many bug fixes, performance improvements, and subtle appearance improvements, NeoChat 1.1 brings many new features that will make your experience with it more convenient.&lt;/p&gt;
&lt;p&gt;Thanks to the work of Hannah, Nicolas, and Tobias, this release also brings NeoChat to many more platforms. Nightly builds of NeoChat are now available on &lt;a class="link" href="https://binary-factory.kde.org/job/Neochat_android/" target="_blank" rel="noopener"
&gt;Android&lt;/a&gt;, &lt;a class="link" href="https://binary-factory.kde.org/job/Neochat_x86_64_flatpak/" target="_blank" rel="noopener"
&gt;Flatpak&lt;/a&gt;, &lt;a class="link" href="https://binary-factory.kde.org/job/NeoChat_Nightly_appimage/" target="_blank" rel="noopener"
&gt;AppImage&lt;/a&gt;, &lt;a class="link" href="https://binary-factory.kde.org/job/NeoChat_Nightly_macos/" target="_blank" rel="noopener"
&gt;macOS&lt;/a&gt; and &lt;a class="link" href="https://binary-factory.kde.org/job/NeoChat_Nightly_win64/" target="_blank" rel="noopener"
&gt;Windows&lt;/a&gt;. Not all of them are considered production ready, but we hope to improve the support for them in future release.&lt;/p&gt;
&lt;h2 id="new-features"&gt;New features&lt;/h2&gt;
&lt;h3 id="first-launch-experience-improvements"&gt;First launch experience improvements&lt;/h3&gt;
&lt;p&gt;Probably the highlight of this release is the completely new login page. It detects the server configuration based on your Matrix Id. This allows you to login to servers requiring Single Sign On (SSO) (like the Mozilla or the incoming Fedora Matrix instance).&lt;/p&gt;
&lt;p&gt;Servers that require agreeing to the TOS before usage are correctly detected now and redirect to their TOS webpage, allowing the user to agree to them instead of silently failing to load the account.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/02/23/neochat-1.1/login.png" data-size="528x794"&gt;
&lt;img src="https://carlschwan.eu/2021/02/23/neochat-1.1/login.png" width="528" height="794" loading="lazy"
alt="Login Page"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Login Page&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id="stickers"&gt;Stickers&lt;/h3&gt;
&lt;p&gt;Everyone loves cute stickers, so now NeoChat supports displaying them too. We don&amp;rsquo;t yet support sending them.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/02/23/neochat-1.1/sticker.png" data-size="418x310"&gt;
&lt;img src="https://carlschwan.eu/2021/02/23/neochat-1.1/sticker.png" width="418" height="310" loading="lazy"
alt="Viewing a Sticker"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Viewing a Sticker&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id="editing-messages"&gt;Editing messages&lt;/h3&gt;
&lt;p&gt;Editing messages is another popular feature of every IM client. NeoChat is now able to show edited messages correctly and also make it possible to edit your messages. The behavior is the same as in Element.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/02/23/neochat-1.1/editing.png" data-size="602x116"&gt;
&lt;img src="https://carlschwan.eu/2021/02/23/neochat-1.1/editing.png" width="602" height="116" loading="lazy"
alt="Editing messages"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Editing messages&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id="multimodal-mode"&gt;Multimodal mode&lt;/h3&gt;
&lt;p&gt;It is now possible to open a room into a new window. This allows you to view and interact with multiple rooms at the same time.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/02/23/neochat-1.1/multimodal.png" data-size="1582x879"&gt;
&lt;img src="https://carlschwan.eu/2021/02/23/neochat-1.1/multimodal.png" width="1582" height="879" loading="lazy"
alt="Multimodal mode"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Multimodal mode&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id="commands"&gt;Commands&lt;/h3&gt;
&lt;p&gt;We added a few commands to NeoChat. Previously you could use &lt;code&gt;/me&lt;/code&gt; and &lt;code&gt;/rainbow&lt;/code&gt;, and we added a few mores: &lt;code&gt;/shrug&lt;/code&gt;, &lt;code&gt;/lenny&lt;/code&gt;, &lt;code&gt;/rainbowme&lt;/code&gt;, &lt;code&gt;/join&lt;/code&gt;, &lt;code&gt;/invite&lt;/code&gt;, &lt;code&gt;/part&lt;/code&gt;, &lt;code&gt;/ignore&lt;/code&gt;, &lt;code&gt;/unignore&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="plasma-integration"&gt;Plasma integration&lt;/h3&gt;
&lt;p&gt;We improved the Plasma integration a bit. Now the number of unread messages is displayed in the Plasma Taskbar. It is using the &lt;code&gt;com.canonical.Unity.LauncherEntry&lt;/code&gt; DBus protocol, so that should be reasonably supported across desktops.&lt;/p&gt;
&lt;h2 id="tarballs"&gt;Tarballs&lt;/h2&gt;
&lt;p&gt;Version 1.1.1 of NeoChat is availabe &lt;a class="link" href="https://download.kde.org/stable/neochat/1.1.1/neochat-1.1.1.tar.xz" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.
The package is signed with my gpg key &lt;a class="link" href="https://carlschwan.eu/gpg.html" &gt;14B0ED91B5783415D0AA1E0A06B35D38387B67BE&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For users, a &lt;a class="link" href="https://flathub.org/apps/details/org.kde.neochat" target="_blank" rel="noopener"
&gt;Flathub version&lt;/a&gt; will be updated shortly.&lt;/p&gt;</description></item><item><title>Pikasso, a simple drawing application in QtQuick with Rust</title><link>https://carlschwan.eu/2021/01/25/pikasso-a-simple-drawing-application-in-qtquick-with-rust/</link><pubDate>Mon, 25 Jan 2021 14:00:35 +0000</pubDate><guid>https://carlschwan.eu/2021/01/25/pikasso-a-simple-drawing-application-in-qtquick-with-rust/</guid><description>&lt;p&gt;Following my &lt;a class="link" href="https://carlschwan.eu/2021/01/20/efficient-custom-shapes-in-qtquick-with-rust/" target="_blank" rel="noopener"
&gt;last blog post&lt;/a&gt;
about using Rust and Lyon to create custom shapes. I&amp;rsquo;m happy to announce
the creation of &lt;a class="link" href="https://invent.kde.org/graphics/pikasso/" target="_blank" rel="noopener"
&gt;Pikasso&lt;/a&gt;, a
very simple drawing program intended to be used on Plasma Mobile.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2021/01/25/pikasso-a-simple-drawing-application-in-qtquick-with-rust/picture.png" data-size="1120x879"&gt;
&lt;img src="https://carlschwan.eu/2021/01/25/pikasso-a-simple-drawing-application-in-qtquick-with-rust/picture.png" width="1120" height="879" loading="lazy"
alt="Pikasso"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Pikasso&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;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&amp;rsquo;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.&lt;/p&gt;
&lt;h2 id="behind-the-scene"&gt;Behind the scene&lt;/h2&gt;
&lt;p&gt;Behind the scene, Pikasso uses Rust and Lyon to do all the drawing.
The entire drawing area is just one &lt;code&gt;QQuickItem&lt;/code&gt; with many &lt;code&gt;QSGNodes&lt;/code&gt;.
This makes Pikasso hardware accelerated.&lt;/p&gt;
&lt;p&gt;When drawing, Pikasso creates &lt;code&gt;DrawEvents&lt;/code&gt; for storing the drawing.
The &lt;code&gt;DrawEvent&lt;/code&gt; contains a &lt;code&gt;QPainterPath&lt;/code&gt; and a few metadata (line width,
pen color and if the &lt;code&gt;QPainterPath&lt;/code&gt; should be filled or not. This is inspired by the
&lt;a class="link" href="https://doc.qt.io/qt-5/qtwidgets-widgets-scribble-example.html" target="_blank" rel="noopener"
&gt;QtWidgets Scribble example&lt;/a&gt;
and the Digikam sketch widgets. Using a &lt;code&gt;QPainterPath&lt;/code&gt; replaces the usage of the &lt;code&gt;std::variant&lt;/code&gt; from my
&lt;a class="link" href="https://carlschwan.eu/2021/01/20/efficient-custom-shapes-in-qtquick-with-rust/" target="_blank" rel="noopener"
&gt;last blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;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 &lt;strong&gt;text rendering&lt;/strong&gt;. Another
advantage of using a &lt;code&gt;QPainterPath&lt;/code&gt; is that we get an SVG export feature
almost for free with a &lt;code&gt;QSvgGenerator&lt;/code&gt;.
&lt;a class="link" href="https://invent.kde.org/graphics/pikasso/-/merge_requests/1/diffs" target="_blank" rel="noopener"
&gt;See the MR from Jonah about this feature.&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;QPainterPath can be natively used with QPainter, but it can also be in
a very simple way transformed to a &lt;code&gt;LyonBuilder&lt;/code&gt; object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rust&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;painterPathToBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;QPainterPath&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_builder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elementCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elementAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isLineTo&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;line_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isMoveTo&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;QPainterPath&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CurveToElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Cubic is encoded with ctrl1 -&amp;gt; CurveToElement, ctrl2 -&amp;gt; CurveToDataElement and to -&amp;gt; CurveToDataElement
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Q_ASSERT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elementCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;CurveToElement doesn&amp;#39;t have data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;ctrl1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elementAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;ctrl2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elementAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elementAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cubic_bezier_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctrl1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctrl1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctrl2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctrl2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// we analysed tree elements instead of just one
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And once we have a &lt;code&gt;LyonBuilder&lt;/code&gt; object we can render our geometries
to the screen. This part didn&amp;rsquo;t change since the last post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;QSGGeometryNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;painterPathToBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LyonGeometry&lt;/span&gt; &lt;span class="n"&gt;lyonGeometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;drawEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lyonGeometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;drawEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;penWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lyonGeometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;QSGGeometry&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;QSGGeometry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QSGGeometry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;defaultAttributes_Point2D&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lyonGeometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;lyonGeometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;QSGGeometry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Point2D&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;vertexDataAsPoint2D&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nl"&gt;vertice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lyonGeometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vertice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vertice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;quint16&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;indexDataAsUShort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nl"&gt;indice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lyonGeometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pikasso, needs to add new nodes for each new DrawEvent but only update
the geometry of the last &lt;code&gt;DrawEvent&lt;/code&gt; since it&amp;rsquo;s the only one who could
have changed. This makes the painting experience very smooth.&lt;/p&gt;
&lt;p&gt;For those interested, this is the &lt;a class="link" href="https://invent.kde.org/graphics/pikasso/-/blob/master/src/drawingarea.cpp#L243" target="_blank" rel="noopener"
&gt;code for the &lt;code&gt;updatePaintNodeData&lt;/code&gt; method&lt;/a&gt;. It&amp;rsquo;s a bit more complex since Pikasso also
handles undo events and needs to cleanup its removed QSGNodes.&lt;/p&gt;
&lt;h2 id="antialiasing"&gt;Antialiasing&lt;/h2&gt;
&lt;p&gt;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 &lt;code&gt;layer.samples&lt;/code&gt;
property:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;DrawingArea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;anchors.fill:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;layer.enabled:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;layer.samples:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="future-goals"&gt;Future goals&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;</description></item><item><title>Efficient custom shapes in QtQuick with Rust</title><link>https://carlschwan.eu/2021/01/20/efficient-custom-shapes-in-qtquick-with-rust/</link><pubDate>Wed, 20 Jan 2021 10:50:35 +0000</pubDate><guid>https://carlschwan.eu/2021/01/20/efficient-custom-shapes-in-qtquick-with-rust/</guid><description>&lt;img src="https://carlschwan.eu/2021/01/20/efficient-custom-shapes-in-qtquick-with-rust/picture.png" alt="Featured image of post Efficient custom shapes in QtQuick with Rust" /&gt;&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2 id="the-current-state-of-custom-geometry-in-qtquick"&gt;The current state of custom geometry in QtQuick&lt;/h2&gt;
&lt;p&gt;Fortunally, the Qt API provides multiple ways to implement custom shapes,
that depending on the needs might be enough.&lt;/p&gt;
&lt;p&gt;There is the Canvas API using the same API as the &lt;code&gt;canvas&lt;/code&gt; API on the web but
in QML. It&amp;rsquo;s easy to use but very slow and I wouldn&amp;rsquo;t recommend it.&lt;/p&gt;
&lt;p&gt;Instead of the Canvas API, from the QML side, there is the
&lt;a class="link" href="https://doc.qt.io/qt-5/qml-qtquick-shapes-shape.html" target="_blank" rel="noopener"
&gt;QtQuick Shapes module&lt;/a&gt;.
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&amp;rsquo;t offer a public C++
API.&lt;/p&gt;
&lt;p&gt;If you need more controls, using C++ will be required to implement custom
&lt;a class="link" href="https://doc.qt.io/qt-5/qquickitem.html" target="_blank" rel="noopener"
&gt;QQuickItem&lt;/a&gt;. Unfortunately drawing
on the GPU using QQuickItem is more
complex than the QPainter API. You can&amp;rsquo;t just use commands like &lt;code&gt;drawRect&lt;/code&gt;,
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
&lt;a class="link" href="https://doc.qt.io/qt-5/qtquick-scenegraph-customgeometry-example.html" target="_blank" rel="noopener"
&gt;official documentation&lt;/a&gt; or from
&lt;a class="link" href="https://www.kdab.com/efficient-custom-shapes-in-qt-quick/" target="_blank" rel="noopener"
&gt;the KDAB tutorial (Efficient custom shapes in Qt Quick)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A QPainter way is also available with &lt;a class="link" href="https://doc.qt.io/qt-5/qquickpainteditem.html" target="_blank" rel="noopener"
&gt;QQuickPaintedItem&lt;/a&gt;,
but it is slow because it renders your shape in a textured rectangle in the
Scene Graph.&lt;/p&gt;
&lt;h2 id="the-rusty-way"&gt;The Rusty way&lt;/h2&gt;
&lt;p&gt;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&amp;rsquo;t easy to use, so I decided to look
at the Rust library ecosystem and in particular at &lt;a class="link" href="https://github.com/nical/lyon" target="_blank" rel="noopener"
&gt;Lyon&lt;/a&gt;,
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.&lt;/p&gt;
&lt;p&gt;Lyon doesn&amp;rsquo;t have any C++ bindings but I got inspired by the recent
&lt;a class="link" href="https://jbbgameich.github.io/misc/2020/12/21/rust-in-a-kde-project.html" target="_blank" rel="noopener"
&gt;blog post from Jonah&lt;/a&gt;
and I need to say the experience of writing bindings was a breeze.&lt;/p&gt;
&lt;p&gt;The first step was creating wrapper &lt;code&gt;struct&lt;/code&gt;s around the
Lyon primitives. &lt;code&gt;LyonPoint&lt;/code&gt;, &lt;code&gt;LyonGeometry&lt;/code&gt; and &lt;code&gt;LyonBuilder&lt;/code&gt; will
later be directly usable from the C++ side.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#[cxx::bridge]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="nn"&gt;ffi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;: &lt;span class="kt"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;: &lt;span class="kt"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;LyonVector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;: &lt;span class="kt"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;: &lt;span class="kt"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;LyonGeometry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;u16&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Rust&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;line_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;relative_move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;: &lt;span class="nc"&gt;LyonVector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;quadratic_bezier_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ctrl&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;cubic_bezier_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ctrl1&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ctrl2&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;build_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;LyonGeometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;build_stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;LyonGeometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We then need to define the methods we declared above. These are all trivial
to implement since they are just wrapping the Lyon API.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ffi&lt;/span&gt;:&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonVector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonGeometry&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Create a wrapper arround Lyon svg path. This struct is opaque from
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// the C++ side so we won&amp;#39;t be able to access the internal object, but
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// we still can call the methods on it.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;LyonBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;: &lt;span class="nc"&gt;WithSvg&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Implement wrapping methods
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;line_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;line_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;quadratic_bezier_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ctrl&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quadratic_bezier_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Lyon Builder constructor
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;: &lt;span class="nc"&gt;Path&lt;/span&gt;::&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;with_svg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The next step was to add the &lt;code&gt;build_fill&lt;/code&gt; 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 &lt;code&gt;QSGGeometry&lt;/code&gt; painting method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;build_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LyonBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;LyonGeometry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buffers&lt;/span&gt;: &lt;span class="nc"&gt;VertexBuffers&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u16&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;VertexBuffers&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vertex_builder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;simple_builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Create the tessellator.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tessellator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FillTessellator&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Compute the tessellation.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tessellator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tessellate_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;FillOptions&lt;/span&gt;::&lt;span class="n"&gt;tolerance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vertex_builder&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LyonGeometry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// convert_points transform lyon::point to our LyonPoint wrapper
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;: &lt;span class="nc"&gt;convert_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;: &lt;span class="nc"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we are almost done with the Rust side, we still need to create the
cargo and corrosion configuration, but I won&amp;rsquo;t go into details in this post.
You can look at how it was done in this &lt;a class="link" href="https://invent.kde.org/carlschwan/libvectorgraphicsquick" target="_blank" rel="noopener"
&gt;pet project&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="using-the-generated-bindings"&gt;Using the generated bindings&lt;/h2&gt;
&lt;p&gt;To make it easy to store and manipulate the path, I create a simple abstraction
to the various SVG path instructions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;QList&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;tessellation.rs.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// Move to the point without drawing a line.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;MoveTo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// The destination.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// Drawe a line to a specific point.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;LineTo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// The destination.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// Draw a cubic bezier curve to the point.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;CubicBezierTo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// First control point.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="n"&gt;ctrl1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Second control point.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="n"&gt;ctrl2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// The destination.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// Close a path.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Close&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// SVG conform path commands
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;PathSection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MoveTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LineTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CubicBezierTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;... &lt;/span&gt;&lt;span class="nc"&gt;Ts&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;overloaded&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ts&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Ts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;()...;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;... &lt;/span&gt;&lt;span class="nc"&gt;Ts&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;overloaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ts&lt;/span&gt;&lt;span class="p"&gt;...)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;overloaded&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Ts&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// The SVG path data. It contains a list of instruction (move to, line to, ...).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;PathData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PathSection&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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&amp;rsquo;s using
the command abstraction, I build previously, but this could directly call
the LyonBuilder methods.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MoveTo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;LineTo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;LineTo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;40.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;CubicBezierTo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;70.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;70.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;LineTo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;LyonPoint&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;40.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Close&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_builder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nl"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overloaded&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;MoveTo&lt;/span&gt; &lt;span class="n"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;LineTo&lt;/span&gt; &lt;span class="n"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;line_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;CubicBezierTo&lt;/span&gt; &lt;span class="n"&gt;cubicBezierTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cubic_bezier_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubicBezierTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctrl1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cubicBezierTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctrl2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cubicBezierTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;m_geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lyonBuilder&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally here is our &lt;code&gt;updatePaintNode&lt;/code&gt; method. It&amp;rsquo;s using the &lt;code&gt;GL_TRIANGLES&lt;/code&gt;
drawing mode and the vertices and indices are copied directly from the geometry
Lyon gave us.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;QSGNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PathItem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;updatePaintNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QSGNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;oldNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UpdatePaintNodeData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;QSGGeometryNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;QSGGeometry&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;oldNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;QSGGeometryNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;QSGGeometry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QSGGeometry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;defaultAttributes_Point2D&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;m_geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;m_geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setIndexDataPattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QSGGeometry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;StaticPattern&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setDrawingMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GL_TRIANGLES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setGeometry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QSGNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;OwnsGeometry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;QSGFlatColorMaterial&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;QSGFlatColorMaterial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;material&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setMaterial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;material&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QSGNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;OwnsMaterial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QSGGeometryNode&lt;/span&gt; &lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;allocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;m_geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;QSGGeometry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Point2D&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;vertexDataAsPoint2D&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nl"&gt;vertice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m_geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vertice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vertice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;quint16&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;indexDataAsUShort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nl"&gt;indice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m_geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;markDirty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QSGNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DirtyGeometry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2 id="custom-shape-in-action"&gt;Custom shape in action&lt;/h2&gt;
&lt;p&gt;I used this technique in a new toy I&amp;rsquo;m building. I&amp;rsquo;m not sure where it is
going, but I currently have this:&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;video src="https://carlschwan.eu/2021/01/20/efficient-custom-shapes-in-qtquick-with-rust/vector.mp4" controls="true" muted="true" loop="true"&gt;&lt;/video&gt;
&lt;/div&gt;</description></item><item><title>NeoChat 1.0.1, first bugfix release</title><link>https://carlschwan.eu/2021/01/13/neochat-1.0.1-first-bugfix-release/</link><pubDate>Wed, 13 Jan 2021 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2021/01/13/neochat-1.0.1-first-bugfix-release/</guid><description>&lt;img src="https://carlschwan.eu/2021/01/13/neochat-1.0.1-first-bugfix-release/picture.png" alt="Featured image of post NeoChat 1.0.1, first bugfix release" /&gt;&lt;p&gt;This version fixes several bugs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NeoChat doesn&amp;rsquo;t require a .well-know configuration in the server to work. (Tobias and Kitsune)&lt;/li&gt;
&lt;li&gt;Edited messages won&amp;rsquo;t show up duplicated anymore. (Carl)&lt;/li&gt;
&lt;li&gt;NeoChat now ask for consent to terms and conditions if required instead of displaying nothing. (Tobias)&lt;/li&gt;
&lt;li&gt;Users avatar in the room list are now displayed correctly. (Carl)&lt;/li&gt;
&lt;li&gt;Fix image saving (Tobias)&lt;/li&gt;
&lt;li&gt;Various graphic glitches have been fixed. (Various authors)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tarball is available in &lt;a class="link" href="https://download.kde.org/stable/neochat/1.0.1/neochat-1.0.1.tar.xz" target="_blank" rel="noopener"
&gt;download.kde.org&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Announcing Season of KDE Selected Projects</title><link>https://carlschwan.eu/2021/01/11/announcing-season-of-kde-selected-projects/</link><pubDate>Mon, 11 Jan 2021 17:00:00 +0000</pubDate><guid>https://carlschwan.eu/2021/01/11/announcing-season-of-kde-selected-projects/</guid><description>&lt;p&gt;I&amp;rsquo;m happy to announce the projects selected for Season of KDE. In this
post we will look at some of the successful proposals, but there is a
full list available &lt;a class="link" href="https://season.kde.org/1/projects/accepted" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="plasma-mobile"&gt;Plasma Mobile&lt;/h2&gt;
&lt;p&gt;Nikunj Goyal will be working under the mentorship of Devin Lin to implement
a global weather alert system for KWeather using the CAP protocol.
Anjani Kumar will work on a D-Bus deamon for KWeather that will provide
weather data. Han Young will mentor this project.&lt;/p&gt;
&lt;p&gt;Rohan Asokan will be working on Kalk and will add a few new features,
including a binary calculator and a scientific mode with more advanced
functions. Han Young will mentor this project.&lt;/p&gt;
&lt;p&gt;Yash Walia will be working on producing new default alarm and ringtones
for KClock. Devin Lin will mentor this project.&lt;/p&gt;
&lt;p&gt;Sai Moukthik Konduru will be working on a basic task management tool
for Plasma Mobile. This will also be mentored by Devin Lin.&lt;/p&gt;
&lt;h2 id="plasma"&gt;Plasma&lt;/h2&gt;
&lt;p&gt;Chirag Goyal will work on the new Plasma Firewall KCM and will make it
possible to share your plasma firewall configuration using the
&lt;a class="link" href="https://store.kde.org" target="_blank" rel="noopener"
&gt;KDE Store&lt;/a&gt;. This project will be mentored by
Lucas Januario.&lt;/p&gt;
&lt;p&gt;Mariam Fahmy Sobhy will add a new backend to Discover for the rpm-ostree
protocol. This backend is required for the new cool immutables distros
like Fedora Kinoite. This project will be mentored by Aleix Pol Gonzalez
and Timothée Ravier.&lt;/p&gt;
&lt;p&gt;Raveesh Agarwal will work on improving the network applets with better
support for multiple network adapters. This project will be mentored by
Jan Grulich.&lt;/p&gt;
&lt;h2 id="promo"&gt;Promo&lt;/h2&gt;
&lt;p&gt;The Promo team will mentor a project allowing us to automate the collection
and analysis of the data we get from our social networks. The goal is to be
able to identify how well our announcements and activity on social media networks
are generating engagements.&lt;/p&gt;
&lt;p&gt;Rohan Reddy will work on extracting the aggregated data from the various
sources (Mastodon, Twitter, Facebook, website stats&amp;hellip;) and Sankalp Das
will work on a data visualization dashboard.&lt;/p&gt;
&lt;p&gt;Manav sethi will work on an application which will allow us to post a
message to multiple social networks at the same time.&lt;/p&gt;
&lt;h2 id="calamares"&gt;Calamares&lt;/h2&gt;
&lt;p&gt;While not a KDE project, Calamares is participating in Season of KDE too.&lt;/p&gt;
&lt;p&gt;Adriaan de Groot will mentor three projects: the first one is porting all the codebase
to Python 3.6 (Hitesh Kumar). The second one consists of porting the documentation
from GitHub Pages to Jekyll. Finally, Anubhav Choudhary will work on various
smaller tasks required by Calamares.&lt;/p&gt;
&lt;h2 id="applications"&gt;Applications&lt;/h2&gt;
&lt;h3 id="kdenlive"&gt;KDenlive&lt;/h3&gt;
&lt;p&gt;Vivek Yadav will work on a separate widget for the bin folders, this will
be helpful for projects with a lot of content, allowing them to be viewed
in a separate window. This project will be mentored by Massimo Stella.&lt;/p&gt;
&lt;h3 id="skanpage-and-skanlite"&gt;SkanPage and SkanLite&lt;/h3&gt;
&lt;p&gt;Smit Shivcharan Patil will work on a QML version of SkanLite called SkanPage.
The goal is to port the old QtQuick Control 1 codebase to Kirigami
and QtQuick Controls 2. This will be mentored by myself (Carl Schwan) and
should provide a way to have multi-page scanning support.&lt;/p&gt;
&lt;h3 id="peruse"&gt;Peruse&lt;/h3&gt;
&lt;p&gt;Mahmoud Ahmed Khalil will add support for reading and creating interactive
fiction comic books in Peruse. This will be mentored by Leinir.&lt;/p&gt;
&lt;h3 id="okular"&gt;Okular&lt;/h3&gt;
&lt;p&gt;Pablo Marcos will work on the Okular website porting it to Hugo and featuring
a new design. This will be mentored by me.&lt;/p&gt;
&lt;h3 id="mark"&gt;Mark&lt;/h3&gt;
&lt;p&gt;Mark is an annotation tool for machine learning datasets. Pranav Gade will
add support for video annotation and Jean Lima Andrade will add support for
audio annotation. Both projects will be mentored by Caio Jordão Carvalho.&lt;/p&gt;
&lt;h2 id="documentation"&gt;Documentation&lt;/h2&gt;
&lt;p&gt;Claudio Cambra will work on improving the developer documentation in
develop.kde.org. This project will be mentored by myself.&lt;/p&gt;
&lt;p&gt;Suraj Kumar Mahto will be working on improving the docs.kde.org website
and will be mentored by Anuj Bansal and me.&lt;/p&gt;
&lt;h2 id="unaccepted-proposals"&gt;Unaccepted Proposals&lt;/h2&gt;
&lt;p&gt;Unfortunately, not every proposal was accepted. There are various reasons for
this. Often proposals are submitted, but are not related to anything we do in
KDE and we can&amp;rsquo;t find mentors for them. In other cases, there are many students who
write a proposal for the same project idea and we can only choose one student per task.&lt;/p&gt;
&lt;p&gt;However, I would like to remind people that you can still contribute to KDE
without having to participate in Season of KDE. Developers will still review your
patches and community members will still help you to contribute.&lt;/p&gt;</description></item><item><title>Matrix Live about NeoChat</title><link>https://carlschwan.eu/2021/01/08/matrix-live-about-neochat/</link><pubDate>Fri, 08 Jan 2021 17:00:00 +0000</pubDate><guid>https://carlschwan.eu/2021/01/08/matrix-live-about-neochat/</guid><description>&lt;p&gt;I was recently interviewed with Tobias by the Matrix folks
about NeoChat as part of Matrix Live. You can read the integrality
of the Matrix update for this week
&lt;a class="link" href="https://matrix.org/blog/2021/01/08/this-week-in-matrix-2021-01-08" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe loading="lazy" src="https://www.youtube.com/embed/BSJY79qN1No" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"&gt;&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Adding comments to your static blog with Mastodon</title><link>https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/</link><pubDate>Tue, 29 Dec 2020 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/</guid><description>&lt;p&gt;&lt;strong&gt;Update 29.01.2023:&lt;/strong&gt; Adapted the code to work with Mastodon 4.0 and replaced linuxrocks.online by floss.social&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 15.03.2023:&lt;/strong&gt; Thanks to &lt;a class="link" href="https://mastodon.online/@veronica" target="_blank" rel="noopener"
&gt;@veronica@mastodon.online&lt;/a&gt;, this code now handles replies in a lot nicer way. You might want check out &lt;a class="link" href="https://mastodon.online/@veronica/110028499674748958" target="_blank" rel="noopener"
&gt;her solution&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 07.07.2023:&lt;/strong&gt; Thanks to &lt;a class="link" href="https://mastodon.blaede.family/@cassidy" target="_blank" rel="noopener"
&gt;@cassidy@blaede.family&lt;/a&gt;, the layout was improved and this now handle emojis&lt;/p&gt;
&lt;p&gt;One of the biggest disadvantages of static site generators is that
they are static and can&amp;rsquo;t include comments.&lt;/p&gt;
&lt;p&gt;There are multiples solutions to solve this problem. You could add
a third party blog engine like Disqus, but this has the drawback
of including a third-party tool with a bad privacy record in your
website. Another solution would be to host an open-source alternative
but this comes at the cost of a higher maintenance burden. Having
to host a database was something we wanted to avoid with a
static site generator.&lt;/p&gt;
&lt;p&gt;In my opinion, a better solution is to leverage the Mastodon and
Fediverse platform. Mastodon is a decentralized social network
and it allows people to communicate with each other without
being on the same server. It is inspired by Twitter, but instead
of tweeting, you write toot.&lt;/p&gt;
&lt;p&gt;When publishing an article, you now only need to also write a
simple toot linking to your article. Then Mastodon has a simple
API to fetch the answer to your toot. This is the code I made for
my Hugo powered blog, but it is easily adaptable for other static
site generators. It will create a button to load comments instead
of loading them for every visitor so that it decreases the load on your
mastodon server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ with .Params.comments }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;comments&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;article-content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Comments&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;With an account on the Fediverse or Mastodon, you can respond to this &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://{{ .host }}/@{{ .username }}/{{ .id }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;post&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;. Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don&amp;#39;t have an account on this one. Known non-private replies are displayed below.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Learn how this is implemented &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;link&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;here.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mastodon-comments-list&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;load-comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Load comments&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;comments-wrapper&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading comments relies on JavaScript. Try enabling JavaScript and reloading, or visit &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://{{ .host }}/@{{ .username }}/{{ .id }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;the original post&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; on Mastodon.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;You need JavaScript to view the comments.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/assets/js/purify.min.js&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;unsafe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;amp;amp;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;amp;lt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;#34;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;amp;quot;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;#39;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;amp;#039;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;emojify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emojis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;emojis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;picture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;picture&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;srcset&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;media&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;(prefers-reduced-motion: no-preference)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;img&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;emoji&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;static_url&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;alt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shortcode&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shortcode&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;width&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;20&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;height&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;20&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;picture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;picture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shortcode&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;picture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outerHTML&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;loadComments&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;commentsWrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;comments-wrapper&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;load-comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Loading&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://{{ .host }}/api/v1/statuses/{{ .id }}/context&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;descendants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;descendants&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;descendants&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;descendants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;descendants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;commentsWrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;descendants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;descendants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;emojify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emojis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{{ .host }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isReply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;in_reply_to_id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{{ .id }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acct&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{{ .username }}&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;emojify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emojis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;avatarSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatarSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;srcset&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatarSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;media&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;(prefers-reduced-motion: no-preference)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;avatarImg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;img&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatarImg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;avatar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatarImg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar_static&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatarImg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;alt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; avatar`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;avatarPicture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;picture&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatarPicture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avatarSource&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatarPicture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avatarImg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;avatar-link&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;external nofollow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`View profile at @&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avatarPicture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;instance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;external nofollow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;span&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;display&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;itemprop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;itemtype&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://schema.org/Person&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;header&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;permalink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;itemprop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`View comment at &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;external nofollow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;en-US&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;dateStyle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;long&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;timeStyle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;short&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;datetime&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;itemprop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;interactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;footer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;favourites_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;faves&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;faves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;faves&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;faves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/favourites`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;faves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`Favorites from &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;faves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;favourites_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;interactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faves&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;article&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`comment-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isReply&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;comment comment-reply&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;itemprop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;itemtype&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://schema.org/Comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interactions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;op&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;op&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Blog post author; &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;op&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Blog post author: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;instanceBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;commentsWrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;DOMPurify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sanitize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outerHTML&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;load-comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadComments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also found some SCSS rules &lt;a class="link" href="https://gitlab.com/ognarb/blog/-/blob/master/assets/scss/partials/article.scss#L310" target="_blank" rel="noopener"
&gt;on my gitlab&lt;/a&gt;. If you blog engine doesn&amp;rsquo;t support converting SCSS to CSS, you can use an &lt;a class="link" href="https://jsonformatter.org/scss-to-css" target="_blank" rel="noopener"
&gt;online SCSS converter&lt;/a&gt; or sass tool from the cli.&lt;/p&gt;
&lt;p&gt;This code is using &lt;a class="link" href="https://github.com/cure53/DOMPurify" target="_blank" rel="noopener"
&gt;DOMPurify&lt;/a&gt;
to sanitize the input, since it is not a great idea to load data from
third party sources without sanitizing them first. Also thanks to
&lt;a class="link" href="https://news.ycombinator.com/item?id=25575111" target="_blank" rel="noopener"
&gt;chrismorgan&lt;/a&gt;, the code
was optimized and is more secure.&lt;/p&gt;
&lt;p&gt;In my blog post, I can now add the following information to my
frontmatter, to make comments appears magically.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;floss.social&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;carlschwan&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;109774012599031406&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Announcing NeoChat 1.0, the KDE Matrix client</title><link>https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/</link><pubDate>Wed, 23 Dec 2020 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/</guid><description>&lt;p&gt;Matrix is an instant messaging system similar to Whatsapp or Telegram, but
uses an open and decentralized network for secure and privacy-protected
communications. NeoChat is a visually attractive Matrix client that works on
desktop computers and mobile phones.&lt;/p&gt;
&lt;h2 id="convergence"&gt;Convergence&lt;/h2&gt;
&lt;p&gt;NeoChat provides an elegant and convergent user interface, allowing
it to adapt to any screen size automatically and gracefully.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.plasma-mobile.org/img/post-2020-11/neochat-drawer.png" alt="Image" /&gt;&lt;/p&gt;
&lt;p&gt;This means you can use it both on desktop computers, where you might want to
have a small bar on the side of your screen, but you can also enjoy NeoChat
on Plasma Mobile and Android phones.&lt;/p&gt;
&lt;p&gt;In fact, NeoChat will be installed by default on the PinePhone KDE edition and
we offer a &lt;a class="link" href="https://binary-factory.kde.org/view/Android/job/Neochat_android/" target="_blank" rel="noopener"
&gt;nightly Android
version&lt;/a&gt;
too. The Android version is for the moment experimental and some
features, like file uploading, don&amp;rsquo;t work yet.&lt;/p&gt;
&lt;h2 id="features"&gt;Features&lt;/h2&gt;
&lt;p&gt;NeoChat provides a timeline with support for simple messages and also allows
you to upload images and video and audio files. You can reply to messages and
add reactions.&lt;/p&gt;
&lt;p&gt;NeoChat provides all the basic features chat application needs: apart from
sending and responding to messages, you can invite users to a room, start
private chats, create new rooms and explore public rooms.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/start-a-chat-dialog.png" data-size="771x879"&gt;
&lt;img src="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/start-a-chat-dialog.png" width="771" height="879" loading="lazy"
alt="Start a chat dialog"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Start a chat dialog&lt;/figcaption&gt;
&lt;/figure&gt; &lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/explore-rooms.png" data-size="771x879"&gt;
&lt;img src="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/explore-rooms.png" width="771" height="879" loading="lazy"
alt="Explore rooms"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Explore rooms&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Some room management features are also available: You can ban or kick
users, upload a room avatar and edit a room&amp;rsquo;s metadata.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/room-setting-dialog.png" data-size="578x447"&gt;
&lt;img src="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/room-setting-dialog.png" width="578" height="447" loading="lazy"
alt="Room Setting dialog"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Room Setting dialog&lt;/figcaption&gt;
&lt;/figure&gt; &lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/neochat-chat-context.png" data-size="1051x756"&gt;
&lt;img src="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/neochat-chat-context.png" width="1051" height="756" loading="lazy"
alt="Message detail dialog"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Message detail dialog&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;The room view contains a sidebar that is automatically displayed on wide
screens, but also appears as a drawer on smaller screens. This sidebar contains
all the information about the room.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/invite.png" data-size="676x692"&gt;
&lt;img src="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/invite.png" width="676" height="692" loading="lazy"
alt="Invitation UI"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Invitation UI&lt;/figcaption&gt;
&lt;/figure&gt; &lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/multiaccount-support.png" data-size="523x579"&gt;
&lt;img src="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/multiaccount-support.png" width="523" height="579" loading="lazy"
alt="Multiaccount Support"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Multiaccount Support&lt;/figcaption&gt;
&lt;/figure&gt; &lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/sidebar.png" data-size="244x676"&gt;
&lt;img src="https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/sidebar.png" width="244" height="676" loading="lazy"
alt="Sidebar"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Sidebar&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;A lot of care has been put into making NeoChat intuitive to use. For
example, copying with Ctrl+C and dragging and dropping images just work; and
the text field gets autofocused so that you are never writing into the void.
NeoChat also integrates an emoji picker, letting you use the greatest invention
of the &lt;del&gt;21st century&lt;/del&gt;. (Note: someone in Mastodon pointed out that
Emojis are from the 20th century and appeared in 1997 in Japan.)&lt;/p&gt;
&lt;h2 id="image-editor"&gt;Image Editor&lt;/h2&gt;
&lt;p&gt;NeoChat also includes a basic image editor that lets you crop and rotate
images before sending them. The image editor is provided by a small library
called KQuickImageEditor.&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;video src="neochat-2020-12-16_10.35.35.webm"
autoplay="true" muted="true" loop="true"&gt;&lt;/video&gt;
&lt;/div&gt;
&lt;p&gt;This library for the moment doesn&amp;rsquo;t have a stable API and is released together
with NeoChat.&lt;/p&gt;
&lt;h2 id="why-matrix-and-neochat"&gt;Why Matrix and NeoChat&lt;/h2&gt;
&lt;p&gt;Matrix is an open network for secure and decentralized communication. This is an
initiative that is very much aligned with KDE&amp;rsquo;s goals of creating an open
operating system for everybody. This is why we need a Matrix client that
integrates into Plasma and thus NeoChat was born.&lt;/p&gt;
&lt;p&gt;NeoChat is a fork of Spectral, another QML client, and uses the
libQuotient library to interact with the Matrix protocol. We would like to send
out a huge thank you to these two projects and their contributors. Without
them, NeoChat wouldn&amp;rsquo;t have been possible.&lt;/p&gt;
&lt;p&gt;NeoChat uses the &lt;a class="link" href="https://develop.kde.org/frameworks/kirigami" target="_blank" rel="noopener"
&gt;Kirigami framework&lt;/a&gt;
and QML to provide an elegant and convergent user interface.&lt;/p&gt;
&lt;h2 id="translations"&gt;Translations&lt;/h2&gt;
&lt;p&gt;NeoChat is fully translated in English, Ukrainian, Swedish, Spanish, Portuguese,
Hungarian, French, Dutch, Catalan (Valencian), Catalan, British English,
Italian, Norwegian Nynorsk and Slovenian. Thanks a lot to all the translators
and if NeoChat is not available in your native language consider joining &lt;a class="link" href="https://community.kde.org/Get_Involved/translation" target="_blank" rel="noopener"
&gt;KDE
localization team&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="what-is-missing"&gt;What is Missing&lt;/h2&gt;
&lt;p&gt;For the moment, encryption support is missing and NeoChat doesn&amp;rsquo;t support video
calls and editing messages yet either. Both things are in the works.&lt;/p&gt;
&lt;p&gt;We are also missing some integration with the rest of the KDE applications,&lt;br&gt;
like with &lt;a class="link" href="https://invent.kde.org/frameworks/purpose" target="_blank" rel="noopener"
&gt;Purpose&lt;/a&gt;, which will
allow NeoChat to be used to share content from other KDE applications; and
with &lt;a class="link" href="https://invent.kde.org/frameworks/sonnet" target="_blank" rel="noopener"
&gt;Sonnet&lt;/a&gt;, which will provide
spellchecking features.&lt;/p&gt;
&lt;p&gt;The fastest way to implement these deficiencies is to get involved! The
NeoChat team is a friendly group of developers and Matrix enthusiasts. Join us
and help us make NeoChat a great Matrix client! You can join us at
&lt;a class="link" href="https://matrix.to/#/#neochat:kde.org" target="_blank" rel="noopener"
&gt;#neochat:kde.org&lt;/a&gt;. We also participate
to Season of KDE, so if you want to get mentored on a project and at the end
get a cool KDE T-Shirt, feel free to say hi.&lt;/p&gt;
&lt;h2 id="tarballs"&gt;Tarballs&lt;/h2&gt;
&lt;p&gt;Version 1.0 of NeoChat is availabe &lt;a class="link" href="https://download.kde.org/stable/neochat/1.0/neochat-1.0.tar.xz" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;,
kquickimageeditor 0.1.2 is availabe &lt;a class="link" href="https://download.kde.org/stable/kquickimageeditor/0.1/kquickimageeditor-0.1.2.tar.xz" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.
Both packages are signed with my gpg key &lt;a class="link" href="https://carlschwan.eu/gpg.html" &gt;14B0ED91B5783415D0AA1E0A06B35D38387B67BE&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;A Flathub release will hopefully be released in the next few days. We will
update this post when it is available.&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;The Flathub version is &lt;a class="link" href="https://flathub.org/apps/details/org.kde.neochat" target="_blank" rel="noopener"
&gt;available&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Doxyqml 0.5.1 release</title><link>https://carlschwan.eu/2020/11/29/doxyqml-0.5.1-release/</link><pubDate>Sun, 29 Nov 2020 19:30:00 +0000</pubDate><guid>https://carlschwan.eu/2020/11/29/doxyqml-0.5.1-release/</guid><description>&lt;p&gt;I&amp;rsquo;m happy to announce the release of Doxyqml 0.5.1. Doxyqml is a python
program allowing to document QML APIs with the help of Doxygen. This
version includes a &lt;a class="link" href="https://invent.kde.org/sdk/doxyqml/-/commit/85800657984b5a257da96640290a2b8ce5e8f78f" target="_blank" rel="noopener"
&gt;single commit&lt;/a&gt;
contributed by Olaf Mandel adding supports for recent versions of Doxygen (&amp;gt; 1.8.20).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="pypi.org/project/doxyqml/0.5.1/" &gt;Pypi page for this release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://invent.kde.org/sdk/doxyqml/-/releases/0.5.1" target="_blank" rel="noopener"
&gt;Invent page for this release&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>KDE.org migrated to Hugo</title><link>https://carlschwan.eu/2020/10/30/kde-org-hugo/</link><pubDate>Fri, 30 Oct 2020 18:00:00 +0000</pubDate><guid>https://carlschwan.eu/2020/10/30/kde-org-hugo/</guid><description>&lt;img src="https://carlschwan.eu/2020/10/30/kde-org-hugo/heading_hugo.png" alt="Featured image of post KDE.org migrated to Hugo" /&gt;&lt;p&gt;&lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;KDE.org&lt;/a&gt; now uses &lt;a class="link" href="https://gohugo.io" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt;. Hugo is a fast and modern static site
generator written in Go. It provides a few improvements over the old system that was
using plain PHP. A large part of the work was done by Anuj during GSoC 2020. This
was a massive work, converting the repository storing more than 20 years of KDE
history.&lt;/p&gt;
&lt;p&gt;The website is now generated once and no longer uses PHP to generate itself at runtime.
This improves the loading speed of the website, but the speed boost is not significant,
since the PHP code used before was quite small and KDE&amp;rsquo;s servers are powerful.&lt;/p&gt;
&lt;p&gt;But the biggest improvement is in terms of features. We are now working with markdown
files instead of raw HTML files, this makes the life of the promo team much easier.&lt;/p&gt;
&lt;p&gt;The internationalization of the website now creates a unique URL per language, this
should allow Google to link to the version of the website using the correct language.
A &lt;a class="link" href="https://kde.org/fr/" target="_blank" rel="noopener"
&gt;French&lt;/a&gt;, &lt;a class="link" href="https://kde.org/uk/" target="_blank" rel="noopener"
&gt;Ukrainian&lt;/a&gt;,
&lt;a class="link" href="https://kde.org/ca/" target="_blank" rel="noopener"
&gt;Catalan&lt;/a&gt;, &lt;a class="link" href="https://kde.org/nl/" target="_blank" rel="noopener"
&gt;Dutch&lt;/a&gt;, and a few more languages
are already available. There is also a proper language selector! We also don&amp;rsquo;t need
to manually tag each string for translations.&lt;/p&gt;
&lt;p&gt;There is also now an &lt;a class="link" href="https://kde.org/announcements/index.xml" target="_blank" rel="noopener"
&gt;RSS feed&lt;/a&gt; with all the
latest announcements. Another big improvement is that the announcements list is
autogenerated and no longer modified by hand and with the help of the release scripts.&lt;/p&gt;
&lt;p&gt;Another nice change for website developers is that now the SCSS code for the individual
pages is located in the kde-org repository itself instead of another repository.
Overall the developer experience is much better, there is no need to set up an apache
server and to the PHP configs to include the &lt;a class="link" href="https://invent.kde.org/websites/capacity" target="_blank" rel="noopener"
&gt;capacity framework&lt;/a&gt;, just to get the
website running locally. Now you only need to download the Hugo binary from their
release page and run it on the repo.&lt;/p&gt;
&lt;h2 id="hugo-and-gettext"&gt;Hugo and Gettext&lt;/h2&gt;
&lt;p&gt;The internationalization of KDE.org was quite a challenge. When working on a multilingual
website with Hugo, Hugo expects a markdown document per language for each translated
page.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;plasma-desktop.md
plasma-desktop.es.md
plasma-desktop.fr.md
plasma-desktop.uk.md
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The problem is that traditional translations workflow rely on a string-based approach,
where a document is split in paragraphs and translated individually. So I couldn&amp;rsquo;t
just put each file markdown file as big blobs in the po files. To solve this problem,
I created a python script splitting the markdown files in paragraphs, simplifying
the markdown syntax (removing leading &lt;code&gt;#&lt;/code&gt; and &lt;code&gt;+&lt;/code&gt; for heading and list item). This
script also handles Hugo shortcodes transforming:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{{&amp;lt; img caption=&amp;#34;My figure caption&amp;#34; alt=&amp;#34;My accessible description&amp;#34; src=&amp;#34;...&amp;#34; &amp;gt;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;in two strings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My figure caption&lt;/li&gt;
&lt;li&gt;My accessible description&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The script can obliviously put the individual strings back in place. The scripts also
handle the menu translation, and the translations of the strings in the footers.
For now, the script is just a file in the kde-org repository, but I would like to
transform it to a standalone library so that other gettext and Hugo users can
translate their website.&lt;/p&gt;
&lt;p&gt;Using Hugo API, the language selector was trivial to write:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;ul class=&amp;#34;navbar-nav ml-auto&amp;#34;&amp;gt;
{{ if .IsTranslated }}
&amp;lt;li class=&amp;#34;nav-item dropdown&amp;#34; aria-describedby=&amp;#34;language-picker-description&amp;#34;&amp;gt;
&amp;lt;p class=&amp;#34;sr-only&amp;#34; id=&amp;#34;language-picker-description&amp;#34;&amp;gt;{{ i18n &amp;#34;Select-your-language&amp;#34; }}&amp;lt;/p&amp;gt;
&amp;lt;a class=&amp;#34;nav-link dropdown-toggle&amp;#34; href=&amp;#34;#&amp;#34; data-toggle=&amp;#34;dropdown&amp;#34; role=&amp;#34;button&amp;#34; aria-haspopup=&amp;#34;true&amp;#34; aria-expanded=&amp;#34;false&amp;#34;&amp;gt;
&amp;lt;img src=&amp;#34;/Language-Icons/icon20x24px-exported-transparent.png&amp;#34; alt=&amp;#34;&amp;#34; /&amp;gt;
&amp;lt;span&amp;gt;{{ i18n &amp;#34;translations&amp;#34; }}&amp;lt;/span&amp;gt;
&amp;lt;/a&amp;gt;
&amp;lt;div class=&amp;#34;dropdown-menu dropdonw-trans&amp;#34; role=&amp;#34;listbox&amp;#34;&amp;gt;
&amp;lt;a class=&amp;#34;dropdown-item&amp;#34; aria-selected=&amp;#34;true&amp;#34; hreflang=&amp;#34;{{ .Site.Language.Lang }}&amp;#34; role=&amp;#34;option&amp;#34; lang=&amp;#34;{{ .Site.Language.Lang }}&amp;#34; href=&amp;#34;{{ .Permalink }}&amp;#34;&amp;gt;{{ .Site.Language.LanguageName }}&amp;lt;/a&amp;gt;
{{ range .Translations }}
&amp;lt;a class=&amp;#34;dropdown-item&amp;#34; hreflang=&amp;#34;{{ .Language.Lang }}&amp;#34; role=&amp;#34;option&amp;#34; lang=&amp;#34;{{ .Language.Lang }}&amp;#34; href=&amp;#34;{{ .Permalink }}&amp;#34;&amp;gt;{{ .Language.LanguageName }}&amp;lt;/a&amp;gt;
{{ end }}
&amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
{{ end }}
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a bit verbose because this selector is also fully accessible for screen readers.&lt;/p&gt;
&lt;p&gt;There are a few more tricks employed in kde.org to improve the internationalization, for
example when a page doesn&amp;rsquo;t exist in a language, there is an Apache rule redirecting it
to the English version. Another nice trick is that there is a special Hugo shortcode
called &lt;code&gt;i18n_var&lt;/code&gt; and used to parametrize the strings. For example:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{{&amp;lt; i18n_var &amp;#34;Today KDE releases a bugfix update to KDE Plasma 5, versioned %[1]s&amp;#34; &amp;#34;5.20.2&amp;#34; &amp;gt;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the extractor is clever and only extract the part that needs to be translated.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://invent.kde.org/websites/kde-org/-/blob/master/translations.py" target="_blank" rel="noopener"
&gt;The script&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can comment this post on &lt;a class="link" href="https://www.reddit.com/r/kde/comments/jl1pim/kdeorg_migrated_to_hugo/?" target="_blank" rel="noopener"
&gt;r/kde&lt;/a&gt;
and &lt;a class="link" href="https://news.ycombinator.com/item?id=24944537" target="_blank" rel="noopener"
&gt;HN&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Announcing MyKDE</title><link>https://carlschwan.eu/2020/10/03/announcing-mykde/</link><pubDate>Sat, 03 Oct 2020 12:00:00 +0000</pubDate><guid>https://carlschwan.eu/2020/10/03/announcing-mykde/</guid><description>&lt;img src="https://carlschwan.eu/2020/10/03/announcing-mykde/homepage.png" alt="Featured image of post Announcing MyKDE" /&gt;&lt;p&gt;I&amp;rsquo;m happy to announce the successful deployment of the new identity system
in KDE, codename MyKDE. The new identity system is now available in
&lt;a class="link" href="https://my.kde.org" target="_blank" rel="noopener"
&gt;my.kde.org&lt;/a&gt;. You should be able to login into the my.kde.org website
with your normal KDE credential.&lt;/p&gt;
&lt;p&gt;For the moment, only the wikis are using MyKDE but in the coming months
this should change with more and more services switching to MyKDE. I will
let you all know of the progress of the migration.&lt;/p&gt;
&lt;h1 id="faq"&gt;FAQ:&lt;/h1&gt;
&lt;h2 id="why-the-move"&gt;Why the move?&lt;/h2&gt;
&lt;p&gt;identity.kde.org is using OpenLDAP for user management with a small PHP
frontend allowing the account creation. And we had the following problems
with it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Account removal is hard, requiring significant manual intervention and
effort (several hours work in some instances)&lt;/li&gt;
&lt;li&gt;Account registration takes 30 seconds or more to complete, creating a poor
user experience&lt;/li&gt;
&lt;li&gt;Groups don&amp;rsquo;t scale effectively&lt;/li&gt;
&lt;li&gt;Anti-spam measures are too crude&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More on that in &lt;a class="link" href="https://phabricator.kde.org/T8449" target="_blank" rel="noopener"
&gt;T8449&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="will-my-data-be-migrated"&gt;Will my data be migrated?&lt;/h2&gt;
&lt;p&gt;Yes, your data are migrated just by login into MyKDE once. This will migrate
all your group membership (KDE developer, Akademy Team, &amp;hellip;), personal
data and password.&lt;/p&gt;
&lt;p&gt;For users who didn&amp;rsquo;t log into MyKDE during the migration period. If you are
a KDE developer or KDE e.V. member, your account will be imported as a
disabled account and you will need to ask sysadmins to enabled it. For the
rest of the users of identity.kde.org who don&amp;rsquo;t have a membership to groups,
your account will be removed. We think this is the best solution because there
is no need to store personal information that we don&amp;rsquo;t need from users who
don&amp;rsquo;t use the system anymore. If you want to conserve your account (and
username), please log at least once. We will send periodic emails reminding
you of migrating your account.&lt;/p&gt;
&lt;h2 id="how-do-i-register-a-new-account-in-mykde"&gt;How do I register a new account in MyKDE?&lt;/h2&gt;
&lt;p&gt;For the moment the possibility of registering a new account is disabled in
MyKDE and the only possibility is to create an identity.kde.org account and
then migrate your account. This is due to the fact we don&amp;rsquo;t want some
accounts existing only in MyKDE. This will naturally change when we migrate
fully to MyKDE.&lt;/p&gt;
&lt;h2 id="how-to-i-collect-a-badge"&gt;How to I collect a badge?&lt;/h2&gt;
&lt;p&gt;MyKDE has the possibility to grant badges to users. For the moment there is
only one badge enabled: KDE developer. This badge is given to every KDE
developer and is displayed on their public profile (if enabled). When the
new Season of KDE website will be deployed, it will be also possible to have
an SoK mentor and SoK mentee badge.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m interested in ideas of new badges (and badge designs), so please let me
know if you have a genius idea that doesn&amp;rsquo;t gamify KDE contribution (e.g no made
100/1000/10 000 commits badge).&lt;/p&gt;
&lt;p&gt;Note that you are in control of that badge get displayed.&lt;/p&gt;
&lt;h2 id="what-is-the-public-profile-functionality"&gt;What is the public profile functionality?&lt;/h2&gt;
&lt;p&gt;One of the new features of MyKDE is the possibility to have a public profile.
This public profile is opt-in, so you need to explicitly enable it to make
it work and it can display a small bio, your avatar, name, username, social
network account, Liberapay account, and badges earned.&lt;/p&gt;
&lt;p&gt;This is for example how it looks for &lt;a class="link" href="https://my.kde.org/user/carlschwan/" target="_blank" rel="noopener"
&gt;me&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="can-i-contribute-to-mykde"&gt;Can I contribute to MyKDE?&lt;/h2&gt;
&lt;p&gt;Yes, the source code is hosted in &lt;a class="link" href="https://invent.kde.org/websites/my-kde-org" target="_blank" rel="noopener"
&gt;websites/my-kde-org&lt;/a&gt;
and all the deployment information can be found in the
&lt;a class="link" href="https://sysadmin-docs.kde.org/services/mykde.html" target="_blank" rel="noopener"
&gt;KDE sysadmin documentation&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>SoK 2021: Mentor Wanted!</title><link>https://carlschwan.eu/2020/09/30/sok-2021-mentor-wanted/</link><pubDate>Wed, 30 Sep 2020 11:40:00 +0000</pubDate><guid>https://carlschwan.eu/2020/09/30/sok-2021-mentor-wanted/</guid><description>&lt;img src="https://kde.org/content/people/GSoC.jpg" alt="Featured image of post SoK 2021: Mentor Wanted!" /&gt;&lt;p&gt;The Season of KDE is a 3 weeks long program that provides an opportunity for
people to do mentored projects for KDE.&lt;/p&gt;
&lt;p&gt;We are still looking for more mentors for SoK 2021. So please consider mentoring
for this year season and adding ideas related to the project you are working on
in the &lt;a class="link" href="https://community.kde.org/SoK/Ideas/2021" target="_blank" rel="noopener"
&gt;Wiki page&lt;/a&gt;. And joining the #kde-soc
channel.&lt;/p&gt;
&lt;p&gt;Possible ideas includes but not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Updating your application user documentation&lt;/li&gt;
&lt;li&gt;Write technical documentation for a framework&lt;/li&gt;
&lt;li&gt;Port your application to non-deprecated Qt and KF5 APIs&lt;/li&gt;
&lt;li&gt;Modernize some part of the code of an application&lt;/li&gt;
&lt;li&gt;Implement a new feature&lt;/li&gt;
&lt;li&gt;And a lot more, SoK is not limited to development :)&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>What is cooking on KDE websites this month (September)?</title><link>https://carlschwan.eu/2020/09/28/what-is-cooking-on-kde-websites-this-month-september/</link><pubDate>Mon, 28 Sep 2020 16:30:35 +0000</pubDate><guid>https://carlschwan.eu/2020/09/28/what-is-cooking-on-kde-websites-this-month-september/</guid><description>&lt;p&gt;This month a few cool things happened to the KDE websites.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The wiki instance we use was migrated to MediaWiki 3.34 (latest LTS version), bringing a few improvements in the translations module and fixing the problem where translated pages couldn&amp;rsquo;t be moved around. The commenting plugin was sadly discountinued in this version and instead the Echo extension was added which provides a way to ping people.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Another improvement related to the wikis is the creation of a small module allowing user to authenticate with the Identity replacement. The source code can be found &lt;a class="link" href="https://invent.kde.org/websites/mykde-mediawiki" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;. This brings us a step closer to the replacement of identity.kde.org.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This month, a completely rewritten backend for Season of KDE was merged. This was part of the work Anuj did during GSoc this year. It is not yet deployed into production, but I host a &lt;a class="link" href="https://season.carlschwan.eu" target="_blank" rel="noopener"
&gt;demo&lt;/a&gt; so you can try it. The new features allow for a mentor to comment on proposals, and mentees can now use markdown when writing a proposal. The new system now uses Symfony, one of the biggest PHP frameworks, and is integrated in MyKDE too, so mentors and mentees get a badge after completing SoK. The admin interface was also significantly improved. Great work Anuj!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/09/28/what-is-cooking-on-kde-websites-this-month-september/season1.png" data-size="1131x882"&gt;
&lt;img src="https://carlschwan.eu/2020/09/28/what-is-cooking-on-kde-websites-this-month-september/season1.png" width="1131" height="882" loading="lazy"
alt="Season homepage"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Season homepage&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;
&lt;a href="https://carlschwan.eu/2020/09/28/what-is-cooking-on-kde-websites-this-month-september/season2.png" data-size="1502x482"&gt;
&lt;img src="https://carlschwan.eu/2020/09/28/what-is-cooking-on-kde-websites-this-month-september/season2.png" width="1502" height="482" loading="lazy"
alt="Season admin"&gt;
&lt;/a&gt;
&lt;figcaption&gt;Season admin&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;And while at it, please consider mentoring for this year&amp;rsquo;s SoK program. It is a good opportunity to bring new blood to KDE. &lt;a class="link" href="https://community.kde.org/SoK/Ideas/2021" target="_blank" rel="noopener"
&gt;community.kde.org/SoK/Ideas/2021&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The new developer documentation website also got a few updates and now contains a complete tutorial about how to write a &lt;a class="link" href="https://develop.kde.org/docs/plasma/" target="_blank" rel="noopener"
&gt;Plasma Widget&lt;/a&gt;. Thanks to Zren for this contribution.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Elisa, everyone&amp;rsquo;s favorite music player, also got a new website developed by Anubhav Choudhary and Nikunj Goyal. You can check it out at &lt;a class="link" href="https://elisa.kde.org" target="_blank" rel="noopener"
&gt;elisa.kde.org&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Subtitle Composer, a subtitle editor, also got a new website developed by Thiago Sueto. You can check it out at &lt;a class="link" href="https://subtitlecomposer.kde.org/" target="_blank" rel="noopener"
&gt;subtitlecomposer.kde.org/&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a class="link" href="https://atelier.kde.org/" target="_blank" rel="noopener"
&gt;atelier website&lt;/a&gt; was also updated by Lays Rodrigues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://kdemail.net/" target="_blank" rel="noopener"
&gt;KDEMail.net&lt;/a&gt; now uses the Aether theme.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I sent a proposal to use a fork of the Blender Fund tool for KDE Fundraising in the community mailing list. You can check out the demo for &lt;a class="link" href="https://fund.carlschwan.eu" target="_blank" rel="noopener"
&gt;KDE&lt;/a&gt; and &lt;a class="link" href="https://krita-fund.carlschwan.eu" target="_blank" rel="noopener"
&gt;Krita&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Presenting Kontrast</title><link>https://carlschwan.eu/2020/09/15/presenting-kontrast/</link><pubDate>Tue, 15 Sep 2020 09:46:00 +0000</pubDate><guid>https://carlschwan.eu/2020/09/15/presenting-kontrast/</guid><description>&lt;p&gt;Kontrast is a contrast checker available for desktop and mobile devices. You can
use Kontrast to choose background and text color combinations for your website or
app that your users will find easy to read. Kontrast can help you improve the
accessibility for your site or app for people with vision problems.&lt;/p&gt;
&lt;p&gt;Kontrast won&amp;rsquo;t catch all the problems, but it should still be very helpful to catch
many issues early on, when designing your interface.&lt;/p&gt;
&lt;p&gt;I released the first version of Kontrast earlier this month.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn.kde.org/screenshots/kontrast/kontrast.png" alt="Kontrast main page" /&gt;&lt;/p&gt;
&lt;p&gt;Another big feature of Kontrast is the possibility to generate random color
combinations with good contrast. These colors can be saved in the application itself,
so that you can keep a particularly good color combination for later use.&lt;/p&gt;
&lt;p&gt;Kontrast is available for the Linux desktop, &lt;a class="link" href="https://plasma-mobile.org" target="_blank" rel="noopener"
&gt;Plasma Mobile&lt;/a&gt; and
there is also a Beta version for Android.&lt;/p&gt;
&lt;p&gt;This application is built using the excellent &lt;a class="link" href="https://develop.kde.org/frameworks/kirigami" target="_blank" rel="noopener"
&gt;Kirigami&lt;/a&gt;
framework.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn.kde.org/screenshots/kontrast/kontrat_mobile.png" alt="Kontrast mobile view" /&gt;&lt;/p&gt;
&lt;p&gt;You can download and install Kontrast from &lt;a class="link" href="https://flathub.org/apps/details/org.kde.kontrast" target="_blank" rel="noopener"
&gt;Flathub&lt;/a&gt; and
a nightly build is also available &lt;a class="link" href="https://binary-factory.kde.org/job/Kontrast_android/" target="_blank" rel="noopener"
&gt;in binary factory&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The tarball is available &lt;a class="link" href="https://download.kde.org/stable/kontrast/kontrast-1.0.2.tar.xz" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;
and is signed with my gpg key &lt;a class="link" href="https://carlschwan.eu/gpg.html" &gt;14B0ED91B5783415D0AA1E0A06B35D38387B67BE&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="future-improvements"&gt;Future Improvements&lt;/h2&gt;
&lt;p&gt;Future plans include improving the Android build, possibly release a stable
build of the Android version and also create a QtQuick-based color picker
that is better integrated with the application.&lt;/p&gt;</description></item><item><title>Develop.kde.org</title><link>https://carlschwan.eu/2020/09/13/develop.kde.org/</link><pubDate>Sun, 13 Sep 2020 13:50:00 +0000</pubDate><guid>https://carlschwan.eu/2020/09/13/develop.kde.org/</guid><description>&lt;img src="https://carlschwan.eu/2020/09/13/develop.kde.org/homepage.png" alt="Featured image of post Develop.kde.org" /&gt;&lt;p&gt;The &lt;a class="link" href="https://develop.kde.org/" target="_blank" rel="noopener"
&gt;new website&lt;/a&gt; for kde developement information was deployed during the
&lt;a class="link" href="https://akademy.kde.org/2020" target="_blank" rel="noopener"
&gt;Akademy&lt;/a&gt; and contains many information about
building awesome stuff using KDE tools and large collection of Qt based
libraries.&lt;/p&gt;
&lt;p&gt;The long term goal of this new website is to increase the first and third
parties use of the KDE Frameworks and development tools. To achieve this goal,
this website will provide high quality and complete documentation about the
usage of the KDE Frameworks and other libraries (a quite ambitious goal I know),
but also provide marketting content for the libraries to offer them a bigger
visibility in the internet.&lt;/p&gt;
&lt;p&gt;The more short term and more realistic goal is to import the existing tutorials
available from various places (&lt;a class="link" href="https://techbase.kde.org" target="_blank" rel="noopener"
&gt;techbase&lt;/a&gt;,
&lt;a class="link" href="https://share.kde.org/s/jqpMS4gPs3HoxLN" target="_blank" rel="noopener"
&gt;the framework book&lt;/a&gt;, the plasma mobile
docs and other more hidden places. And more importantly while importing the
content, also update and improve it and allow other in the community to review
the content for correctness. Another big task is to better organize the content
in logical sections.&lt;/p&gt;
&lt;h2 id="behind-the-scene"&gt;Behind the scene&lt;/h2&gt;
&lt;p&gt;The website is powered by the Hugo static site generator and by a fork of the
&lt;a class="link" href="https://github.com/google/docsy/" target="_blank" rel="noopener"
&gt;docsy&lt;/a&gt; theme. The modifications to the theme
are the support of gitlab web editor, the integration of KDE branding and support
of linking to &lt;a class="link" href="https://api.kde.org" target="_blank" rel="noopener"
&gt;api.kde.org&lt;/a&gt; class and function with macros.
In term of design, I&amp;rsquo;m not entirely satisfied yet but for a first it&amp;rsquo;s good
enough and because I know developers like dark themes, the website also supports
a dark via &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All these changes allow us to provide a custom website tailored to our needs
(git based code review, web interface to edit the page if needed, custom design,
api.kde.org integration and good separation between the content and the layout
of the page).&lt;/p&gt;
&lt;p&gt;Future planned improvements to the infrastructure are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; to test most of the code examples, to make sure the code
examples still compiles and there is no regressions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;You can find many tasks in &lt;a class="link" href="https://invent.kde.org/documentation/develop-kde-org/-/issues" target="_blank" rel="noopener"
&gt;Invent&lt;/a&gt;,
porting old tutorials and improving them can be a good way to learn them or
better understand them.&lt;/p&gt;
&lt;p&gt;You can also review &lt;a class="link" href="https://invent.kde.org/documentation/develop-kde-org/-/merge_requests" target="_blank" rel="noopener"
&gt;open merge requests&lt;/a&gt;
and look if the information are correct.&lt;/p&gt;
&lt;p&gt;Thanks to (in no particular order) Paul Brow, Suraj Kumar Mahto, Tobias Fella,
David Barchiesi, Han Young, Jonah Brüchert, Nicolas Fella, Nate Graham, Cyril
Rossi, Ahmad Samir and Kevin Ottens for their help contributing new content
or/and reviewing open merge requests.&lt;/p&gt;</description></item><item><title>Making Koko Great for Desktop Use</title><link>https://carlschwan.eu/2020/06/06/making-koko-great-for-desktop-use/</link><pubDate>Sat, 06 Jun 2020 13:50:00 +0000</pubDate><guid>https://carlschwan.eu/2020/06/06/making-koko-great-for-desktop-use/</guid><description>&lt;img src="https://carlschwan.eu/2020/06/06/making-koko-great-for-desktop-use/koko.png" alt="Featured image of post Making Koko Great for Desktop Use" /&gt;&lt;p&gt;Koko is a great image viewer designed for Plasma Mobile. Unfortunately, up until recently it did not work great on desktop computers. This changed last week with various small patches to Koko&amp;rsquo;s code and Kirigami, KDE&amp;rsquo;s framework for developing apps for the desktop and mobile devices which is what Koko is based on.&lt;/p&gt;
&lt;p&gt;First the sidebar color was updated to use the view colorset instead of the window colorset. This change was initially only done in Koko but, seeing that various Kirigami apps had this configuration too, I updated the default in Kirigami so that each app doesn&amp;rsquo;t have to change it again. (&lt;a class="link" href="https://invent.kde.org/frameworks/kirigami/-/merge_requests/15" target="_blank" rel="noopener"
&gt;Kirigami !15&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;I added a proper header to the sidebar describing what the sidebar does and also made a small change to the list header. Now, if all the actions list are expandable, the list header look is applied to them. This makes it more similar to the sidebar on System Settings. That said, more work is needed to achieve a consistent look. (&lt;a class="link" href="https://invent.kde.org/frameworks/kirigami/-/merge_requests/19" target="_blank" rel="noopener"
&gt;Kirigami !19&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;I added &amp;ldquo;next&amp;rdquo; and &amp;ldquo;previous&amp;rdquo; buttons on the desktop mode to the image viewer itself, as swipe gestures are harder to use on desktop apps. (&lt;a class="link" href="https://invent.kde.org/plasma-mobile/koko/-/merge_requests/3" target="_blank" rel="noopener"
&gt;Koko !3&lt;/a&gt;)
I also added text to the context buttons to make it clearer what the actions do. (&lt;a class="link" href="https://invent.kde.org/plasma-mobile/koko/-/merge_requests/2" target="_blank" rel="noopener"
&gt;Koko !2&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Another thing I improved was the animation for showing or hiding the thumbnail list: It doesn&amp;rsquo;t steal the scrollbar input anymore. (&lt;a class="link" href="https://invent.kde.org/plasma-mobile/koko/-/merge_requests/12" target="_blank" rel="noopener"
&gt;Koko !12&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;video src="https://carlschwan.eu/assets/img/koko-animation.mp4" autoplay muted&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;A more exciting feature that hasn&amp;rsquo;t landed yet is that I am including a simple image editor for upcoming versions. The image editor is developed as an external library that can be embedded into Koko and also into a future version of spectacle for plasma mobile, Pix, or QML messaging apps. It can also be included into messenger apps and possibly more types of apps. The API is not stable yet, so if you are interested in integrating a basic image editor into your qml app, please let me know! The code is available at &lt;a class="link" href="https://invent.kde.org/libraries/kquickimageeditor" target="_blank" rel="noopener"
&gt;invent.kde.org/libraries/kquickimageeditor&lt;/a&gt; and merge request and features suggestions are welcome.&lt;/p&gt;
&lt;p&gt;&lt;video src="https://carlschwan.eu/assets/img/koko-editor.mp4" autoplay muted&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Is Koko already a full replacement for Gwenview? Probably not or at least not for yet for everyone. But with a bit more love it could become a serious contender.&lt;/p&gt;</description></item><item><title>A new look for KDE.org</title><link>https://carlschwan.eu/2020/04/18/a-new-look-for-kde.org/</link><pubDate>Sat, 18 Apr 2020 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/2020/04/18/a-new-look-for-kde.org/</guid><description>&lt;img src="https://carlschwan.eu/2020/04/18/a-new-look-for-kde.org/kde-org-homepage.png" alt="Featured image of post A new look for KDE.org" /&gt;&lt;p&gt;Started a long time ago and only finished now, I have the pleasure to announce
the publication of a new homepage for the KDE community. I hope you will enjoy it
as much as I did creating it.&lt;/p&gt;
&lt;p&gt;This is only a small preview. Head over to &lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;kde.org&lt;/a&gt; to see
the complete webpage.&lt;/p&gt;
&lt;p&gt;First of all I have removed the carousel. Some studies show that a carousel is
a very bad way to convey information. See &amp;ldquo;&lt;a class="link" href="http://shouldiuseacarousel.com/" target="_blank" rel="noopener"
&gt;should I use a carousel&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Instead, I&amp;rsquo;ve created various sections about KDE. Plasma, the applications,
hardware that is shipped with Plasma, and, more importantly, the KDE Community.
This is just an overview, we have way more projects, but I hope it gives you a
good introduction of the work we, as the KDE Community, have been doing for more then 20
years. If you are interested, you can find all the different products we
create on the &lt;a class="link" href="https://kde.org/products" target="_blank" rel="noopener"
&gt;KDE products page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new webpage was not only my work, but a collaborative work and I want to
thank in no particular order Ilya Bizyaev, Niccolò Venerandi, Paul Brown, manueljlin,
David Cahalane, Blumen Herzenschein and others for their help proofreading,
suggesting changes and creating the artwork for the hardware section (thanks
again manueljlin).&lt;/p&gt;
&lt;h2 id="fun-fact"&gt;Fun fact&lt;/h2&gt;
&lt;p&gt;Thanks to David Cahalane, I learned that this is the 8th version of the kde.org homepage.
For those interested, I compiled a list of snapshots from archive.org showing
the evolution of the homepage.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/19980129141510/http://www.kde.org/index.html" target="_blank" rel="noopener"
&gt;29 January 1998&lt;/a&gt; It&amp;rsquo;s older than I am \o/&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20000303113442/http://www.kde.org/index.html" target="_blank" rel="noopener"
&gt;3 March 2000&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20010302060501/http://www.kde.org/" target="_blank" rel="noopener"
&gt;2 March 2001&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20030623221955/http://www.kde.org/" target="_blank" rel="noopener"
&gt;23 June 2003&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20090618162051/http://www.kde.org/" target="_blank" rel="noopener"
&gt;18 June 2009&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20130228134554/http://www.kde.org/" target="_blank" rel="noopener"
&gt;28 February 2013&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20130228134554/http://www.kde.org/" target="_blank" rel="noopener"
&gt;26 Jun 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;20 April 2020&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;video class="embed-responsive-item" src="https://carlschwan.eu/assets/kdeorg_thru_ages_fast.mp4" controls allowfullscreen&gt;&lt;/video&gt;
&lt;/div&gt;
&lt;h2 id="get-involved"&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;There is always more work needed in order to spread KDE all over the world. Creating graphics,
polishing websites, writing announcements are just some of the tasks you can help with. Join the KDE Web and KDE Promo group and
help us let the world know about KDE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KDE Web:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;#kde-www&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://webchat.kde.org/#/room/#freenode_#kde-www:matrix.org" target="_blank" rel="noopener"
&gt;#freenode_#kde-www:matrix.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://t.me/KDEWeb" target="_blank" rel="noopener"
&gt;Telegram&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;KDE Promo:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;#kde-promo&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://webchat.kde.org/#/room/#kde-promo:kde.org" target="_blank" rel="noopener"
&gt;#kde-promo:kde.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://t.me/joinchat/AEyx-0O8HKlHV7Cg7ZoSyA" target="_blank" rel="noopener"
&gt;Telegram&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>This month in KDE Web: March 2020</title><link>https://carlschwan.eu/2020/03/28/this-month-in-kde-web-march-2020/</link><pubDate>Sat, 28 Mar 2020 18:00:00 +0000</pubDate><guid>https://carlschwan.eu/2020/03/28/this-month-in-kde-web-march-2020/</guid><description>&lt;img src="https://carlschwan.eu/2020/03/28/this-month-in-kde-web-march-2020/aether-sphinx.png" alt="Featured image of post This month in KDE Web: March 2020" /&gt;&lt;p&gt;This month KDE web developers worked on updating more websites and some progress
was made in a new identity provider and a lot of other exiting stuff and a lot
of background work was also done.&lt;/p&gt;
&lt;h2 id="updated-websites"&gt;Updated Websites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://calligra.org" target="_blank" rel="noopener"
&gt;Calligra&lt;/a&gt;, the KDE office suite, launched a new website.
This was done during &lt;a class="link" href="https://season.kde.org" target="_blank" rel="noopener"
&gt;Season of KDE&lt;/a&gt; by Anuj Bansal. This
replaces the old Wordpress website. &lt;a class="link" href="https://invent.kde.org/websites/calligra-org" target="_blank" rel="noopener"
&gt;See repository.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/calligra.png" alt="Calligra website" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://planet.kde.org" target="_blank" rel="noopener"
&gt;Planet KDE&lt;/a&gt; is an aggregator of all the individual blogs
of KDE contributors. It is the place where you can find technical details about a
change, general KDE news, and blog posts about the life of KDE contributors. The website was
updated and now contains feeds in more languages. (Me: Carl Schwan,
&lt;a class="link" href="https://invent.kde.org/websites/planet-kde-org" target="_blank" rel="noopener"
&gt;See repository&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/planet.png" alt="Planet KDE Website" /&gt;&lt;/p&gt;
&lt;h2 id="progress-to-a-new-identity-service"&gt;Progress to a new identity service&lt;/h2&gt;
&lt;p&gt;KDE Identity was and still is a source of pain in the KDE infrastructure. It only
supports OpenLDAP, doesn&amp;rsquo;t provide a great onbording experience and isn&amp;rsquo;t very
flexible.&lt;/p&gt;
&lt;p&gt;A new account management service is currently in creation and should be based on a
fork of the successful &lt;a class="link" href="https://id.blender.org" target="_blank" rel="noopener"
&gt;Blender ID system&lt;/a&gt;. This new system
will be based on OAuth2.&lt;/p&gt;
&lt;p&gt;This month the homepage of the new identity service was updated to follow KDE branding (Me: Carl Schwan,
&lt;a class="link" href="https://invent.kde.org/websites/my-kde-org/-/merge_requests/7" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;)
and the settings are now using environment variables (Lays Rodrigues,
&lt;a class="link" href="https://invent.kde.org/websites/my-kde-org/-/merge_requests/4/diffs" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/accounts.png" alt="KDE identity" /&gt;&lt;/p&gt;
&lt;p&gt;But the most significant work was rewriting &lt;a class="link" href="https://season.kde.org" target="_blank" rel="noopener"
&gt;season.kde.org&lt;/a&gt;
and making it compatible with the new identity service. This is still in progress and
a few features are missing but everything should be finished soon. (Me: Carl Schwan,
&lt;a class="link" href="https://invent.kde.org/carlschwan/season-kde-org" target="_blank" rel="noopener"
&gt;See repository.&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/season.png" alt="Season Website" /&gt;&lt;/p&gt;
&lt;h2 id="kdeorg-changes"&gt;KDE.org changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a class="link" href="https://kde.org/hardware" target="_blank" rel="noopener"
&gt;hardware page&lt;/a&gt; created last month was updated
to include the Pinebook Pro (Niccolò Venerandi) and now includes hardware specifications
(Me: Carl Schwan).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/hardware.png" alt="hardware page" /&gt;&lt;/p&gt;
&lt;h2 id="library-updates"&gt;Library updates&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;An Aether theme was created for the Sphinx documention engine based on the Read
the Docs theme. This isn&amp;rsquo;t used yet for any website but in the future, it will
be used for &lt;a class="link" href="https://hig.kde.org" target="_blank" rel="noopener"
&gt;hig.kde.org&lt;/a&gt; and maybe others. Stay tuned!
(Carson Black, &lt;a class="link" href="invent.kde.org/websites/aether-sphinx" &gt;see repository&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/aether-sphinx.png" alt="Aether sphinx" /&gt;&lt;/p&gt;
&lt;h2 id="how-you-can-help"&gt;How you can help&lt;/h2&gt;
&lt;p&gt;We always need help with websites, fixing papercuts, upgrading old
websites to the new Jekyll/Hugo infrastructure, making sure information on
the websites are up-to-date, creating beautiful home page for your
favorite project and a lot more.&lt;/p&gt;
&lt;p&gt;You can join the web team through our &lt;a class="link" href="https://webchat.kde.org/#/room/#freenode_#kde-www:matrix.org" target="_blank" rel="noopener"
&gt;Matrix channel&lt;/a&gt;
, our IRC channel (#kde-www) or our &lt;a class="link" href="https://t.me/KDEWeb" target="_blank" rel="noopener"
&gt;Telegram channel&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Why are most KDE websites using the same theme?</title><link>https://carlschwan.eu/2020/03/25/why-are-most-kde-websites-using-the-same-theme/</link><pubDate>Wed, 25 Mar 2020 12:50:00 +0000</pubDate><guid>https://carlschwan.eu/2020/03/25/why-are-most-kde-websites-using-the-same-theme/</guid><description>&lt;h2 id="a-unified-theme-why-why-not"&gt;A unified theme? Why? Why not?&lt;/h2&gt;
&lt;p&gt;Nearly all KDE websites use a unified theme across the board. This is part of the consistency goal, chosen as a KDE goal at the last Akademy in Milano (Italy).&lt;/p&gt;
&lt;p&gt;Using a unified theme has multiple advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It strengthens KDE&amp;rsquo;s brand, since websites are more visible than applications or projects part of the KDE community. Websites with a unified look help visitors immediately identify an application or subproject as being part of a bigger organization.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It is easier to maintain only one theme and the tooling behind it than a theme for each project. We all wish Free and Open Source projects had unlimited budgets and manpower, but that is not realistic and maintaining a theme is not an easy task.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This doesn&amp;rsquo;t mean the KDE theme should be used for every KDE project, in fact, there are legitimate reasons not to. Sometimes an application under the KDE umbrella wants to create their own brand and does have the manpower and talent to design their own website. &lt;a class="link" href="https://krita.org" target="_blank" rel="noopener"
&gt;Krita&lt;/a&gt; is a good example here. The team has built a beautiful website that does not use the unified theme.&lt;/p&gt;
&lt;h2 id="what-theme-does-kde-use"&gt;What theme does KDE use?&lt;/h2&gt;
&lt;p&gt;KDE is using the Aether theme. This is a theme designed and initially developed by &lt;a class="link" href="https://kver.wordpress.com" target="_blank" rel="noopener"
&gt;Ken Vermette&lt;/a&gt;, the talented artist that is also behind most of the Plasma wallpapers and some interesting design concept like &lt;a class="link" href="https://kver.wordpress.com/2014/10/25/presenting-dwd-a-candidate-for-kde-window-decorations/" target="_blank" rel="noopener"
&gt;DWD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This theme was originally based on one of the first Bootstrap 4 alpha version and later rebased on a stable Bootstrap 4 version. Using Bootstrap has its advantages and disadvantages.&lt;/p&gt;
&lt;p&gt;The biggest advantage is that it has a large community and a lot of bootstrap themes exist for CMS and static site generators. It can be easily adapted to your specific needs without starting from scratch every time.&lt;/p&gt;
&lt;p&gt;Another advantage is that Bootstrap is built using SASS and is designed to be extendable with tons of variables a developer can modify to globally change colors, layouts and a lot more. You can also specify the modules you want to use, and add your self-made components. For those interested in extending a Bootstrap theme, the &lt;a class="link" href="https://getbootstrap.com/docs/4.4/getting-started/theming/" target="_blank" rel="noopener"
&gt;official documentation&lt;/a&gt; is a great start. These capabilities were sadly not used when creating the Aether theme, but we are slowly moving to use more of the Bootstrap theming capabilities over time.&lt;/p&gt;
&lt;p&gt;The problem with Boostrap is that, because it is so popular, in its default from it looks like a generic website without any personal identity. Changing only the colors won&amp;rsquo;t help to make your website more unique.&lt;/p&gt;
&lt;h2 id="building-the-websites"&gt;Building the websites&lt;/h2&gt;
&lt;p&gt;In KDE we use a lot of different web frameworks for our websites. Most of the applications websites are now using Jekyll or Hugo, but some are also using Wordpress, Drupal 7 and Drupal 8.&lt;/p&gt;
&lt;p&gt;Jekyll has first-class support for creating a shared theme, so we created a Jekyll theme for KDE using the Aether theme that offers a lot of options. Here are two examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Should it display a Made By KDE logo? This is useful for application websites but shouldn&amp;rsquo;t appear on, for example, the KDE e.V. website.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Should it display a KDE logo or the application logo?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This allows for small customizations with only a few changes for each website without having to maintain different versions.&lt;/p&gt;
&lt;p&gt;Some websites are using Hugo instead of Jekyll. The reason for this is that Hugo is faster when generating the webpages and has more advanced internationalization features included by default. The internationalization features are used for &lt;a class="link" href="https://kate-editor.org" target="_blank" rel="noopener"
&gt;kate-editor.org&lt;/a&gt; and the &lt;a class="link" href="https://kde.org/announcements/releases" target="_blank" rel="noopener"
&gt;monthly application release announcements&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For my WIP replacement for the &lt;a class="link" href="https://dot.kde.org" target="_blank" rel="noopener"
&gt;KDE Dot&lt;/a&gt;, I needed the performance of Hugo. With Jekyll, I gave up generating the website after 10 minutes, but with Hugo, it only took a few seconds to generate.&lt;/p&gt;
&lt;p&gt;In the case of Content Management Systems, creating a theme is also well-supported but it isn&amp;rsquo;t as
pleasant to do. I have a limited experience with Wordpress, but creating a theme was still possible and not too complicated. In the case of Drupal 7, creating a theme from scratch is almost impossible. It requires messing with a lot of PHP arrays and the security responsibility is on the side of the theme developer.&lt;/p&gt;
&lt;h2 id="deployment-of-the-theme"&gt;Deployment of the theme&lt;/h2&gt;
&lt;p&gt;To deploy the theme, I use &lt;a class="link" href="https://symfony.com/doc/current/frontend.html" target="_blank" rel="noopener"
&gt;Symfony Encore&lt;/a&gt;. Sympfony Encore is a &lt;a class="link" href="https://webpack.js.org/" target="_blank" rel="noopener"
&gt;webpack&lt;/a&gt; wrapper. I chose Symfony Encore because I&amp;rsquo;m very familiar with its PHP framework and I have been working with it since I was in high school (using Webpack alone would have been completely fine too).&lt;/p&gt;
&lt;p&gt;Webpack is used to compile and optimize the SCSS and ECMAScript (aka JavaScript). After each commit to the master branch, a CI job compiles and deploys the compiled CSS and ECMAScript to a CDN host and the hosted CDN asset is then used by all the new KDE websites. This means that my changes are immediately propagated to all websites&amp;hellip; or so I thought initially. In reality the CSS and JS files are cached for a long time and often visitors get an old version.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m solving this problem in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For dynamic websites: I also generate a versioned version of the assets and resolve the version in the backend. This is what I used in the
&lt;a class="link" href="https://invent.kde.org/websites/aether-mediawiki/-/blob/master/SkinAether.php" target="_blank" rel="noopener"
&gt;MediaWiki plugin&lt;/a&gt;, for example.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For static websites: In this case, I can&amp;rsquo;t dynamically resolve the last version in the backend, so instead I add a GET parameter to the URL (e.g. main.css?v=10) and increase the number each time I fix a bug. This doesn&amp;rsquo;t happen often, so it is still easy to manage.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-you-can-help"&gt;How you can help&lt;/h2&gt;
&lt;p&gt;We always need help with the websites, fixing papercuts, upgrading old websites to the new Jekyll/Hugo infrastructure, making sure information on the website is up-to-date, creating new beautiful home pages for your favorite projects and a lot more.&lt;/p&gt;
&lt;p&gt;The Elisa maintainers are still looking for someone who wants to create a website and we created a &lt;a class="link" href="https://phabricator.kde.org/T12726" target="_blank" rel="noopener"
&gt;junior job task&lt;/a&gt; for this. Maybe that someone could be you?&lt;/p&gt;
&lt;p&gt;You can also join the web team through our &lt;a class="link" href="https://webchat.kde.org/#/room/#freenode_#kde-www:matrix.org" target="_blank" rel="noopener"
&gt;Matrix channel&lt;/a&gt;, our IRC channel (#kde-www) or our &lt;a class="link" href="https://t.me/KDEWeb" target="_blank" rel="noopener"
&gt;Telegram channel&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>This month in KDE Web: January-February 2020</title><link>https://carlschwan.eu/2020/02/29/this-month-in-kde-web-january-february-2020/</link><pubDate>Sat, 29 Feb 2020 18:00:00 +0000</pubDate><guid>https://carlschwan.eu/2020/02/29/this-month-in-kde-web-january-february-2020/</guid><description>&lt;p&gt;This is the first post in a monthly series about improvements to the KDE
websites. I plan to publish it every last Saturday of the month. Since a
lot happened in January and I didn&amp;rsquo;t mention it anywhere, I will also
mention those things in this post.&lt;/p&gt;
&lt;h2 id="new-websites"&gt;New Websites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kdeconnect.kde.org" target="_blank" rel="noopener"
&gt;KDE Connect&lt;/a&gt; gained a new website
and a promotional video. This was done during the &lt;strong&gt;Season of KDE&lt;/strong&gt; project.
(Arjun Thekoot Harisankar, &lt;a class="link" href="invent.kde.org/websites/kdeconnect-kde-org" &gt;see repository&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/kde-connect-web.png" alt="KDE Connect website" /&gt;&lt;/p&gt;
&lt;h2 id="updated-websites"&gt;Updated Websites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://juk.kde.org" target="_blank" rel="noopener"
&gt;Juk&lt;/a&gt; is a music player and music manager. A new website for it
was created by Anuj Bansal, (&lt;a class="link" href="https://cgit.kde.org/websites/juk-kde-org.git/commit/?id=397eb218cdcad9c14e912bd7262f81a3dcc6f638" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/juk-web.png" alt="Juk website" /&gt;{:.img-border}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://ev.kde.org" target="_blank" rel="noopener"
&gt;KDE e.V.&lt;/a&gt; is the non-profit organization that represents
the KDE community in legal and financial matters. The e.V. website was migrated
to Jekyll and now uses the shared &lt;a class="link" href="https://invent.kde.org/websites/jekyll-jde-theme" target="_blank" rel="noopener"
&gt;Jekyll theme&lt;/a&gt;.
(Me: Carl Schwan, &lt;a class="link" href="https://cgit.kde.org/websites/ev-kde-org.git/log/" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/ev-web.png" alt="KDE e.V. website" /&gt;{:.img-border}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kmymoney.org" target="_blank" rel="noopener"
&gt;KMyMoney&lt;/a&gt; is a personal finance manager and the project
website was also migrated to Jekyll and now uses the shared Jekyll theme (Me:
Carl Schwan, &lt;a class="link" href="https://cgit.kde.org/websites/kmymoney-org.git/commit/?id=a27d3784f92806fd04c6d6968c782b27ed042bac" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/kmymoney-web.png" alt="KMyMoney website" /&gt;{:.img-border}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kde-china.org" target="_blank" rel="noopener"
&gt;kde-china.org&lt;/a&gt; is now using the Jekyll theme and is
hosted in the KDE infrastructure (Yunhe Guo, &lt;a class="link" href="https://invent.kde.org/websites/kde-china-org" target="_blank" rel="noopener"
&gt;see repository&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/kde-china-web.png" alt="KDE chinal website" /&gt;{:.img-border}&lt;/p&gt;
&lt;h2 id="updates-to-kdeorg"&gt;Updates to kde.org&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A &lt;a class="link" href="https://kde.org/hardware" target="_blank" rel="noopener"
&gt;hardware page&lt;/a&gt; was created listing all the
devices that ship with Plasma preinstalled (Niccolò Venerandi, &lt;a class="link" href="https://phabricator.kde.org/R883:1561070" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Updated image for the recently launched Plasma 5.18 (Jonathan Riddell and Me: Carl
Schwan).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reduce KDE patron logo size for kde.org (Ilya Bizyaev, &lt;a class="link" href="https://phabricator.kde.org/T11878" target="_blank" rel="noopener"
&gt;see task&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="library-updates"&gt;Library updates&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Added options to customize footer in the Jekyll theme. This will be helpful for
the coming update to &lt;a class="link" href="https://jp.kde.org" target="_blank" rel="noopener"
&gt;jp.kde.org&lt;/a&gt; (Jumpei Ogawa,
&lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme/commit/8c7432271a9063349e7f1f0c0681d225e71fd2c5" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Added option to disabled the donation form in the Jekyll theme. (Jumpei Ogawa,
&lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme/commit/ed17dfd74a9f31c53012f08a26ceb04e2e8fe444" target="_blank" rel="noopener"
&gt;see commit&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Various minor fixes to the &lt;a class="link" href="https://invent.kde.org/websites/aether-sass" target="_blank" rel="noopener"
&gt;Aether css theme&lt;/a&gt;
(Anuj Bansal and Me: Carl Schwan).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A component selector for the Jekyll theme was also added. This will be helpful
for making kontact.kde.org use the KDE Jekyll theme and not a custom theme and for
others projects who have multiples components (e.g. Calligra). (Anuj Bansal,
&lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme/merge_requests/13" target="_blank" rel="noopener"
&gt;see merge request&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Improve translations infrastructure for &lt;a class="link" href="https://kate-editor.org" target="_blank" rel="noopener"
&gt;kate-editor.org&lt;/a&gt;
(Me: Carl Schwan,
&lt;a class="link" href="https://invent.kde.org/websites/kate-editor-org/-/commit/9bd5b9eda7adfad0690eb485b935ba7ae6a5537c" target="_blank" rel="noopener"
&gt;see commits&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-you-can-help"&gt;How you can help&lt;/h2&gt;
&lt;p&gt;We always need help with website, fixing papercuts, upgrading old
websites to the new Jekyll/Hugo infrastructure, making sure information on
the website is up-to-date, creating beautiful home page for your
favorite project and a lot more.&lt;/p&gt;
&lt;p&gt;The Elisa maintainers are looking for someone who wants to create a website and
a &lt;a class="link" href="https://phabricator.kde.org/T12726" target="_blank" rel="noopener"
&gt;junior job task was created&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can join the web team through our &lt;a class="link" href="https://webchat.kde.org/#/room/#freenode_#kde-www:matrix.org" target="_blank" rel="noopener"
&gt;Matrix channel&lt;/a&gt;
, our IRC channel (#kde-www) or our &lt;a class="link" href="https://t.me/KDEWeb" target="_blank" rel="noopener"
&gt;Telegram channel&lt;/a&gt;.&lt;/p&gt;
&lt;!--
Post about this next month after deployment
## Comming soon
* A new website for the Calligra project was developed during SoK by Anuj
Bansal. It still needs to be reviewed by the Calligra maintainers before
landing it.
* A new website for the Umbrello project was developed during SoK by Akshay
Nair. It also needs to be reviewed by the Umbrello maintainers before landing
it.
--&gt;</description></item><item><title>Bugzilla integration in the KDE wikis</title><link>https://carlschwan.eu/2019/11/23/bugzilla-integration-in-the-kde-wikis/</link><pubDate>Sat, 23 Nov 2019 18:20:35 +0000</pubDate><guid>https://carlschwan.eu/2019/11/23/bugzilla-integration-in-the-kde-wikis/</guid><description>&lt;p&gt;The Bugzilla integration in the KDE wikis is now usable again.&lt;/p&gt;
&lt;p&gt;KDE wikis were using a long time ago the Bugzilla integration extension
developed by Mozilla. This was used for example by some KDE components to keep track
of the feature planned for a new release.&lt;/p&gt;
&lt;p&gt;Sadly this extension wasn&amp;rsquo;t keep up to date and was not compatible with recent
MediaWiki extension.&lt;/p&gt;
&lt;p&gt;So I developed a &lt;a class="link" href="https://invent.kde.org/websites/mediawiki-bugzilla" target="_blank" rel="noopener"
&gt;new extension&lt;/a&gt;.
This doesn&amp;rsquo;t have all the features of the old extension but all the one needed for KDE.&lt;/p&gt;
&lt;p&gt;The extension can be see in use at &lt;a class="link" href="https://community.kde.org/Schedules/Applications/19.12_Feature_Plan" target="_blank" rel="noopener"
&gt;community.kde.org/Schedules/Applications/19.12_Feature_Plan&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;bugzilla&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;TODO&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;table-bugs-todo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;product&amp;#34;: &amp;#34;umbrello&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;status&amp;#34; : &amp;#34;CONFIRMED&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;severity&amp;#34; : &amp;#34;wishlist&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;f2&amp;#34;: &amp;#34;target_milestone&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;o2&amp;#34;: &amp;#34;substring&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;v2&amp;#34;: &amp;#34;19.12&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;include_fields&amp;#34; : [ &amp;#34;id&amp;#34;, &amp;#34;product&amp;#34;, &amp;#34;summary&amp;#34;, &amp;#34;assigned_to&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;bugzilla&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this should display a list of all open which list for the milestone 19.12.&lt;/p&gt;
&lt;p&gt;Enjoy :)&lt;/p&gt;</description></item><item><title>Happy Halloween - Dark theme for more KDE website</title><link>https://carlschwan.eu/2019/10/31/happy-halloween-dark-theme-for-more-kde-website/</link><pubDate>Thu, 31 Oct 2019 16:20:35 +0000</pubDate><guid>https://carlschwan.eu/2019/10/31/happy-halloween-dark-theme-for-more-kde-website/</guid><description>&lt;p&gt;Halloween was the perfect occasion for me to hack together a dark theme for
more KDE websites. So now a dark theme version is also available for
&lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;kde.org&lt;/a&gt; and &lt;a class="link" href="https://planet.kde.org" target="_blank" rel="noopener"
&gt;planetkde&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It is using &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt; media query so it&amp;rsquo;s only available
if you browser prefers the dark theme version.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/kde-org-dark.png" alt="KDE.ORG dark theme" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/planetkde-dark.png" alt="Planet kde dark theme" /&gt;&lt;/p&gt;</description></item><item><title>List stores in kde.org/applications</title><link>https://carlschwan.eu/2019/10/17/list-stores-in-kde.org/applications/</link><pubDate>Thu, 17 Oct 2019 20:35:35 +0000</pubDate><guid>https://carlschwan.eu/2019/10/17/list-stores-in-kde.org/applications/</guid><description>&lt;p&gt;Announcing a small update for the goal: KDE is All About the Apps.
&lt;a class="link" href="https://kde.org/applications" target="_blank" rel="noopener"
&gt;kde.org/applications&lt;/a&gt; is now listing
the stores where the application is available. For the moment, it&amp;rsquo;s only
listing Linux and the Windows Store, but support for F-Droid and the
Play Store is planned. Stay tuned!&lt;/p&gt;
&lt;img alt="Store listing" src="https://carlschwan.eu/assets/img/store-listing.png" style="border: 1px solid black; max-width: 100%"/&gt;</description></item><item><title>Windows download link in kde.org/applications</title><link>https://carlschwan.eu/2019/10/03/windows-download-link-in-kde.org/applications/</link><pubDate>Thu, 03 Oct 2019 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2019/10/03/windows-download-link-in-kde.org/applications/</guid><description>&lt;p&gt;Some quick news about a new feature of kde.org/applications! With the amazing work
done by Hannah von Reth and Christoph Cullmann, we now have more applications
in the Windows Store.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://cullmann.io/posts/kde-applications-on-windows/" target="_blank" rel="noopener"
&gt;KDE applications on Windows&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I updated &lt;a class="link" href="https://kde.org/applications" target="_blank" rel="noopener"
&gt;kde.org/applications&lt;/a&gt; to display this
important information. Now if you are browsing for example the
&lt;a class="link" href="https://kde.org/applications/utilities/org.kde.kate" target="_blank" rel="noopener"
&gt;Kate application page&lt;/a&gt; using Windows,
you should see a button &amp;ldquo;Install on Windows&amp;rdquo; instead of &amp;ldquo;Install of Linux&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/kde-application-windows.png" alt="Install on Windows button" /&gt;&lt;/p&gt;
&lt;p&gt;Since the generation of this website use the AppStream standard and this standard
allow to add some &lt;a class="link" href="https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-custom" target="_blank" rel="noopener"
&gt;custom fields&lt;/a&gt;,
I added a vendor prefixed field to it: &lt;code&gt;KDE::windows_store&lt;/code&gt;. Et voilà, the metadata
is available to the php code.&lt;/p&gt;
&lt;p&gt;The next task is to add a list at the bottom of the supported platform, in case the operating
system detection doesn&amp;rsquo;t work. This will let the visitor know about all the supported
platforms.&lt;/p&gt;
&lt;p&gt;If someone is interested in doing this task, please contact me on irc/matrix #kde-www
and username &amp;lsquo;Carl Schwan/CarlSchwan[m]&amp;rsquo;. The repository is available in the new
KDE Gitlab instance: &lt;a class="link" href="https://invent.kde.org/websites/kde-org-applications/" target="_blank" rel="noopener"
&gt;invent.kde.org/websites/kde-org-applications/&lt;/a&gt;.&lt;/p&gt;
&lt;style&gt;
.post-content img { text-align: center; }
&lt;/style&gt;</description></item><item><title>New webpage for Plasma Desktop</title><link>https://carlschwan.eu/2019/09/16/new-webpage-for-plasma-desktop/</link><pubDate>Mon, 16 Sep 2019 09:35:35 +0000</pubDate><guid>https://carlschwan.eu/2019/09/16/new-webpage-for-plasma-desktop/</guid><description>&lt;p&gt;In my quest to improve the website of KDE, I updated the &lt;a class="link" href="https://kde.org/plasma-desktop" target="_blank" rel="noopener"
&gt;Plasma Desktop webpage&lt;/a&gt;.
This is a huge improvement to the old website, which didn&amp;rsquo;t show any screenshots
and didn&amp;rsquo;t list any Plasma features.&lt;/p&gt;
&lt;p&gt;I already teased the improvements I made in the Plasma BoF in Milan to the Akademy.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/plasma-bof-webpage.png" alt="Me (Carl Schwan) at the Plasma BoF showing the new Plasma Desktop webpage" /&gt;&lt;/p&gt;
&lt;p&gt;The redesign got a lot of positive feedback by the Plasma team and after some small
modifications the changes landed.&lt;/p&gt;
&lt;p&gt;The webpage looks like this now:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/plasma-desktop-webpage.png" alt="Plasma desktop webpage" /&gt;&lt;/p&gt;
&lt;p&gt;Thanks to all the people from the &lt;a class="link" href="https://community.kde.org/Promo" target="_blank" rel="noopener"
&gt;Promo team&lt;/a&gt; and
&lt;a class="link" href="https://less.re/profile/vinzv" target="_blank" rel="noopener"
&gt;vinz&lt;/a&gt; who helped me write the text and give me some ideas.&lt;/p&gt;
&lt;h2 id="improving-the-kde-websites-junior-jobs"&gt;Improving the KDE websites: Junior Jobs&lt;/h2&gt;
&lt;p&gt;If you want to help improving the web presence of KDE, I regularly add some Junior Job to this
&lt;a class="link" href="https://phabricator.kde.org/tag/websites/" target="_blank" rel="noopener"
&gt;Phabricator Workboard&lt;/a&gt;. Lots of things need to be
updated, so don&amp;rsquo;t hesitate to propose other changes in the
&lt;a class="link" href="https://mail.kde.org/mailman/listinfo/kde-www" target="_blank" rel="noopener"
&gt;kde-www mailing list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Discussion: &lt;a class="link" href="https://www.reddit.com/r/kde/comments/d50h6a/new_plasma_desktop_webpage_showcases_its_favorite/" target="_blank" rel="noopener"
&gt;Reddit&lt;/a&gt; or
&lt;a class="link" href="https://linuxrocks.online/@carl/102802358970158871" target="_blank" rel="noopener"
&gt;Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;style&gt;
img, video {
max-width: 100%;
margin-left: auto;
margin-right: auto;
display: block;
}
&lt;/style&gt;</description></item><item><title>KDE websites infrastructure update and new websites</title><link>https://carlschwan.eu/2019/08/29/kde-websites-infrastructure-update-and-new-websites/</link><pubDate>Thu, 29 Aug 2019 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2019/08/29/kde-websites-infrastructure-update-and-new-websites/</guid><description>&lt;p&gt;Since my latest &lt;a class="link" href="https://carlschwan.eu/2019/07/12/new-userbase.html" &gt;post&lt;/a&gt; two months ago,
a lot of things changed regarding the KDE websites. More and more KDE websites
are switching to the Aether theme designed by &lt;a class="link" href="https://kver.wordpress.com/" target="_blank" rel="noopener"
&gt;Ken Vermette&lt;/a&gt;.
You can follow the progress at the &lt;a class="link" href="https://phabricator.kde.org/T10827" target="_blank" rel="noopener"
&gt;Phabricator task T10827&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="new-websites"&gt;New websites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;All three wikis were updated to MediaWiki 1.31 and are now using the
&lt;a class="link" href="https://invent.kde.org/websites/aether-mediawiki/" target="_blank" rel="noopener"
&gt;Aether MediaWiki theme&lt;/a&gt;. There are some
visual glitches when using the dark theme version that can still be observed. They are usually very
simple to fix, so please report them in &lt;a class="link" href="https://bugs.kde.org/enter_bug.cgi?product=KDE%20MediaWiki" target="_blank" rel="noopener"
&gt;bugs.kde.org&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/wikiupdate.svg" alt="All 3 kde wiki" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The French speaking KDE websites was also updated to use the
&lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme" target="_blank" rel="noopener"
&gt;Aether Jekyll theme&lt;/a&gt;. It&amp;rsquo;s only an aesthetic
change and some parts of the content still need to be updated. If you speak French and want to
help, contact the French speaking KDE community in #kde-fr (IRC/Matrix) or
&lt;a class="link" href="https://mail.kde.org/mailman/listinfo/kde-francophone" target="_blank" rel="noopener"
&gt;via email&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/fr.kde.org.png" alt="Screenshot fr.kde.org" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://choqok.kde.org" target="_blank" rel="noopener"
&gt;Choqok&lt;/a&gt; also got a new website.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/choqok.kde.org.png" alt="Screenshot choqok.kde.org" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a class="link" href="https://wiki.kde.org" target="_blank" rel="noopener"
&gt;wiki explanation page&lt;/a&gt; was also
&lt;a class="link" href="https://cgit.kde.org/websites/wiki-kde-org.git/commit/?id=1292cff44980b39417f4b51b6d9830bf3333dea7" target="_blank" rel="noopener"
&gt;updated&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="behind-the-scenes"&gt;Behind the scenes&lt;/h2&gt;
&lt;h3 id="using-a-single-codebase-for-the-css-theming"&gt;Using a single codebase for the CSS theming&lt;/h3&gt;
&lt;p&gt;One of the big problems encountered was the multiplication of different versions of
the CSS files. There is a CCS file used by &lt;a class="link" href="https://konsole.kde.org" target="_blank" rel="noopener"
&gt;konsole.kde.org&lt;/a&gt;
and &lt;a class="link" href="https://choqok.kde.org" target="_blank" rel="noopener"
&gt;choqok.kde.org&lt;/a&gt;, one for all the MediaWiki
instances, and one for &lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;kde.org&lt;/a&gt;. This was getting harder
and harder to maintain, so I decided to create a single SASS codebase for all
the KDE websites.&lt;/p&gt;
&lt;p&gt;The code is located in the &lt;a class="link" href="https://invent.kde.org/websites/aether-sass" target="_blank" rel="noopener"
&gt;KDE Gitlab&lt;/a&gt;
instance and uses &lt;a class="link" href="https://symfony.com/doc/current/frontend.html" target="_blank" rel="noopener"
&gt;Symfony Encore&lt;/a&gt; to
generate all the CSS files from the SASS codebase.&lt;/p&gt;
&lt;p&gt;For the moment, the CSS code is only split into multiple SASS modules and the
tooling builds multiple versions using some generic components (breeze buttons) and other
more specific components (MediaWiki dark theme).&lt;/p&gt;
&lt;p&gt;Compiling the SASS files to CSS is done using
&lt;a class="link" href="https://invent.kde.org/sysadmin/binary-factory-tooling/blob/master/staticweb/pipeline-templates/symfonyencore.pipeline" target="_blank" rel="noopener"
&gt;the KDE Binary Factory&lt;/a&gt;
and produces two versions of each file, one with versioning (e.g. boostrap.hi8yh2huh.css)
which is intended for all the dynamics websites (MediaWiki, kde.org, &amp;hellip;) and another one without
versioning for the static websites (Hugo and Jekyll).&lt;/p&gt;
&lt;h3 id="using-versioned-assets-in-a-mediawiki-skin"&gt;Using versioned assets in a MediaWiki skin&lt;/h3&gt;
&lt;p&gt;MediaWiki uses ResourceLoader to load assets, but in our case we want to use the Webpack
generated &lt;code&gt;manifest.json&lt;/code&gt; to load the versioned file. For this, I&amp;rsquo;m using a Symfony
component &lt;a class="link" href="https://symfony.com/doc/current/components/asset.html" target="_blank" rel="noopener"
&gt;asset&lt;/a&gt; that is doing
most of the job for me.&lt;/p&gt;
&lt;p&gt;Here is the code used:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$urlPackage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;UrlPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;https://cdn.kde.org/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;JsonManifestVersionStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://cdn.kde.org/aether-devel/version/manifest.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;addStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$urlPackage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aether-devel/version/bootstrap.css&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;all&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I actually created a fork of the JsonManifestVersionStrategy class because of a &lt;a class="link" href="https://github.com/symfony/symfony/issues/33001" target="_blank" rel="noopener"
&gt;bug&lt;/a&gt;
in the library. I hope my change will be merged upstream.&lt;/p&gt;
&lt;h3 id="new-features-in-the-jekyll-theme"&gt;New features in the Jekyll theme&lt;/h3&gt;
&lt;p&gt;The Jekyll theme got some nice new features added by various contributors.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eike Hein added support for &lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme/merge_requests/1" target="_blank" rel="noopener"
&gt;meta tags allowing some basic SEO from page metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Jumpei Ogawa added support for &lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme/merge_requests/3" target="_blank" rel="noopener"
&gt;a custom navigation menu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I added a &lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme/merge_requests/4" target="_blank" rel="noopener"
&gt;blog component&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And special thanks to Mark Winter for fixing a small typo.&lt;/p&gt;
&lt;h2 id="junior-job"&gt;Junior Job&lt;/h2&gt;
&lt;p&gt;There are multiple other websites in the pipeline and any help is welcome.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://phabricator.kde.org/T11404" target="_blank" rel="noopener"
&gt;Update RKward website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://phabricator.kde.org/T11403" target="_blank" rel="noopener"
&gt;Update KMyMoney website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://phabricator.kde.org/T11466" target="_blank" rel="noopener"
&gt;Update Umbrello website&lt;/a&gt; I already started it, but if
someone wants to take care of it, they are welcome.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://phabricator.kde.org/T11243" target="_blank" rel="noopener"
&gt;Update kde.org/plasma-desktop&lt;/a&gt; There are still some
screenshots and videos missing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are interested in one of this tasks, join us in &lt;a class="link" href="https://community.kde.org/Matrix" target="_blank" rel="noopener"
&gt;#kde-www&lt;/a&gt; in IRC/Matrix.&lt;/p&gt;
&lt;h2 id="websites-bof-at-akademy"&gt;Websites Bof at Akademy&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m going to Akademy and I will organize a BoF about the websites. It will take place
Tuesday, 10th September 2019 in room U2-08b, just after the Plasma BoFs.&lt;/p&gt;
&lt;p&gt;If you are interested and want to attend, you can put yourself in the list
&lt;a class="link" href="https://phabricator.kde.org/T11423" target="_blank" rel="noopener"
&gt;T11423&lt;/a&gt;.&lt;/p&gt;
&lt;style&gt;
.post-content img {border: 1px #808080 solid; }
&lt;/style&gt;</description></item><item><title>The new userbase wiki</title><link>https://carlschwan.eu/2019/07/12/the-new-userbase-wiki/</link><pubDate>Fri, 12 Jul 2019 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2019/07/12/the-new-userbase-wiki/</guid><description>&lt;p&gt;I&amp;rsquo;m happy to announce that the userbase wiki is getting a new theme and an updated
MediaWiki version.&lt;/p&gt;
&lt;h2 id="new-theme---aether"&gt;New theme - Aether&lt;/h2&gt;
&lt;p&gt;The old userbase theme was called Neverland and looked a bit antiquated. A new theme
was created with a similar look to &lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;kde.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new theme features a light and dark modes using the new
&lt;code&gt;prefers-color-scheme: dark&lt;/code&gt; CSS media query. The new theme is also mobile friendly.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/mediawiki-dark.png" alt="mediawiki old" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/mediawiki-default.png" alt="mediawiki old" /&gt;&lt;/p&gt;
&lt;p&gt;I think this is quite an improvement over this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/mediawiki-old.png" alt="mediawiki old" /&gt;&lt;/p&gt;
&lt;p&gt;I am confident that &lt;a class="link" href="https://userbase.kde.org/User:Claus_chr" target="_blank" rel="noopener"
&gt;Claus_Chr&lt;/a&gt; and me found
most of the visual glitches, but if you do find a glitch, please report it to me on my
&lt;a class="link" href="https://userbase.kde.org/User_talk:Ognarb" target="_blank" rel="noopener"
&gt;talk page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new theme is hosted in KDE
&lt;a class="link" href="https://invent.kde.org/websites/aether-mediawiki" target="_blank" rel="noopener"
&gt;gitlab instance&lt;/a&gt;. Contributions
are welcome.&lt;/p&gt;
&lt;h2 id="new-mediawiki-version"&gt;New MediaWiki version&lt;/h2&gt;
&lt;p&gt;We jumped MediaWiki from the obsolete version 1.26 to 1.31, the latest LTS version. This
should fix some of the long-standing bugs and allow us to get all security updates with
minimal maintenance needs.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next?&lt;/h2&gt;
&lt;p&gt;A similar update for the community and techbase wikis should be comming soon™. The only
thing that we still need to work is an update of the configuration files and some testing
to make sure nothing broke during the update. A preview version of the community wiki can
already be tested at: &lt;a class="link" href="https://wikisandbox.kde.org" target="_blank" rel="noopener"
&gt;wikisandbox.kde.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="contribute-to-userbase"&gt;Contribute to Userbase&lt;/h2&gt;
&lt;p&gt;When you find a kool feature in KDE software, you can write a small tutorial or just
a small paragraph about it and the &lt;a class="link" href="https://userbase.kde.org" target="_blank" rel="noopener"
&gt;KDE Userbase Wiki&lt;/a&gt; is
the right place to publish it. You don&amp;rsquo;t need to know how to code, have perfect English
or know how MediaWiki&amp;rsquo;s formatting work, to contribute. We also need translators.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/i-want-you-userbase.png" alt="I want you for userbase" /&gt;&lt;/p&gt;
&lt;p&gt;Admire my GIMP skills ;)&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a class="link" href="https://gitlab.com/ognarb/blog/merge_requests/2" target="_blank" rel="noopener"
&gt;Blumen Herzenschein&lt;/a&gt; and
&lt;a class="link" href="https://gitlab.com/ognarb/blog/merge_requests/3" target="_blank" rel="noopener"
&gt;Paul Brown&lt;/a&gt; for proofreading this blog
post and to &lt;a class="link" href="https://phabricator.kde.org/p/bcooksley/" target="_blank" rel="noopener"
&gt;Ben Cooksley&lt;/a&gt; for pointing me to
the right direction.&lt;/p&gt;
&lt;p&gt;Discussion: &lt;a class="link" href="https://www.reddit.com/r/kde/comments/ccnogd/the_new_userbase_wiki/" target="_blank" rel="noopener"
&gt;Reddit&lt;/a&gt; or
&lt;a class="link" href="https://linuxrocks.online/@carl/102433547285904922" target="_blank" rel="noopener"
&gt;Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;style&gt;
img, video {
max-width: 100%;
margin-left: auto;
margin-right: auto;
display: block;
}
&lt;/style&gt;</description></item><item><title>New website for Konsole</title><link>https://carlschwan.eu/2019/06/23/new-website-for-konsole/</link><pubDate>Sun, 23 Jun 2019 11:00:00 +0000</pubDate><guid>https://carlschwan.eu/2019/06/23/new-website-for-konsole/</guid><description>&lt;p&gt;Yesterday, &lt;a class="link" href="https://konsole.kde.org" target="_blank" rel="noopener"
&gt;konsole.kde.org&lt;/a&gt; got a new website.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/new-konsole.png" alt="Screenshot of the new konsole website" /&gt;&lt;/p&gt;
&lt;p&gt;Doesn&amp;rsquo;t it look nice? As a reminder the old website looked like this.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/old-konsole.png" alt="Screenshot of the old konsole website" /&gt;&lt;/p&gt;
&lt;p&gt;The design is very similar to the &lt;a class="link" href="https://kontact.kde.org" target="_blank" rel="noopener"
&gt;kontact.kde.org&lt;/a&gt;
and &lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;kde.org&lt;/a&gt; websites.&lt;/p&gt;
&lt;p&gt;The content could probably still need some improvements, so if you find typos
or want to improve the wording of a sentence, please get in touch with
&lt;a class="link" href="https://community.kde.org/Get_Involved/promotion" target="_blank" rel="noopener"
&gt;KDE Promo&lt;/a&gt;. The good news is
that you don&amp;rsquo;t need to be a programmer for this.&lt;/p&gt;
&lt;h2 id="community-goal"&gt;Community goal&lt;/h2&gt;
&lt;p&gt;With Jonathan Riddell, we proposed a new community goal:
&lt;a class="link" href="https://phabricator.kde.org/T11117" target="_blank" rel="noopener"
&gt;KDE is All About the Apps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One part of this goal is to provide a better infrastructure and promotional
material for the KDE applications (notice the lowercase a). I think websites
are important to let people know about our amazing applications.&lt;/p&gt;
&lt;p&gt;So if you are maintaining a KDE applications and want a new shinning website,
please contact me. And I will try to setup for you a new websites, following
the general design.&lt;/p&gt;
&lt;h2 id="technical-details"&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The new website uses Jekyll to render static html. Because the layout and the
design aren&amp;rsquo;t unique to konsole.kde.org, I created a special Jekyll located at
&lt;a class="link" href="https://invent.kde.org/websites/jekyll-kde-theme" target="_blank" rel="noopener"
&gt;invent.kde.org/websites/jekyll-kde-theme&lt;/a&gt;,
so that only the content and some configuration files are located in the
&lt;a class="link" href="https://cgit.kde.org/websites/konsole-kde-org.git/" target="_blank" rel="noopener"
&gt;websites/konsole-kde-org&lt;/a&gt;
repository. This make it easier to maintain and will make it easier to change
others website in the future without repeating ourself.&lt;/p&gt;
&lt;p&gt;This was a bit harder to deploy than I first though, I had problem with
installing my Jekyll theme in the docker image, but after the third or fourth
try, it worked and then I had an encoding issue, that wasn&amp;rsquo;t present on my
development machine.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/konsole-ci.png" alt="Ci konsole website" /&gt;&lt;/p&gt;
&lt;h2 id="how-can-i-help"&gt;How can I help?&lt;/h2&gt;
&lt;p&gt;Help is always welcome, the KDE community develops more than 200 different
applications, and even though not all applications need or want a new modern
website, there is tons of work to do.&lt;/p&gt;
&lt;p&gt;If you are a web developer, you can help in the development of new website
or in improving the Jekyll theme. Internalization, localization and accessibility
still need to be implemented.&lt;/p&gt;
&lt;p&gt;If you are not a web developer, but a web designer, I&amp;rsquo;m sure there is room
for improvement in our theme. And it can be interesting to have small variations
across the different websites.&lt;/p&gt;
&lt;p&gt;And if you are neither a designer nor a developer, there is still tons of work
with writing content and taking good looking screenshots. For the screenshots,
you don&amp;rsquo;t even need to have a good English.&lt;/p&gt;
&lt;p&gt;If you have question, you can as always contact me in Mastodon at
&lt;a class="link" href="https://linuxrocks.online/@carl" target="_blank" rel="noopener"
&gt;@carl@linuxrocks.online&lt;/a&gt; or with
matrix at @carl:kde.org.&lt;/p&gt;
&lt;p&gt;You can discuss this post in &lt;a class="link" href="https://www.reddit.com/r/kde/comments/c4bbym/new_website_for_konsole/" target="_blank" rel="noopener"
&gt;reddit&lt;/a&gt; or &lt;a class="link" href="https://linuxrocks.online/@carl/102322804611731440" target="_blank" rel="noopener"
&gt;mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;style&gt;
img, video {
max-width: 100%;
margin-left: auto;
margin-right: auto;
display: block;
}
&lt;/style&gt;</description></item><item><title>New features for kde.org</title><link>https://carlschwan.eu/2019/06/15/new-features-for-kde.org/</link><pubDate>Sat, 15 Jun 2019 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2019/06/15/new-features-for-kde.org/</guid><description>&lt;p&gt;In the last week, there have been various small improvements to the
&lt;a class="link" href="https://kde.org" target="_blank" rel="noopener"
&gt;kde.org website&lt;/a&gt;.&lt;/p&gt;
&lt;style&gt;
img, video {
max-width: 100%;
}
&lt;/style&gt;
&lt;h2 id="search-bar-in-kdeorgapplications"&gt;Search bar in kde.org/applications&lt;/h2&gt;
&lt;p&gt;KDE has a lot of applications and with the last update to
&lt;a class="link" href="https://kde.org/applications" target="_blank" rel="noopener"
&gt;kde.org/application&lt;/a&gt; made by Jonathan
Riddell, all these applications are now displayed on the website.&lt;/p&gt;
&lt;p&gt;The problem is that it&amp;rsquo;s now difficult to search for a specific
application, so I added a search bar to this page. See
&lt;a class="link" href="https://invent.kde.org/websites/kde-org-applications/merge_requests/5" target="_blank" rel="noopener"
&gt;Merge request 5 kde-org-applications&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://invent.kde.org/websites/kde-org-applications/uploads/fd1411036f24facb9e1dda6b42bf4846/search.png" alt="Search bar in kde.org/applications" /&gt;&lt;/p&gt;
&lt;h2 id="support-multiple-screenshots-per-application"&gt;Support multiple screenshots per application&lt;/h2&gt;
&lt;p&gt;According to the AppStream specification, an application can have multiple
screenshots. If before only the first screenshot was displayed,
now all screenshots are displayed in a carousel. See
&lt;a class="link" href="https://invent.kde.org/websites/kde-org-applications/merge_requests/3" target="_blank" rel="noopener"
&gt;Merge request 3 kde-org-applications&lt;/a&gt;&lt;/p&gt;
&lt;video controls&gt;
&lt;source type="video/mp4" src="https://carlschwan.eu/assets/simplescreenrecorder-2019-06-15_21.42.03.mp4"&gt;
&lt;/video&gt;
&lt;h2 id="adding-schemaorg-information"&gt;Adding schema.org information&lt;/h2&gt;
&lt;p&gt;The application page now contains some additional metadata
information. This can help search engines to better understand
the content of the webpage. See &lt;a class="link" href="https://invent.kde.org/websites/kde-org-applications/merge_requests/7" target="_blank" rel="noopener"
&gt;Merge request 7 kde-org-applications&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="improving-the-plasma-product-page"&gt;Improving the Plasma product page&lt;/h2&gt;
&lt;p&gt;With Plasma 5.16, we now have a new wallpaper, so I changed the
wallpaper displayed at &lt;a class="link" href="https://kde.org/plasma-desktop" target="_blank" rel="noopener"
&gt;kde.org/plasma-desktop&lt;/a&gt;.
Problem: the contrast between the text and the background wasn&amp;rsquo;t
great. So I added a small breeze like window, created only with
CSS and HTML.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/css_window.png" alt="Window breeze like with only CSS" /&gt;&lt;/p&gt;
&lt;h2 id="junior-jobs"&gt;Junior jobs&lt;/h2&gt;
&lt;p&gt;There still are a lot of things that can be improved, and we would appreciate
some help to do it. If you always wanted to contribute to KDE, but
don&amp;rsquo;t have any knowledge in Cpp and Qt, then you can also help
with web development.&lt;/p&gt;
&lt;p&gt;If you are interested, I listed some junior jobs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The carousel used to display the application screenshots still
has some problems. The next and previous buttons don&amp;rsquo;t use the
same theme as the carousel shown on the homepage. Also, when
screenshots have a different size, the transition is not perfect.
For more information, see &lt;a class="link" href="https://bugs.kde.org/show_bug.cgi?id=408728" target="_blank" rel="noopener"
&gt;bug 408728&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need to add a description for each page in kde.org. This
will improve the SEO used for KDE. First we need to define the new variable in
aether, use it, and then we can start adding descriptions.
This task needs to be done in cooperation with kde-promo and this
should help with Search Engine Optimization (SEO).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wrote a small &lt;a class="link" href="https://community.kde.org/KDE.org/Local_Setup" target="_blank" rel="noopener"
&gt;installation instruction&lt;/a&gt;
on how to setup your local development environment. And if you need any help,
you can as always contact me in Mastodon at
&lt;a class="link" href="https://linuxrocks.online/@carl" target="_blank" rel="noopener"
&gt;@carl@linuxrocks.online&lt;/a&gt; or with
matrix at @carl:kde.org.&lt;/p&gt;</description></item><item><title>I was at the Libre Graphics Meeting 2019</title><link>https://carlschwan.eu/2019/06/03/i-was-at-the-libre-graphics-meeting-2019/</link><pubDate>Mon, 03 Jun 2019 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2019/06/03/i-was-at-the-libre-graphics-meeting-2019/</guid><description>&lt;img src="https://carlschwan.eu/assets/img/LGM_transparent_Klein_g02.png" alt="Featured image of post I was at the Libre Graphics Meeting 2019" /&gt;&lt;p&gt;I had a nice surprise last Monday, I learned that the city where I live Saarbrücken (Germany) is hosting the 2019 edition of the nice Libre Graphics Meeting (lgm). So I took the opportunity to attend my first FOSS event. The event took place at the Hochschule der Bildenden Künste Saar from the Wed 29.05 to Sunday 02.06.&lt;/p&gt;
&lt;p&gt;I really enjoyed, I meet a lot of other Free Software contributors (not only devs), and discovered some nice programming and artistic projects.&lt;/p&gt;
&lt;p&gt;There were some really impressive presentations and workshops.&lt;/p&gt;
&lt;h2 id="thursday-3005"&gt;Thursday 30.05.&lt;/h2&gt;
&lt;p&gt;GEGL (GIMP new &amp;lsquo;rendering engine&amp;rsquo;) maintainer Øyvind Kolås presented, how to use GEGL effect from the command line and the same commands can be used directly from GIMP. This is helpful, when we want to automate some workflow.&lt;/p&gt;
&lt;p&gt;In the afternoon, I discovered &lt;a class="link" href="https://www.praxislive.org/" target="_blank" rel="noopener"
&gt;PraxisLIVE&lt;/a&gt;, an awesome live coding IDE where you can create effect with Java and a graph editor and showing the effect instantly on for example a webcam stream or a music track.&lt;/p&gt;
&lt;p&gt;Ana Isabel Carvalho and Ricardo Lafuente explained their past workshop in Porto where the participants create pixel art fonts with git and the gitlab-ci.&lt;/p&gt;
&lt;h2 id="friday-3105"&gt;Friday, 31.05.&lt;/h2&gt;
&lt;p&gt;On Friday, I took part to two workshops. The first was GIMP, there I met a lot of GIMP/GEGL developers. But it was more development meeting than a workshop where I could get my hand dirty.&lt;/p&gt;
&lt;p&gt;I also took part in the Inkscape workshop, where I learned about all of the nice features coming in Inkscape 1.0 (a new alpha version was released during the LGM 2019 and users are encouraged to reports bugs and regressions). I also learned that Inkscape can be used to create nice wood works:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/wood_train.jpg" alt="A wood train" /&gt;&lt;/p&gt;
&lt;p&gt;The model is published in the &lt;a class="link" href="https://www.thingiverse.com/thing:3153574" target="_blank" rel="noopener"
&gt;Thingiverse&lt;/a&gt; under CC BY-NC-SA 3.0.&lt;/p&gt;
&lt;p&gt;After this productive day, most of the LGM participants went to the &amp;lsquo;Kneipentour&amp;rsquo; (bar-hopping) and enjoyed some good Zwickel (the local beer).&lt;/p&gt;
&lt;h2 id="saturday-0106"&gt;Saturday, 01.06.&lt;/h2&gt;
&lt;p&gt;After last night, it was a bit difficult to get up, but I was able be only one minute late to see Boudewijn Rempt talk &amp;ldquo;HDR Support in Krita&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In the afternoon, I took part in the Paged.js workshop, where we were able to create a book layout with CSS and HTML. &lt;a class="link" href="https://www.pagedmedia.org/paged-js/" target="_blank" rel="noopener"
&gt;Paged.js&lt;/a&gt; could be interesting for generating nice KDE handbooks with a professional looking feel, because it&amp;rsquo;s only using web standards (not implemented in any web browsers), and we could generate the pdf from the already existing html version.&lt;/p&gt;
&lt;h2 id="sunday-0206"&gt;Sunday, 02.06.&lt;/h2&gt;
&lt;p&gt;Sunday I took part in the Blender workshop, and Julian Eisel did an excellent job explaining the internal of how &amp;ldquo;&lt;a class="link" href="https://www.blendernation.com/2008/12/01/blender-dna-rna-and-backward-compatibility/" target="_blank" rel="noopener"
&gt;Blender DNA and RNA system&lt;/a&gt;&amp;rdquo; archives great backward compatibility for &lt;code&gt;.blend&lt;/code&gt; files and make it painless to write UI in Python almost directly connected to the DNA.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In summarize, LGM was a great event, I really enjoyed it and I hope I will be able to attend the next edition in Rennes (France) and see all these nice people again.&lt;/p&gt;
&lt;p&gt;Oh, and now I have now more stickers on my laptop.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/laptop_after_lgm19.jpg" alt="Laptop after lgm19" /&gt;&lt;/p&gt;
&lt;p&gt;You can comment to this post in &lt;a class="link" href="https://linuxrocks.online/@carl/102209749528789211" target="_blank" rel="noopener"
&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many thanks to &lt;a class="link" href="https://cmpwn.com/@sir" target="_blank" rel="noopener"
&gt;Drew DeVault&lt;/a&gt; for proofreading this blog post.&lt;/p&gt;</description></item><item><title>Contributing to the kde userbase wiki</title><link>https://carlschwan.eu/2018/11/18/contributing-to-the-kde-userbase-wiki/</link><pubDate>Sun, 18 Nov 2018 11:35:35 +0000</pubDate><guid>https://carlschwan.eu/2018/11/18/contributing-to-the-kde-userbase-wiki/</guid><description>&lt;p&gt;This is the story about how I started more than one month ago contributing to the KDE project.&lt;/p&gt;
&lt;p&gt;So, one month ago, I found a task on the &lt;a class="link" href="https://phabricator.kde.org/T9142" target="_blank" rel="noopener"
&gt;Phabricator instance&lt;/a&gt; from KDE, about the deplorable state of the &lt;a class="link" href="https://userbase.kde.org/" target="_blank" rel="noopener"
&gt;KDE userbase wiki&lt;/a&gt;. The wiki contains a lot of screenshots dating back to the KDE 4 era and some are even from the KDE 3 era. It’s a problem, because a wiki is something important in the user experience and can be really useful for new users and experienced ones alike.&lt;/p&gt;
&lt;p&gt;Lucky for us, even though Plasma and the KDE applications did change a lot in the last few years, most of the changes are new features and UI/UX improvements, so most of the information are still up-to-date. So most of the work is only updating screenshots. But up-to-date screenshots are also quite important, because when the user see the old screenshots, he can think that the instructions are also outdated.&lt;/p&gt;
&lt;p&gt;So I started, updating the screenshots one after the other. (Honestly when I started, I didn’t think it would take so long, not because the process was slow or difficult, but because of the amount of outdated screenshots.)&lt;/p&gt;
&lt;p&gt;But I also learned a lot about KDE doing this. For example did you know that Blink (Chrome webengine) is a fork of Webkit (Safari webengine) and Webkit is a fork of KHTML (Konqueror webengine). I also learned about the existence of lesser-known KDE apps, for example Kile (a latex IDE), Calligra (an office suite), KFloppy (a floppy disk editor), …&lt;/p&gt;
&lt;h2 id="how-to-update-the-screenshots"&gt;How to update the screenshots&lt;/h2&gt;
&lt;p&gt;As a non-native english speaker, I found out updating screenshots and quickly checking if the information is up-to-date is easier as I first though. There aren’t a lot of requirements, you only need a Phabricator account and the default Breeze theme installed. The Phabricator account is easy to create and the default theme should already be installed.&lt;/p&gt;
&lt;p&gt;Then for each wiki entry, you only need to download the software, find all outdated screenshots in the wiki entry, take a new screenshot for each old screenshot, and upload the new screenshot.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://carlschwan.eu/assets/img/upload_new_version.png" alt="Upload new file version in userbase kde" /&gt;&lt;/p&gt;
&lt;p&gt;For all icons, I quickly generated a png from the svg file with the following command:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;convert -density 1200 -resize 128x128 -background transparent /usr/share/icons/breeze/apps/48/okular.svg okular.png&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It’s not finished, there are still a lot of outdated screenshots in the wiki, but every day the amount decreases. :)&lt;/p&gt;
&lt;h2 id="geting-involved"&gt;Geting involved&lt;/h2&gt;
&lt;p&gt;And you, dear reader, can also help. Like I said: this job doesn’t need any programming skills or perfect english skills, just a bit of motivation. If you need help, there are some instructions available to get started editing the wiki: &lt;a class="link" href="https://userbase.kde.org/Tasks_and_Tools" target="_blank" rel="noopener"
&gt;Start Contributing&lt;/a&gt;, &lt;a class="link" href="https://userbase.kde.org/Toolbox" target="_blank" rel="noopener"
&gt;Markup help&lt;/a&gt;, &lt;a class="link" href="https://userbase.kde.org/Quick_Start" target="_blank" rel="noopener"
&gt;Quick Start&lt;/a&gt;. You can also contact me: on the fediverse &lt;a class="link" href="https://linuxrocks.online/@carl" target="_blank" rel="noopener"
&gt;@carl@linuxrocks.online&lt;/a&gt; or on reddit (&lt;a class="link" href="https://reddit.com/u/ognarb1" target="_blank" rel="noopener"
&gt;/u/ognarb1&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Thanks to XYQuadrat for proofreading this blog post. :D&lt;/p&gt;</description></item><item><title>Good practices for your Nextcloud app - Database</title><link>https://carlschwan.eu/1/01/01/good-practices-for-your-nextcloud-app-database/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/1/01/01/good-practices-for-your-nextcloud-app-database/</guid><description>&lt;h2 id="iquerybuilder-in-33"&gt;IQueryBuilder in 33&lt;/h2&gt;
&lt;p&gt;In Nextcloud, we use an query builder to create SQL request. This is quite a
bit more robust than just writing SQL directly, as this enable the standard
static analysis tools of PHP to run on it, avoid string concatenation and helps
abstracting the various database engines by hiding some implementation details.&lt;/p&gt;
&lt;p&gt;Our implementation of &lt;code&gt;IQueryBuilder&lt;/code&gt; is based on the &lt;code&gt;QueryBuilder&lt;/code&gt; from the
doctrine DBAL project and is basically just a wrapper with some additional tweaks
and the same API stability guarantee as the rest of Nextcloud.&lt;/p&gt;
&lt;p&gt;In the coming Nextcloud 33, we exposed some new features from the underlying
&lt;code&gt;QueryBuilder&lt;/code&gt; to our wrapper. Namely, more type-correct APIs for fetching the
results from the query builder. Now instead of:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getQueryBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mytable&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;executeQuery&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getQueryBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mytable&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;executeQuery&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fetchAssociative&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The advantage is that now static analysis engine like psalm consider &lt;code&gt;$row&lt;/code&gt; to
be either an associative array if a matching entry was found in the database or
false otherwise.&lt;/p&gt;
&lt;p&gt;Also available are &lt;code&gt;IResult::fetchNumeric&lt;/code&gt;, &lt;code&gt;IResult::fetchOne&lt;/code&gt;,
&lt;code&gt;IResult::fetchAllAssociative&lt;/code&gt;, &lt;code&gt;IResult::fetchAllNumeric&lt;/code&gt; and
&lt;code&gt;IResult::fetchFirstColumn&lt;/code&gt;. Porting to the new code can be tedious, this is
why we also added some &lt;a class="link" href="https://getrector.com/" target="_blank" rel="noopener"
&gt;rector&lt;/a&gt; rules to our &lt;a class="link" href="https://github.com/nextcloud-libraries/rector/" target="_blank" rel="noopener"
&gt;rules
collections&lt;/a&gt; that does that for
you automatically.&lt;/p&gt;
&lt;p&gt;Rector is a very nice tool which allow to modernize your codebase
automatically, to newer version of PHP and your dependencies. You can find out
to use it in your Nextcloud app in &lt;a class="link" href="https://github.com/nextcloud-libraries/rector/?tab=readme-ov-file#nextcloudrector" target="_blank" rel="noopener"
&gt;this
tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition, we added two new methods &lt;code&gt;IResult::iterateNumeric&lt;/code&gt; and
&lt;code&gt;IResult::iterateAssociative&lt;/code&gt; which can be an attractive replacement for
&lt;code&gt;IResult::fetchAll&lt;/code&gt; as these prevents the PHP process to hold the whole
result in memory which can in some cases then exceed the memory limit of
the PHP process and lead to crashes.&lt;/p&gt;
&lt;p&gt;Finally regarding the changes for 33 related to the QueryBuilder. We finally
deleted the long deprecated &lt;code&gt;IQueryBuilder::execute&lt;/code&gt; method. You should port
your app to &lt;code&gt;IQueryBuilder::executeStatement&lt;/code&gt; or &lt;code&gt;IQueryBuilder::executeQuery&lt;/code&gt;.
This two methods have a more correct return type that the static analyser can
use. When porting beware that &lt;code&gt;IQueryBuilder::execute&lt;/code&gt; was throwing an
third-party exception from doctrine, while the new methods are throwing an
&lt;code&gt;\OCP\DB\Exception&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="snowflake-ids"&gt;Snowflake IDs&lt;/h2&gt;
&lt;p&gt;We are also thinking about deprecating and removing &lt;code&gt;getLastInsertedId&lt;/code&gt;. This
feature is not longer supported by the latest release of Doctrine in all
databases we support and to use &lt;a class="link" href="https://en.wikipedia.org/wiki/Snowflake_ID" target="_blank" rel="noopener"
&gt;snowflake ids&lt;/a&gt; instead. More information
about that will be published in a following post.&lt;/p&gt;
&lt;h2 id="sql-functions"&gt;SQL functions&lt;/h2&gt;
&lt;p&gt;While analysis a bug report, we found out that in some places the
&lt;code&gt;ÌQueryBuilder::createFunction&lt;/code&gt; was misused in various places. While the
documentation explicitely indicate that column names should be escaped, this
wasn&amp;rsquo;t done before. For example, this was invalid:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getQueryBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;createFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;COUNT(id)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the correct way to do that with &lt;code&gt;createFunction&lt;/code&gt; is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getQueryBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;createFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;COUNT(&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getColumnName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But a simpler way to do it, is to use the &lt;code&gt;IFunctionBuilder&lt;/code&gt;. This is way harder
to get wrong and is shorter to type.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getQueryBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is still some legitimate uses of &lt;code&gt;createFunction&lt;/code&gt;, for example subqueries, but many
uses can be replaced by the &lt;code&gt;IFunctionBuilder&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="future"&gt;Future&lt;/h2&gt;
&lt;p&gt;For the future, there is already work ongoing to fully add type hints to the &lt;code&gt;ÌQueryBuilder&lt;/code&gt; and related
classes. There is already some work toward that on this &lt;a class="link" href="https://github.com/nextcloud/server/pull/57763" target="_blank" rel="noopener"
&gt;draft pull
request&lt;/a&gt;. This should help static analysers
to find errors even more.&lt;/p&gt;
&lt;p&gt;Additionally, there is some ongoing research about how we do entities in
Nextcloud, so that app developers can avoid in many situation touching directly
to the query builder. One current direction, is to provide a new Entity API
based on modern PHP features like attributes and simple objects. Here is an example
on how this would look like based on the current progress on &lt;a class="link" href="https://github.com/nextcloud/server/pull/56199" target="_blank" rel="noopener"
&gt;this WIP pull request&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#[Entity]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#[Table(name: &amp;#39;twofactor_backupcodes&amp;#39;)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BackupCode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;#[Id(generatorClass: IGenerator::class)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;#[Column(name: &amp;#39;id&amp;#39;, type: Types::STRING, length: 64, nullable: false)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;#[Column(name: &amp;#39;user_id&amp;#39;, type: Types::STRING, length: 64, nullable: false)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;#[Column(name: &amp;#39;code&amp;#39;, type: Types::STRING, length: 128, nullable: false)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;#[Column(name: &amp;#39;used&amp;#39;, type: Types::SMALLINT, nullable: false, default: 0)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>KDE websites and documentation news (December 2020 - March 2021)</title><link>https://carlschwan.eu/1/01/01/kde-websites-and-documentation-news-december-2020-march-2021/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/1/01/01/kde-websites-and-documentation-news-december-2020-march-2021/</guid><description>&lt;p&gt;This last few months have been focused on improving the website tooling uing the Hugo framework. The base theme that was developed for kde.org was moved to a seperate repository and is now completely reusable for other projects to use. Other than the speed improvement other Jekyll, there are two big improvements in using Hugo instead of Jekyll for the KDE websites: the translation system is a lot more mature and by using go modules it is easier to share assets, scss modules, templates and translations between websites.&lt;/p&gt;
&lt;p&gt;The new Hugo theme was quickly made of use inside the Kate website making it looks nicer and more consistent with the other KDE websites.&lt;/p&gt;
&lt;p&gt;Phunh has done a lot of cleaning in the kde.org hugo codebase to simplify it and reduce the amount of duplicate content.&lt;/p&gt;
&lt;p&gt;Phunh then ported the planet.kde.org website to use Hugo. The most notable user visible change is that there is now a small tool on the left to navigate between articles. Aside from that the localized feed page is now completely translatable.&lt;/p&gt;
&lt;p&gt;Similarly I made plasma-mobile.org switch to Hugo and I&amp;rsquo;m slowing making every pages translatable. I also added in the homepage a list including most of the Kirigami/Maui applications. This list is provided by apps.kde.org, so that it remains up to date and is localized. Help would be welcome around updating the rest of the plasma-mobile.org website to make it more up to date with the development of the project.&lt;/p&gt;
&lt;p&gt;Another big news is that Pablo Marcos finished his SoK project and ported the okular.kde.org website to Hugo and the current KDE theme. It&amp;rsquo;s looking great.&lt;/p&gt;
&lt;p&gt;In term of documentation, Clau Cambra improved his Kirigami intruduction and updated the Configuration-related articles. I wrote a new article about how to use &lt;a class="link" href="https://develop.kde.org/docs/akonadi/using_akonadi_applications/" target="_blank" rel="noopener"
&gt;Akonadi and Kirigami to build a mail viewer&lt;/a&gt; and imported a few old KWin and Plasma tutorial from techbase to develop.k.o. This include the old &lt;a class="link" href="https://develop.kde.org/docs/plasma/windowswitcher/" target="_blank" rel="noopener"
&gt;KWin Window Switcher tutorial&lt;/a&gt;, the &lt;a class="link" href="https://develop.kde.org/docs/plasma/kwin/" target="_blank" rel="noopener"
&gt;KWin scripting guide&lt;/a&gt; and the &lt;a class="link" href="https://develop.kde.org/docs/plasma/theme/" target="_blank" rel="noopener"
&gt;Plasma theme tutorial&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Working with QtWidgets</title><link>https://carlschwan.eu/1/01/01/working-with-qtwidgets/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://carlschwan.eu/1/01/01/working-with-qtwidgets/</guid><description>&lt;p&gt;If you know me, you probably know that I have been a big than of QML and
QtQuick as technology. I&amp;rsquo;m started NeoChat, Tokodon, Arianna, Kalendar
(now known as Merkuro), ported KWordQuiz to QML and regularly contribute
to an handful of other QML projects inside KDE.&lt;/p&gt;
&lt;p&gt;Recently I started working at GnuPG on Kleopatra and other Qt components
of the &lt;a class="link" href="https://www.gpg4win.org/" target="_blank" rel="noopener"
&gt;GPG4Win&lt;/a&gt; and
&lt;a class="link" href="https://gnupg.com/gnupg-desktop.html" target="_blank" rel="noopener"
&gt;GnuPG-VS Desktop&lt;/a&gt; product, which are
all QtWidgets based. This has been a quite rewarding learning experience as
it is both very similar and different to QtQuick in various aspects.&lt;/p&gt;
&lt;p&gt;These two technologies share a lot in term of data structures with all the
containers being the same (QVector, QString, QHash, &amp;hellip;) as well as the
concept of models (QAbstractItemModel) and the QObject signal/slots mechanism
to react to user interactions. So nothing really new from this side. There
stuff are getting different is related to graphics.&lt;/p&gt;
&lt;p&gt;QtQuick uses QML as it primarely way to design an user interface. It is a
really nice language and it&amp;rsquo;s really easy to interact between QML and C++.&lt;/p&gt;</description></item></channel></rss>