I am used to the Mac OS dictionary widget. When I moved to KDE, I was glad to add a dictionary widget to the system tray. A global key shortcut (Shift+Win+D) made it even better and faster to use.

Still, a small problem needed a fix: the input field did not focus automatically and I had to use touchpad to click it, which is quite distracting. My ideal dictionary applet workflow is: reading a text, spotting an unknown/unclear word, pressing Shift+Win+D, typing the word in (typing is good for remembering!), getting results on Enter, Esc, get back to the text.

Hey, it’s the Linux tinkering time again!

Disclaimer: I am not an expert in KDE by any means, take the recipes with a grain of salt; if you are, please point out what can be done better.

Finding the source for the widget

I worked with Qt a little, a long time ago, so I knew about QML: it’s a fun little declarative language on top of Qt used to describe interfaces and UI actions. Turns out, you can write whole widgets in it!

Let’s find the source of the dictionary widget then.

After a few guesses (plasmoid? widget? dict?), dpkg --list | grep (I am using Kubuntu) gave me this:

$ dpkg --list | grep plasma
...
ii  plasma-widgets-addons   4:5.12.6-0ubuntu0.1    amd64    additional widgets for Plasma 5
...

Looks like the dictionary applet might be in there:

$ dpkg -L plasma-widgets-addons | grep dict
...
/usr/share/plasma/plasmoids/org.kde.plasma_applet_dict
...

Right. Examining the source showed that the applet UI is described here: /usr/share/plasma/plasmoids/org.kde.plasma_dict_applet/contents/ui/main.qml.

Looking for the focus event

Now it was time to actually fix the problem: get the text field (input) to focus every time the plasmoid is shown.

First, I tried to do it the right way: reading the documentation, understanding QML events and layouts, etc.

Googling “QML widget events” led me to the official QML documentation, which looks pretty solid and systematic. Sadly, I had spent a couple of hours trying to find anything relevant in the dump of definitions of methods and properties (scattered over super- and supersuperclasses of TextField and ColumnLayout), trying to grep through tutorials and StackOverflow – before giving up.

Then I had a better idea: installing and running QtCreator, opening the main.qml file and just stupidly trying a few completions here and there. This proved to be surprisingly effective and trying a few on completions found the onActiveFocusChanged callback and .focus property of the input element:

ColumnLayout {
    onActiveFocusChanged: input.focus = true
    
    ...
}

Now, let’s test the changes:

$ pkill plasmashell ; kstart5 plasmashell

Nice, Shift+Win+D now showed the dictionary widget with the search field focused.

A note: it’s better to keep the modified main.qml in a safe place and copy it in /usr/share/..., because it might be rewritten by a new system update. I omitted copying it for simplicity.

Making the previous search selected

Another annoying usability problem remained: now the search field was focused, but contained the previous search in it, unselected, so I had to be careful: if there was a word in the search field, I had to erase it first (Shift+Backspace) or I would just append a new word to its end. It was not a big deal, but it introduced some state I needed to track every time and any state is distracting (Vim is still a holy cow that is above any whining about modes)!

The problem could be solved by clearing the field every time the widget was hidden, but sometimes you just want to see the previous word, not to look up a new one. Selecting the previous search when the widget is shown solves this: the previous search is here, but typing anything replaces it.

This time the documentation was more helpful:

ColumnLayout {
    onActiveFocusChanged: {
        input.selectAll()
        input.focus = true
    }
    
    ...
}

Linux is still DIY (Design It Yourself) sometimes!

While we’re at it, why not fix the annoying font and background color choice with my crazy design skills? I find it annoying and not as streamlined as the Mac widget, don’t you (let the flame war begin!)?

Trying to tinker with QML Font property was not successful. WebEngineView is obviously a piece of web that listens only to CSS. Quick googling shows that there is no easy way to style WebEngineView other than running some Javascript (I will be glad to be proved wrong here!):

ColumnLayout {
    ...
    WebEngineView: {
        id: web
        ...
        onLoadingChanged: {
            web.runJavaScript(
                "document.body.style['font-family'] = 'Lato, monospace'; " +
                "document.body.style['line-height'] = '1.5'; " +
                "document.body.style['background-color'] = '#f9f7e0'; "
            )
        }
    }
}

Now it was much more palatable to me:

Conclusions

  • The KDE dictionary widget is very basic, quick and dirty, and could use some love and usability testing. On the other hand, I am satisfied with my tweaks and resulting usability (should I contribute that upstream?).

  • KDE widgets are small, fun and flexible (for a programmer) and fixing them does not require to recompile half of KDE.

  • QtCreator was more helpful than the docs, the QML documentation is a OOP mess.