<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Usability on Ansel</title><link>https://ansel.photos/de/tags/usability/</link><description>Recent content in Usability on Ansel</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>© Copyright 2022-2025 – Aurélien Pierre</copyright><lastBuildDate>Sun, 01 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://ansel.photos/de/tags/usability/index.xml" rel="self" type="application/rss+xml"/><item><title>Rewriting keyboard shortcuts (accelerators) from scratch</title><link>https://ansel.photos/de/news/rewriting-key-shortcuts/</link><pubDate>Sun, 01 Jun 2025 00:00:00 +0000</pubDate><updated>Sun, 01 Jun 2025 00:00:00 +0000</updated><guid>a41c3cc703d9946e775621178f72f47550341638caf5354d7f1635e6eccb815c</guid><description>&lt;p>In my defining post,
&lt;a href="https://ansel.photos/de/news/darktable-dans-le-mur-au-ralenti/" title="" rel="dofollow" >&lt;em>Darktable: crashing into the wall in slow-motion&lt;/em>&lt;/a>, I presented the trainwreck that the new &amp;ldquo;Great MIDI turducken&amp;rdquo; was. The purpose of this turducken&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> was to rewrite the keyboard shortcuts system to extend it for MIDI devices.&lt;/p>
&lt;p>To this day, I&amp;rsquo;m still mad about this enterprise of mass-destruction, here is a recap of he the reasons:&lt;/p>
&lt;ol>
&lt;li>it replaced in 2021 a keyboard shortcuts system that was pretty good, feature-complete, well tested, stable and coded in less than &lt;strong>1500 lines&lt;/strong> (comments included),&lt;/li>
&lt;li>…to add support for MIDI devices and PlayStation gamepads (!?!)…&lt;/li>
&lt;li>…but in my 2022 Darktable survey, one year after this new feature, over 1251 users who participated:
&lt;ul>
&lt;li>81% of users didn&amp;rsquo;t have a MIDI device and didn&amp;rsquo;t plan to get one,&lt;/li>
&lt;li>2% didn&amp;rsquo;t even know what a MIDI device was.&lt;/li>
&lt;li>8% of users had a MIDI device but didn&amp;rsquo;t use it with Darktable,&lt;/li>
&lt;li>6% were considering &lt;em>maybe&lt;/em> getting a MIDI device in the future,&lt;/li>
&lt;li>&lt;strong>2% of users had a MIDI device they actually used in Darktable&lt;/strong>,&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>the code was absolutely terrible, in terms of:
&lt;ul>
&lt;li>code quality: unlegible &lt;code>if&lt;/code>/&lt;code>switch-case&lt;/code> statements nested on 4 levels, in the middle of 1000-lines functions (I posted example snippets in my
&lt;a href="https://ansel.photos/de/news/darktable-dans-le-mur-au-ralenti/#keyboard-shortcuts" title="" rel="dofollow" >article&lt;/a>),&lt;/li>
&lt;li>code volume:
&lt;ul>
&lt;li>&lt;strong>3546 lines&lt;/strong> of code for Darktable 4.0,&lt;/li>
&lt;li>&lt;strong>4397 lines&lt;/strong> of code for Darktable 5.0,&lt;/li>
&lt;li>the increase in volume is a direct consequence of trying to fix bugs in an architecture that can&amp;rsquo;t be fixed because its complexity promotes more complexity. All that stems from the design, but solving issues created by complexity with adding more complexity is not a solution.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>code complexity:
&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity" title="Wikipedia link" target="_blank" rel="noopener noreferrer nofollow" >cyclomatic complexity&amp;thinsp;&lt;sup class="icon">&lt;i class="fab fa-wikipedia-w">&lt;/i>&lt;/sup>&lt;/a>:
&lt;ul>
&lt;li>&lt;strong>1088&lt;/strong> for Darktable 4.0,&lt;/li>
&lt;li>&lt;strong>1245&lt;/strong> for Darktable 5.0 (&lt;a href="https://sonarcloud.io/component_measures?id=aurelienpierreeng_darktable-5&amp;amp;metric=complexity&amp;amp;view=list" title="External link" target="_blank" rel="noopener noreferrer nofollow" >details&amp;thinsp;&lt;sup class="icon">&lt;i class="fa fa-external-link-alt">&lt;/i>&lt;/sup>&lt;/a>),&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://www.sonarsource.com/blog/cognitive-complexity-because-testability-understandability/" title="External link" target="_blank" rel="noopener noreferrer nofollow" >cognitive complexity&amp;thinsp;&lt;sup class="icon">&lt;i class="fa fa-external-link-alt">&lt;/i>&lt;/sup>&lt;/a>:
&lt;ul>
&lt;li>&lt;strong>1885&lt;/strong> for Darktable 4.0,&lt;/li>
&lt;li>&lt;strong>2098&lt;/strong> for Darktable 5.0 (&lt;a href="https://sonarcloud.io/component_measures?metric=cognitive_complexity&amp;amp;view=list&amp;amp;id=aurelienpierreeng_darktable-5" title="External link" target="_blank" rel="noopener noreferrer nofollow" >details&amp;thinsp;&lt;sup class="icon">&lt;i class="fa fa-external-link-alt">&lt;/i>&lt;/sup>&lt;/a>).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>it is by far the most complex feature of the software, even though it does not operate on images. As a comparison, the second most complex feature is the EXIF metadata decoding, which has a cognitive complexity of &lt;strong>1348&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>it doesn&amp;rsquo;t decode key modifiers &lt;strong>by design&lt;/strong>, but only deals with hardware key-strokes, which means:
&lt;ul>
&lt;li>&amp;ldquo;1&amp;rdquo; input from the numeric pad is decoded &lt;code>Keypad End&lt;/code>,&lt;/li>
&lt;li>&amp;ldquo;1&amp;rdquo; input from a French AZERTY keyboard is decoded &lt;code>Shift+&amp;amp;&lt;/code>, or &lt;code>Shift+&amp;quot;&lt;/code> on BÉPO,&lt;/li>
&lt;li>you therefore need to duplicate all your number-based shortcuts for each way of entering a number, and be prepared for the shortcut settings window to not contain any actual number in the key combinations.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>the user-end design is absolutely terrible, with way too many actions and emulations to configure (&amp;ldquo;effects&amp;rdquo;), that are not even fully documented 4 years later (what is &amp;ldquo;ctrl-toggle&amp;rdquo; ? &amp;ldquo;right-activate&amp;rdquo; ?), and the shortcut configuration uses a weird split-window that doesn&amp;rsquo;t make any sense,&lt;/li>
&lt;li>the implementation is also terrible: the feature is aware of all the software GUI, and the software GUI is aware of the shortcuts code. There is no modularity here, and changing anything in the shortcuts code may have unexpected and undesired effect anywhere in the software.&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> Just see the dependency graph below,&lt;/li>
&lt;li>several &amp;ldquo;shortcuts&amp;rdquo; (or MIDI bindings) can be attached to the same action, which means every user interaction has to lookup the whole list of available actions, inducing very inefficient shortcut handling, GUI lags in some cases and &amp;ldquo;unknown key combination&amp;rdquo; false positives in peculiar cases.&lt;/li>
&lt;/ol>
&lt;figure class="img-wrapper caption-figure" id="shortcuts-brokenpng">
&lt;button class="open-lightbox-button" onclick="lightbox(&amp;#34;shortcuts-brokenpng&amp;#34;)">
&lt;i class="fa fa-expand-alt open-lightbox-icon fa-fw">&lt;/i>
&lt;/button>
&lt;button class="open-link-button" onclick="window.open('https:\/\/ansel.photos\/shortcuts-broken.png', '_blank');">
&lt;i class="fa fa-external-link-alt open-link-icon fa-fw">&lt;/i>
&lt;/button>
&lt;div class="img-container" onclick="lightbox(&amp;#34;shortcuts-brokenpng&amp;#34;)" style="cursor: pointer;" >
&lt;img src=https://ansel.photos/shortcuts-broken.png alt="image"
style=" "
decoding="async" loading="lazy"sizes="(max-width: 992px) 100vw, (max-width: 1200px) 66.67vw, 50vw" srcset="https://ansel.photos/shortcuts-broken_hu_37da4ddfba37297.png 140w, https://ansel.photos/shortcuts-broken_hu_2ca56332c4a639fc.png 264w, https://ansel.photos/shortcuts-broken_hu_8894f2c5978674fe.png 331w, https://ansel.photos/shortcuts-broken_hu_bc265109b1e12781.png 400w, https://ansel.photos/shortcuts-broken_hu_d9d6e746b11eb2e5.png 496w, https://ansel.photos/shortcuts-broken_hu_ef3f427f08f0988e.png 576w, https://ansel.photos/shortcuts-broken_hu_42b4918b27cbec62.png 600w, https://ansel.photos/shortcuts-broken_hu_c18353f82cd93b8b.png 720w, https://ansel.photos/shortcuts-broken_hu_f875cf7401de6b2b.png 800w, https://ansel.photos/shortcuts-broken_hu_329e0f33193bcc21.png 992w, https://ansel.photos/shortcuts-broken_hu_3e979df69b871fbc.png 1200w, https://ansel.photos/shortcuts-broken_hu_5fce2b3bbfaa9bba.png 1320w, https://ansel.photos/shortcuts-broken_hu_324828f59d54c82b.png 1440w, https://ansel.photos/shortcuts-broken_hu_1af3de6d2fb4e36e.png 1600w, https://ansel.photos/shortcuts-broken_hu_8d207886803761a.png 1920w, https://ansel.photos/shortcuts-broken_hu_7ac56fd0b2cf2d5c.png 2048w"width="2508" height="1674"/>
&lt;/div>
&lt;figcaption>
Non-decoded number keys and weird window splitting between "action" and "shortcut".
&lt;/figcaption>
&lt;/figure>
&lt;figure class="img-wrapper caption-figure" id="accelerators-beforejpg">
&lt;button class="open-lightbox-button" onclick="lightbox(&amp;#34;accelerators-beforejpg&amp;#34;)">
&lt;i class="fa fa-expand-alt open-lightbox-icon fa-fw">&lt;/i>
&lt;/button>
&lt;button class="open-link-button" onclick="window.open('https:\/\/ansel.photos\/accelerators-before.jpg', '_blank');">
&lt;i class="fa fa-external-link-alt open-link-icon fa-fw">&lt;/i>
&lt;/button>
&lt;div class="img-container" onclick="lightbox(&amp;#34;accelerators-beforejpg&amp;#34;)" style="cursor: pointer;" >
&lt;img src=https://ansel.photos/accelerators-before.jpg alt="image"
style=" "
decoding="async" loading="lazy"sizes="(max-width: 992px) 100vw, (max-width: 1200px) 66.67vw, 50vw" srcset="https://ansel.photos/accelerators-before_hu_7bfdceacd2bb7c89.jpg 140w, https://ansel.photos/accelerators-before_hu_d7938822cb09ca35.jpg 264w, https://ansel.photos/accelerators-before_hu_fcf7d3d779c1e468.jpg 331w, https://ansel.photos/accelerators-before_hu_e21dbd9378dd11aa.jpg 400w, https://ansel.photos/accelerators-before_hu_deb5f0ca868df06f.jpg 496w, https://ansel.photos/accelerators-before_hu_4bb52b8bdd9be52c.jpg 576w, https://ansel.photos/accelerators-before_hu_69881a278551351a.jpg 600w, https://ansel.photos/accelerators-before_hu_22c484c4dbe786e6.jpg 720w, https://ansel.photos/accelerators-before_hu_966ee3fdb6fc09ce.jpg 800w, https://ansel.photos/accelerators-before_hu_532d40704d99e3eb.jpg 992w, https://ansel.photos/accelerators-before_hu_67b7df50fe19e136.jpg 1200w, https://ansel.photos/accelerators-before_hu_543c1dc8e601743b.jpg 1320w, https://ansel.photos/accelerators-before_hu_d59a4cf0b04746dc.jpg 1440w, https://ansel.photos/accelerators-before_hu_a6ec2bd03f3faac7.jpg 1600w, https://ansel.photos/accelerators-before_hu_bccb1b41efcab977.jpg 1920w, https://ansel.photos/accelerators-before_hu_acdb628243edd870.jpg 2048w"width="3730" height="1912"/>
&lt;/div>
&lt;figcaption>
&lt;p>The dependency graph of &lt;code>src/gui/accelerators.c&lt;/code> (Great MIDI turducken) before the rewrite. Guess why we call it &amp;ldquo;&lt;a href="https://en.wikipedia.org/wiki/Spaghetti_code" title="Wikipedia link" target="_blank" rel="noopener noreferrer nofollow" >spaghetti code&amp;thinsp;&lt;sup class="icon">&lt;i class="fab fa-wikipedia-w">&lt;/i>&lt;/sup>&lt;/a>&amp;quot;… This makes it clear that there is a double-sided dependency between the accels code and the rest of the GUI code. This is a nightmare to maintain.&lt;/p></description></item></channel></rss>