Skip to content

Wednesday, 13 August 2025

KStars v3.7.8 is released on 2025.08.13 for Windows & Linux. MacOS release is pending. It's a bi-monthly bug-fix release with a couple of exciting features.

Live Stacking

The moment many of you have been waiting for has arrived! John Evans has introduced the highly anticipated Live Stacking feature to Ekos, seamlessly integrated into the FITS Viewer. Once you configure your live stacking directory, any FITS images added will be automatically stacked in real-time.

This feature works independently of your existing sequences or scheduler sessions, allowing you to configure the live stacker to process captured images for an enhanced view of your final results. It's particularly valuable for public observatories and star parties, where real-time feedback can greatly enhance the viewing experience.

Since Live Stacking is still in beta, we welcome your feedback and suggestions for improvement. Check out the demonstration videos below to see the feature in action and learn about its capabilities.


Equipment Profile Editor

The Ekos Equipment Profile editor has received a complete redesign. We've eliminated the limitations of the previous editor—you can now add unlimited drivers to your equipment profiles without restrictions. Need three different mounts in a single profile? No problem!

Both driver and profile lists now include search functionality, making it quick and easy to locate specific drivers. This flexibility gives you unprecedented control over your equipment configurations.



Focus History Navigation

Wolfgang Reissenberger has added an invaluable Focus History navigation feature. When autofocus encounters issues, you can now navigate through your focus frame history to inspect images from all autofocus runs, making troubleshooting much more effective.

The Focus module now includes a dedicated navigation panel. To optimize screen real estate, we've moved the detected stars display and current HFR readings above the V-Curve rather than below it, freeing up valuable space.


To use this feature, simply configure the maximum number of stored frames in your Focus settings. The navigation controls are active only when autofocus sequences aren't running.



FITS Viewer Stretch Presets

Hy Murveit has refined the FITS Viewer stretch sliders and added a convenient preset button that cycles through seven different stretch configurations, offering various combinations of background brightness and contrast levels.

When you display the histogram and zoom in, the sliders provide much finer resolution control—particularly useful for precise black point adjustments.

Flatpak Support

Thanks to extensive collaboration between the Ikarus Technologies team, KDE Infrastructure team, and the Flathub community, we now offer both Stable and Nightly Flatpak packages.

The KStars Flatpak includes all INDI drivers and runs securely within the flatpak sandbox environment. This allows you to install both Stable and Nightly versions simultaneously without conflicts; perfect for testing the latest features and bug fixes while maintaining access to the stable release.

For installation on a brand new system, you'll need to install INDI firmware files. We've prepared comprehensive instructions and a simple installation script for both stable and nightly versions to facilitate this process.

Polar Alignment Improvements

Hy Murveit improved the Polar Alignment algorithm, especially when it the mount is close to the meridian. Wolfgang Reissenberger improved the the size of the arrows indicating the direction of the necessary adjustments so they're more visible in the Polar Alignment Assistant.

Migration to Qt6

This release represents a significant technical milestone with our complete migration to Qt 6 and KDE Frameworks 6. KStars is still compatible with Qt 5 until all major distributions fully switch to Qt 6. While these changes happen behind the scenes, they provide a more robust foundation for future development.

The entire KStars development team contributed to this substantial effort. Special recognition goes to Eric Dejouhanet and Robert Lancaster, who invested countless hours reworking our complex CI/CD pipelines and macOS support builds in KDE's Craft system. This release wouldn't have been possible without their exceptional dedication.


Monday, 11 August 2025

Dark mode has been available in Plasma for quite a while now but the next release explores new areas where it can be taken one step further, beginning from some behind the scenes infrastructure to more user facing features such as dynamic wallpapers and automatic day/night global theme switching.

My journey to improving dark mode capabilities started at the recent Plasma Sprint. A while ago, I developed a wallpaper plugin that’s capable of synchronizing images to the position of the Sun, which I wanted to upstream but one of the biggest issues was the sheer size of the project. It supports a bunch of modes, it requires special tooling, and the wallpaper format is too cumbersome for upstream (although it’s amazing for 5K or 8K images!). So, I decided to make a pitch for adding one of the modes available in that plugin to other Plasma developers, which should be very easy to integrate with the existing infrastructure in Plasma. The idea was well-received and it led to a discussion about other related things, like better integration of geolocation services in Plasma, etc.

Plasma Sprint 2025

Day/night wallpapers

Some wallpaper packages can contain light and dark images. Currently, the light image will be used with a light color scheme, and the dark image will be used with a dark color scheme. The main idea behind the new day/night dynamic wallpapers was to exploit this design of the wallpaper packages: switch between light and dark images depending on the elevation of the Sun rather than the color scheme.

New day/night mode

The plasma5-wallpapers-dynamic served as the basis for the new dynamic wallpaper mode in Plasma. It works exactly like we want. If geolocation data is available, use it to compute the position of the Sun. The daylight duration varies depending on your location throughout the year; by taking the geolocation into account, the dynamic wallpapers can closely match the dark/light cycle. If the geolocation is unknown, fall back to some pre-configured morning and evening times.

From functional point of view, it doesn’t have any purpose, but it’s a lovely touch that helps you to personalize your computer.

While on this, we also looked into optimizing the wallpaper configuration dialog. Specifically, fixing GridView in QtQuick so it is possible to reuse wallpaper preview items for smoother scrolling and re-working the wallpaper preview generation process for better performance and to make it less error prone.

How to create a day/night wallpaper

A day/night wallpaper is a regular wallpaper package but with light and dark images, Plasma does the rest to figure out what images should be displayed.

For example, imagine that you want to create a wallpaper with a lake. Then the wallpaper would look something like this

Lake/
├── contents
│   ├── images
│   │   ├── 1080x1920.png
│   │   └── 5120x2880.png
│   └── images_dark
│       ├── 1080x1920.png
│       └── 5120x2880.png
└── metadata.json

The Lake folder contains two things: a contents folder with images and a metadata.json file. The metadata.json file provides extra information about the wallpaper, e.g. the author, etc.

{
   "KPlugin": {
       "Authors": [
           {
               "Email": "foobar@example.com",
               "Name": "Name of the author"
           }
       ],
       "Id": "Next",
       "License": "CC-BY-SA-4.0",
       "Name": "Lake"
   },
"X-KDE-CrossFade": true
}

The Id specifies the unique id of the wallpaper, the Name specifies the human readable name of the wallpaper. The metadata may also contain an X-KDE-CrossFade key, this is used to enable or disable long cross-fade. For example, if the transition from day to night lasts 30 minutes, then Plasma will slowly cross-fade from the light to the dark image for 30 minutes. Such a long cross-fade transition can be undesired if the images are too distinct.

images and images_dark folders contain light and dark images, respectively. Note that image file names must match the image resolution, i.e. 5120x2880.png instead of light.png, etc.

The wallpaper should be installed in /usr/share/wallpapers or ~/.local/share/wallpapers.

knighttime(d)

In addition to day/night wallpapers, it would also be nice if more things could be more dynamic. For example, switching the global theme at night. However, this creates a bit of a problem. If you want to configure the times when wallpapers or themes need to be changed, you will need to set settings for every feature individually, which is not great.

To solve that problem, a new desktop component was introduced that orchestrates when morning and evening transitions occurs – KNightTime. It’s a super duper tiny project with a client library and a daemon (knighttimed) that provides the schedule for day/night cycle transitions.

Since all features that synchronize to the day/night cycle subscribe to KNightTime, you only need to configure it to use specific morning and evening times. Night Light, day/night wallpapers and so on will be all synchronized.

knighttimed runs in background and it is started on demand, so in most cases, you don’t even need to worry about its existence.

Day/night themes

Besides the wallpaper, one can also switch between light and dark global themes depending on time of day.

Perhaps the hardest part about this was figuring out how to retrofit our system settings to accommodate for this new mode rather than actually implementing the logic that changes the active theme. Many thanks to Felix Ernst and Nate Graham for providing mock-ups and feedback about the UI.

Global theme options
Landing page

It is worth noting that Plasma will switch between the themes only if the computer has been idle for 5 seconds. The main reason behind this is to avoid interruptions while actively using the computer. In case you don’t like it, you can turn it off in system settings or increase the default idle interval if you think it’s too low for your needs.

With the automatic mode, the global theme switching code is going to be stress-tested quite a bit, so we’ve also started (and continue) looking into various paper cut issues regarding theme switching, e.g. some buttons not adapting to the new color scheme correctly, etc.

Future improvements

There are other things that can be synchronized to the day-night cycle, for example screen brightness. Although, there is nothing clear on the radar yet.

Conclusion

The next release is going to feature a few dark mode improvements. Some are pretty technical, some are very user facing. In either case, I hope that things like this will help to make Plasma users happier and make Plasma stand out more among other desktop environments.

Intro

Last week, I added canvas limitations to keep the Selection Action Bar within a user's view. This week, I focused on positioning the toolbar so it renders directly underneath the initial selection instead of a hard coded value on the canvas. This change improves the user experience by reducing mouse travel and making the toolbar feel more intentional and intuitive to use.

Side note: This week I also added 2 buttons: 'Fill with Color' and 'Crop to Selection'.

Obstacles to Implementation

The first step to render the Selection Action Bar under the selection was to find the selection's coordinates.

Mapping Coordinates

The difficulty and learning opportunity I found during this process was understanding the difference between the image space and the canvas space. To keep it simple, selections live in the image space and the Selection Action Bar lives in the canvas space. Due to zooming in and out, different resolutions, and resizing, coordinates in the image space are not always equal to the canvas space. So to translate/map the coordinates of the selection to match where the toolbar should render, we can use KisCoordinatesConverter to translate these coordinates from image space to canvas space.

// get current selection
KisSelectionSP selection = d->m_viewManager->selection();

// get coordinates of current selection
QRectF selectionBounds = selection->selectedRect();
int selectionBottom = selectionBounds.bottom();
QPointF selectionCenter = selectionBounds.center();
QPointF bottomCenter(selectionCenter.x(), selectionBottom);

// translate the selection coordinates to the canvas widget space
QPointF widgetBottomCenter = converter->imageToWidget(bottomCenter);

// adjust/center/space how the toolbar renders under the selection
widgetBottomCenter.setX(widgetBottomCenter.x() - (d->actionBarWidth / 2)); 
widgetBottomCenter.setY(widgetBottomCenter.y() + d->bufferSpace);

// update the relative position of the toolbar
d->dragRectPosition = widgetBottomCenter.toPoint();

Conclusion

With these improvements, the UI now feels user friendly and intentional. For the next (and final) 2 weeks of GSoC, I plan to organize my documentation to build out a technical page on Krita's official documentation website and to refactor some code for better maintainability.

Contact

To anyone reading this, please feel free to reach out to me. I'm always open to suggestions and thoughts on how to improve as a developer and as a person. Email: ross.erosales@gmail.com Matrix: @rossr:matrix.org

Saturday, 9 August 2025

The Amarok Development Squad is happy to announce the immediate availability of Amarok 3.3.1, the first bugfix release for Amarok 3.3 "Far Above the Clouds"!

3.3.1 features a number of minor bugfixes and code and build system cleanups. Some occasional crashes related to the new audio backend have also been fixed, and the remaining non-theme icons in user interface have been replaced. The new release also features improvements to the scripting support. Although current scripting functionality is not yet on a par with that of 2.x series in the '10's, especially the script console improvements should help anyone willing to explore the current capabilities.

Changes since 3.3.0

FEATURES:
  • Enable saving and loading script console items, autocompletion in script console, and re-enable some more scripting functionality
CHANGES:
  • Convert the remaining main UI toolbuttons to use icons from theme
  • Clear out remnants of the now-discontinued MusicDNS service
BUGFIXES:
  • Fix example permission grant command in database settings (BR 386004)
  • Fix equalizer gains not updating when selecting some presets (BR 463908)
  • Fix continuing playback after timecoded tracks (cue files etc, (BR 270003)
  • Fix MusicBrainz search
  • Properly start CD playback if Amarok is not already running (BR 503310)
  • Also transmit embedded cover art through MPRIS (BR 357620)
  • Don't show transcoding dialog after canceling download (BR 275840)
  • Load network information earlier to avoid crashes on startup (BR 507497)
  • Try to export as-compatible-as-possible playlist files (BR 507329)
  • Fix some random crashes during playback

Getting Amarok

In addition to source code, Amarok is available for installation from many distributions' package repositories, which are likely to get updated to 3.3.1 soon, as well as the flatpak available on flathub.

Packager section

You can find the tarball package on download.kde.org and it has been signed with Tuomas Nurmi's GPG key. One should also observe that Amarok 3.3.1 drops the previous optional build-time dependency to ffmpeg.

Friday, 8 August 2025

As of a couple days ago Filelight on Windows is performing its search many times faster than before. Go check it out!

https://apps.kde.org/filelight/

It’s shocking that it was slow to begin with… A few years ago I rewrote the Filelight file walking logic to use native API for Linux and Windows. I thought I did a pretty good job of it and speed wasn’t too shabby. You can imagine my surprise when a friend recently pointed out that a similar piece of software performed maybe even better. Obviously this couldn’t stand. I immediately went to profile the situation and indeed Filelight wasn’t as fast as expected.

It turns out there are multiple competing APIs for querying file information on Windows and depending on which gets used performance may not be what you would expect. There’s a good write-up at https://www.cppstories.com/2024/cpp-query-file-attribs-faster/ in case you care to read more.

Long story short: by not calling slow API, Filelight is now heaps faster than before. Scans that took multiple seconds before now finish in the blink of an eye.

In case you are wondering just how much faster it is:

New

Old

Enjoy!

Using the Qt Logger in Rust with CXX-Qt

For projects incorporating Rust and Qt, a minor but important problem can arise: too many sources of log messages. In the usual case, one of these is from Rust (and crate dependencies) and another is from Qt. Our Rust and Qt solution, CXX-Qt, exposes a new API to help with this in the upcoming 0.8.0 release.

There are several benefits to ensuring your log messages go through one logger. It makes it easier to redirect said messages into a file, there's less configuration required for end users, and it makes consistent formatting easier.

Qt has its own logging infrastructure, and their suggested way to print messages in Qt applications. It has a category system, rules for filtering which messages you want to see, custom formatting and so on. Applications like GammaRay can also display messages from Qt, and configure which categories are emitted.

One of the more popular logging libraries in Rust is called tracing, and we're going to show how you can redirect messages from it to the Qt logger. The integration can work for any other logging crate too, including your own custom solution.

Getting started

Before we start splicing the two log streams, let's quickly go over sending a Qt log message in Rust. Assuming you already have a CXX-Qt application (and if you don't, you can follow our book, construct a message log context with QMessageLogContext:

let file = CString::new("main.rs").unwrap();  
let function = CString::new("main").unwrap();  
let category = CString::new("lib").unwrap();  
  
let context = QMessageLogContext::new(&file, 0, &function, &category);

You can specify the filename, the line number, the function name and even a category. We have to use CString here because QMessageLogContext in C++ uses const char*, not QString.

Note that there isn't a way to get the name of the currently calling function out of the box in Rust, but there are alternative solutions if you want that information.

Now that we have a context, we can send a message to the Qt logger. We can do this using the unfortunately undocumented qt_message_output function. This sends a message to the currently installed message handler.

Note that CXX-Qt currently doesn't have Rust bindings to install a custom one.

The default message handler goes to the standard output and error streams. This function takes a QtMsgType, the context we just created, and a QString message:

qt_message_output(  
    QtMsgType::QtInfoMsg,  
    &context,  
    &QString::from("This is an informational message..."),  
);

And voilà:

lib: This is an informational message...

But that looks pretty plain, and we can't see any of our context! Most information available in the Qt logger isn't shown by default, but we can modify it by using the QT_MESSAGE_PATTERN environment variable.

I used the comprehensive example given in the linked documentation, but it's useful for showcasing the context:

export QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz ttt} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"
[20250314 8:56:45.033 EDT I] main.rs:0 - This is an informational message...

Now that we have sent our first Qt log message from Rust, let's up the ante and integrate the tracing crate.

Integrating with Tracing

To redirect events from tracing, we have to add a subscriber that forwards events to Qt. This basically means anything that uses tracing (e.g. your application, another crate) will be picked up.

As a side effect of tracing's flexibility, we also have to create our own Visitor. This is necessary because tracing records structured data, and we need to flatten said data to a string.

use std::fmt::Write; // needed for write_fmt

struct StringVisitor<'a> {
    string: &'a mut String,
}

impl tracing::field::Visit for StringVisitor<'_> {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        write!(self.string, "{} = {:?} ", field.name(), value).unwrap();
    }
}

And now for the custom layer that will catch the events, and send them to Qt:

pub struct QtSubscriber {}

impl<S> tracing_subscriber::Layer<S> for QtSubscriber
    where
        S: tracing::Subscriber,
{
    fn on_event(
        &self,
        event: &tracing::Event<'_>,
        _ctx: tracing_subscriber::layer::Context<'_, S>,
    ) {
        let mut buffer: String = String::new();
        let mut visitor = StringVisitor {
            string: &mut buffer
        };
        event.record(&mut visitor);

        let msg_type = match *event.metadata().level() {
            tracing::Level::ERROR => QtMsgType::QtCriticalMsg,
            tracing::Level::WARN => QtMsgType::QtWarningMsg,
            tracing::Level::INFO => QtMsgType::QtInfoMsg,
            tracing::Level::DEBUG => QtMsgType::QtDebugMsg,
            tracing::Level::TRACE => QtMsgType::QtDebugMsg
        };

        let file = if let Some(file) = event.metadata().file() {
            CString::new(file).unwrap()
        } else {
            CString::default()
        };

        let line = if let Some(line) = event.metadata().line() {
            line as i32
        } else {
            0
        };
        
        let function = CString::default();  
        let category = CString::new("lib").unwrap();  

        let context = QMessageLogContext::new(&file, line, &function, &category);  

        qt_message_output(msg_type, &context, &QString::from(buffer));
    }
}

And finally, we have to register our new layer with the registry:

use tracing_subscriber::layer::SubscriberExt; // needed for with
use tracing_subscriber::util::SubscriberInitExt; // needed for init

tracing_subscriber::registry().with(QtSubscriber{}).init();

Afterwards, try sending a message using tracing::info! and see if it works:

tracing::info!("This is an informational message... from tracing!")

Using a more comprehensive QT_MESSAGE_PATTERN, you should get a log message from Qt. Note that "message" is written here, as that's the default field in tracing:

[20250320 10:41:40.617 EDT I] examples/cargo_without_cmake/src/main.rs:104 - message = This is an informational message...

These new bindings in CXX-Qt should be useful to application developers who want to adopt the best logging crates in Rust, while ensuring their messages are forwarded to Qt. If you want to redirect Qt log messages to Rust, CXX-Qt doesn't have a way to install custom message handlers yet.

See our KDABLabs repository for complete examples of the snippets shown here.

The post Using the Qt Logger in Rust with CXX-Qt appeared first on KDAB.

The other day I finally replaced my trusty Thinkpad T480s I bought 6½ years ago. Overall, I was still pretty happy with it and even gave it a little refresh early last year (RAM upgrade, bigger SSD, new keyboard) but the CPU was really starting to show its age when compiling. I’m almost as picky as Nate when it comes to laptops but the P14s Gen 5 AMD (what a mouthful) checked more boxes than most laptops I looked at in recent years.

Plasma 6.5 Dev desktop, black panel and analog clock, wallpaper dark variant (darker hues of purple than the default). KInfoCenter window with light theme ontop, showing information about the device (e.g. KDE neon unstable edition, Qt 6.9.1, 64 GB of RAM, etc)
Breeze Twilight, for the OLED’s sake

The device shipped with Windows 11 and whenever I touch a Windows machine I’m baffled that people put up with this. I connected it to Wifi (beginner’s mistake apparently) since I wanted to install all firmware updates and salvage a couple of things from it before formatting the SSD (ICC profiles, Dolby audio presets, etc). The first run wizard asked me for my choice of locale, then went looking for updates. Once done, the system rebooted. After that it asked me to give the computer a name. Guess what? Another reboot, and more updates. And then the dreaded compulsory Microsoft account I had no intention of creating. You can open a terminal by pressing Shift+F10 but the old bypassnro trick just led to a reboot and it asking the same questions again. Just when I was about to give up, Bhushan showed me another trick how to create a local account. Indeed, after yet another reboot and clicking away like 10 individual nag screens about privacy and cloud stuff, I was able to log into the system.

This is the sort of usability nightmare and command line tinkering bullshit that people were mocking Linux users for back in the days! Compare that to my KDE neon “unstable” installation where I plugged in a USB stick (Secure Boot of course rejected “3rd party keys” by default), booted into the live system, had the entire drive formatted, and within 10 minutes or so ended up with a working sexy KDE Plasma setup. I’m still sometimes amazed how beautiful our default setup looks nowadays with the floating panel, frosted glass, and all. I don’t like dark mode but since that laptop has an OLED screen I opted for “Breeze Twilight” which combines light applications with a dark panel and wallpaper.

Screen configuration options:
Resolution: 2880 x 1800 (16:10)
Scale: 200%
Orientation: upright
Refresh rate: 120 Hz
Overscan: 0%
Color profile: ICC profile TPLCD_414B_Default.icm
I salvaged the color profiles from the factory Windows install

As with any new device, there’s a few surprises waiting for you. Like most recent laptops it has a stupid Copilot (“AI”) key. Unfortunately, rather than inventing a new key code, it emulates an actual key combination of Meta+Shift+Touchpad Off (F23 I heard, yes, there can be more than just F12). This makes it difficult to just remap the key to something useful (like a right Meta key). However, at least you should be able to use it as a global shortcut, right? Unfortunately, you weren’t able to assign it from GUI. I now fixed the Copilot key by allowing Shift be used in a shortcut in conjunction with “Touchpad Off”. It’s a kludge but at least you can now make it bring up KRunner or something.

Speaking of proprietary keys, it also has a “Phone Link” function key. It is recognized as such starting from Linux kernel 6.14 but there’s no support in Xkb or Qt yet. I just sent a pull request to libxkbcommon to add it and once that lands, I’ll look into adding it to Qt. How cool would it be if under Plasma the Phone Link button would instead open KDE Connect?!

The suspend-to-idle stuff is both a great opportunity and a little scary. Modern laptops don’t do “proper” S3 suspend anymore but only S2 which works more like on a smartphone where it can wake up anytime and fast. Right now even plugging in the charger or touching the touchpad causes the machine to wake up. Luckily, if KWin detects the lid is shut and no monitor is connected, it sleeps again after a short time to prevent a hot backpack situation. Work is ongoing to make better use of this capability, to detect what caused the system to wake up. For example, when plugging in the charger, we might want to wake up, play a short animation and the “plugged in” sound and go back to sleep.

I can still return the device within the 14 day period if something major crops up but I already fell in love with its 120 Hz OLED 16:10 display (I luckily don’t seem to be sensitive to the 240 Hz PWM it uses), so I don’t think I’ll be returning it :-)

Thursday, 7 August 2025

During all my time with KDE projects, I've never made an app from scratch.. Except now.

Today my first KDE app ever, KomoDo, was released on Flathub!

Screenshot of the KomoDo todo.txt application

It's a simple todo application for todo.txt format and parses todo.txt files into a list of cards. One task is one card.

The application has help sections for any of the rules todo.txt format follows, but you can use as many or as little of them as you want.

I wanted to go through in this blogpost about the process, or what I remember of it anyway. I've always just fixed bugs in Plasma or Frameworks or even in apps like Dolphin, but never made anything from scratch. So I thought that writing this down might be helpful for someone in similar position.

As with any KDE project, it's open source and the repository can be explored right here: utilities/komodo.

Starting up

Starting a Qt project using KDE libraries, especially QtQuick, is not that difficult in the end. KDE has really good documentation for starting up. I mostly followed this guide to get started: https://develop.kde.org/docs/getting-started/kirigami/setup-cpp/

There is also guides for Python and Rust if you wish to use those instead of C++.

I also highly recommend using kde-builder for building and running the applications. It makes things so much easier.

Other than that, there's not much to tell about the setup.

The project itself

Working on the application was not that bad either. I've worked on various C++ and QML files during my work on KDE software, so I had easy time getting into the flow.

I think the most difficult part for me was the CMake files: I couldn't really understand them or what they do, I mostly followed what other projects did and replicated them.

This of course caused me to do unnecessary things, like installing libraries I made for the app: TodoModel which the QML code uses to parse the todo.txt file and generate the view was accidentally installed among the system libraries, which was not ideal, since I'm not building a framework.

Luckily with tons of help and reviews from my friends in the KDE fam, we got CMake to build things nicely. Then with kde-builder the feedback loop of code->build->test was fast enough, especially in small app like this, that I could iterate on the application in a good pace.

I also had a lot of help from a friend with the CI and sysadmin stuff. That side of development is very confusing to me so I'm glad with the help. I dunno if you want to be named so I didn't, but you know who you are: Big thanks. :)

TodoModel

Since Qt is reliant on the model-view system, I had to make a "model" that parses the todo.txt file and spits out Todo objects, which can then be parsed by the QML code, which is the frontend code.

If you have never worked on model-view systems, it can take some time to understand. To me, the model-view as words was always a bit confusing: I would have understood it better if it was worded something like data-ui. But tech jargon has always been my weakest point.

The parsing is done by a RegExp nightmare I concocted with help of stack-overflow and tears.

Here it is, in its nightmarish glory, enjoy the nightmare fuel:

QStringLiteral("(?:^[ "
	"\\t]*(?P<Completion>x))|(?P<Priority>\\([A-Z]\\))|(?:(?P<FirstDate>"
	"\\d{4}-\\d\\d-\\d\\d)[ "
	"\\t]*(?P<SecondDate>\\d{4}-\\d\\d-\\d\\d)?)|(?P<Projects>\\B\\+[\\w\\d\\S]+)|(?P<"
	"Contexts>(?<=\\s)@[^\\s]+)|(?P<KeyValuePairs>[a-zA-Z]+:[\\S]*)");

Yyyeah. It's.. Something.

Testing

Due to the nightmare fuel that RegExp can be, I decided that unit testing would be great addition. So I fought with CMake a bit and then made myself a proper parser testing file.

Thankfully Qt has nice QTest library that can be used to create tests. They can be hard to parse at first, but when you understand how they work, they're really quick and easy to work with.

Testing saved my rear a few times during this project, especially when modifying anything parser related.

Look and feel

When the model worked fine, I started concentrating more on the look and feel of the app.

The application went through various phases: At first, everything was done through a dialog, except viewing the cards. It was kind of distracting that every time you wanted to modify or make new task, the application would pop things up.

Over time though, I think I got it into a good spot: Most actions can be done in the task list, but when user wants to delete a task, the app asks the user first if they are sure about it.

Then there is help menu and quick info for syntax related things.

Otherwise there's really no settings: I did not want to add any settings if I could avoid it. The only setting the app saves to config file is the file you opened last time.

I also consulted the KDE Human Interface Guidelines (HIG) few times, but it was never "in the way." It just helped me to make decisions sometimes when I wasn't sure what to do with the design.

Also my lovely wife @tecsiederp made the application the most adorable icon ever.

Cute green lizard holding a todo sign

Look at it. Just a lil guy.

Also, if you don't get your tasks done, he will get sad. So you better do those tasks!

It just makes me so happy and motivated to work on my tasks when I see this lil guy in my taskbar. :D

The icons however have to follow our guidelines, also mentioned in the HIG. Luckily my wife is good with SVG files, and if she wasn't I already had two people wanting to help me with it, which I very much appreciated. If I had to make an icon myself, it would.. Not be good. :P

Something I mostly struggled with though was getting a nice syntax highlighting for the task text. I wanted to use our KSyntaxHighlighting library, but it would not match all colorschemes: Users may use their own or third party colorscheme for the app, but the syntax highlighter does not have a colorscheme that matches it.. So it would look bit odd.

So I made my own simple one that appends some color tags to the string, and the QML engine does the rest. The text can have HTML tags like <span> in it which QML can parse automagically.

I think the app turned out to look pretty good. It also supports keyboard navigation for getting around the app. (Though there might be some bugs with it still.)

Releasing the app

KDE has documentation for the release process, so I just followed it to the letter. During the release process, I have to wait for at least 2 weeks for people to give me reviews, and so they did. And then I fixed things. And some more.

Eventually the app was good to go (though it was never directly told me, it was just implicitly agreed), and I followed the last bits of relese process, setting up signing keys and such.

And now it exists as a tarball here, so any interested distros can go grab it: https://download.kde.org/stable/komodo/1.0.0/

I also recommend this patch to go with it, to make sure it doesn't install the static libraries in the system: https://invent.kde.org/utilities/komodo/-/commit/57c6fa82719155bd32cb35b4c64cddae956c53e0

With Flathub, the submission process was rather painless in my opinion. I followed their requirements and submission documentation and it all just clicked together. Eventually someone was reviewing my PR and then.. The app is in Flathub. I had to make a patch for Flathub specifically, but it was also really painless so I didn't mind.

What it all mostly took from me is time. Otherwise it was just nice and easy.

More apps?

So at least in my experience, the whole process was rather easy. I didn't want to go into nitty gritty details too much.. Because I don't remember everything + you can see the source code + the process itself happens in the open.

I'm definitely interested in making more KDE apps, but also I have my game project(s).

And I really want to get back into gamedev. Like spend much more time into it.

At least now I have a todo app which fits my needs to keep track of the gamedev projects. :)

Thanks for reading! And if you're looking for new todo application, give KomoDo a try! :)

Monday, 4 August 2025

I write this in the wake of a personal attack against my work and a project that is near and dear to me. Instead of spreading vile rumors and hearsay, talk to me. I am not known to be ‘hard to talk to’ and am wide open for productive communication. I am disheartened and would like to share some thoughts of the importance of communication. Thanks for listening.

Open source development thrives on collaboration, shared knowledge, and mutual respect. Yet sometimes, the very passion that drives us to contribute can lead to misunderstandings and conflicts that harm both individuals and the projects we care about. As contributors, maintainers, and community members, we have a responsibility to foster environments where constructive dialogue flourishes.

The Foundation of Healthy Open Source Communities

At its core, open source is about people coming together to build something greater than what any individual could create alone. This collaborative spirit requires more than just technical skills—it demands emotional intelligence, empathy, and a commitment to treating one another with dignity and respect.

When disagreements arise—and they inevitably will—the manner in which we handle them defines the character of our community. Technical debates should focus on the merits of ideas, implementations, and approaches, not on personal attacks or character assassinations conducted behind closed doors.

The Importance of Direct Communication

One of the most damaging patterns in any community is when criticism travels through indirect channels while bypassing the person who could actually address the concerns. When we have legitimate technical disagreements or concerns about someone’s work, the constructive path forward is always direct, respectful communication.

Consider these approaches:

  • Address concerns directly: If you have technical objections to someone’s work, engage with them directly through appropriate channels
  • Focus on specifics: Critique implementations, documentation, or processes—not the person behind them
  • Assume good intentions: Most contributors are doing their best with the time and resources available to them
  • Offer solutions: Instead of just pointing out problems, suggest constructive alternatives

Supporting Contributors Through Challenges

Open source contributors often juggle their community involvement with work, family, and personal challenges. Many are volunteers giving their time freely, while others may be going through difficult periods in their lives—job searching, dealing with health issues, or facing other personal struggles.

During these times, our response as a community matters enormously. A word of encouragement can sustain someone through tough periods, while harsh criticism delivered thoughtlessly can drive away valuable contributors permanently.

Building Resilient Communities

Strong open source communities are built on several key principles:

Transparency in Communication: Discussions about technical decisions should happen in public forums where all stakeholders can participate and learn from the discourse.

Constructive Feedback Culture: Criticism should be specific, actionable, and delivered with the intent to improve rather than to tear down.

Recognition of Contribution: Every contribution, whether it’s code, documentation, bug reports, or community support, has value and deserves acknowledgment.

Conflict Resolution Processes: Clear, fair procedures for handling disputes help prevent minor disagreements from escalating into community-damaging conflicts.

The Long View

Many successful open source projects span decades, with contributors coming and going as their life circumstances change. The relationships we build and the culture we create today will determine whether these projects continue to attract and retain the diverse talent they need to thrive.

When we invest in treating each other well—even during disagreements—we’re investing in the long-term health of our projects and communities. We’re creating spaces where innovation can flourish because people feel safe to experiment, learn from mistakes, and grow together.

Moving Forward Constructively

If you find yourself in conflict with another community member, consider these steps:

  1. Take a breath: Strong emotions rarely lead to productive outcomes
  2. Seek to understand: What are the underlying concerns or motivations?
  3. Communicate directly: Reach out privately first, then publicly if necessary
  4. Focus on solutions: How can the situation be improved for everyone involved?
  5. Know when to step back: Sometimes the healthiest choice is to disengage from unproductive conflicts

A Call for Better

Open source has given us incredible tools, technologies, and opportunities. The least we can do in return is treat each other with the respect and kindness that makes these collaborative achievements possible.

Every contributor—whether they’re packaging software, writing documentation, fixing bugs, or supporting users—is helping to build something remarkable. Let’s make sure our communities are places where that work can continue to flourish, supported by constructive communication and mutual respect.

The next time you encounter work you disagree with, ask yourself: How can I make this better? How can I help this contributor grow? How can I model the kind of community interaction I want to see?

Our projects are only as strong as the communities that support them. Let’s build communities worthy of the amazing software we create together.

https://gofund.me/506c910c