#107 Open

Bitbucket cannot automatically merge this request.

The commits that make up this pull request have been removed.

Bitbucket cannot automatically merge this request due to conflicts.

Review the conflicts on the Overview tab. You can then either decline the request or merge it manually on your local system using the following commands:

git checkout master
git remote add syrja/speedcrunch https://bitbucket.org/syrja/speedcrunch.git
git fetch syrja/speedcrunch
git merge --no-ff -m 'Merged in syrja/speedcrunch/keypad (pull request #107)' remotes/syrja/speedcrunch/keypad
  1. Mikko Syrjä

Hello again,

SpeedCrunch is traditionally keyboard based application and this is mostly fine. However, mobile and touchscreen devices need better on-screen keyboard. Also, users have different requirements. Many may never need traditional sin/cos/tan keys. So, let's make keypad configurable.

Let's start with configuration file. It uses json format and the new default desktop keypad definition file (Current.json) looks like that:

  "editkey": { },
  "leftpad": { },
  "rightpad": { },
          { "label": "7", "value": "", "second": "", "tip": "", "color": false, "bold": true },
          { "label": "8", "value": "", "second": "", "tip": "", "color": false, "bold": true },
          { "label": "9", "value": "", "second": "j", "tip": "", "color": false, "bold": true },
          { "label": "÷", "value": "/", "second": "\\", "tip": "", "color": false, "bold": true },
          { "label": "√", "value": "sqrt()", "second": "cbrt()", "tip": "", "color": false, "bold": false },
          { "label": "π", "value": "pi", "second": "", "tip": "", "color": false, "bold": false },
          { "label": "exp", "value": "exp()", "second": "10^", "tip": "", "color": false, "bold": false },
          { "label": "ln", "value": "ln()", "second": "lg()", "tip": "", "color": false, "bold": false },
          { "label": "(", "value": "", "second": "", "tip": "", "color": false, "bold": false },
          { "label": ")", "value": "", "second": "", "tip": "", "color": false, "bold": false }
          { "label": "4", "value": "", "second": "D", "tip": "", "color": false, "bold": true },
          { "label": "5", "value": "", "second": "E", "tip": "", "color": false, "bold": true },
          { "label": "6", "value": "", "second": "F", "tip": "", "color": false, "bold": true },
          { "label": "×", "value": "", "second": "", "tip": "", "color": false, "bold": true },
          { "label": "x²", "value": "^2", "second": "^", "tip": "", "color": false },
          { "label": "e", "value": "e", "second": "", "tip": "", "color": false, "bold": false },
          { "label": "sin", "value": "sin()", "second": "sinh()", "tip": "", "color": false, "bold": false },
          { "label": "arcsin", "value": "arcsin()", "second": "arsinh()", "tip": "", "color": false, "bold": false },
          { "label": "➔", "value": "->", "second": "", "tip": "", "color": false, "bold": false },
          { "label": "ans", "value": "ans", "second": "", "tip": "", "color": false, "bold": false }
          { "label": "1", "value": "", "second": "A", "tip": "", "color": false, "bold": true },
          { "label": "2", "value": "", "second": "B", "tip": "", "color": false, "bold": true },
          { "label": "3", "value": "", "second": "C", "tip": "", "color": false, "bold": true },
          { "label": "-", "value": "", "second": "", "tip": "", "color": false, "bold": true },
          { "label": "1/x", "value": "1/", "second": "", "tip": "", "color": false, "bold": false },
          { "label": "0x", "value": "", "second": "0b", "tip": "", "color": false, "bold": false },
          { "label": "cos", "value": "cos()", "second": "cosh()", "tip": "", "color": false, "bold": false },
          { "label": "arccos", "value": "arccos()", "second": "arcosh()", "tip": "", "color": false, "bold": false },
          { "label": "x", "value": "", "second": "y", "tip": "", "color": false, "bold": false },
          { "label": "x=", "value": "=", "second": "(x)=", "tip": "", "color": false, "bold": false }
          { "label": "0", "value": "", "second": "°", "tip": "", "color": false, "bold": true },
          { "label": ",", "value": "", "second": "'", "tip": "", "color": false, "bold": true },
          { "label": ";", "value": "", "second": ":", "tip": "", "color": false, "bold": true },
          { "label": "+", "value": "", "second": "", "tip": "", "color": false, "bold": true },
          { "label": "n!", "value": "!", "second": "", "tip": "", "color": false, "bold": false },
          { "label": "&", "value": "", "second": "|", "tip": "", "color": false, "bold": false },
          { "label": "tan", "value": "tan()", "second": "tanh()", "tip": "", "color": false, "bold": false },
          { "label": "arctan", "value": "arctan()", "second": "artanh()", "tip": "", "color": false, "bold": false },
          { "label": "C", "value": "<clear>", "second": "", "tip": "", "color": false, "bold": false },
          { "label": "=", "value": "<evaluate>", "second": "ans", "tip": "", "color": true, "bold": true }

Objects editkey, leftpad and rightpad are used by mobile systems. Object editkey is the single button beside the edit field. Objects leftpad and rightpad are left and right portrait mode keypads.Object landscape is keypad used in landscape mode. Desktop SpeedCrunch uses only landscape object. In each keypad object, array 'rows' contains keypad row objects and within these rows array 'keys' contains objects with individual key information.

Each key object has following members:

“label” Text visible in button
”value” Actual text inserted into the edit field, empty defaults to label
”second” Secondary value available with long key press, empty defaults to primary value
”tip” Button tooltip text, currently only in desktop systems, defaults to value
”color” True for highlighted backgroud, currently only in mobile systems
”bold” True for bold text, works in both mobile and desktop systems

By default, the value field is inserted into the edit field. However, there are some special command values defined with angle brackets:

<evaluate> Evaluates the expression, for use with the equal key
<left> Moves edit field cursor left, for use with left arrow key
<start> Moves edit field cursor to the beginning, for use with left arrow key long press
<right> Moves edit field cursor right, for use with right arrow key
<end> Moves edit field cursor to the end, for use with right arrow key long press
<back> Removes character before caret, for use with backspace key
<clear> Clears whole edit field, for use with backspace key long press

Desktop system installs four keyboards: Classic, Current, Horizontal and Super. Classic is same as default keypad in previous versions. Current is above described improved version with some additions. Horizontal keyboard uses horizontal numeric key layout and Super has one additional row for more functions. These are currently just for testing. Better ones can be created for official release.

Mobile systems install also four keyboards: Classic, Current, Gemini and Tablet. Classic is same as keyboard used in previous mobile versions. Current is similar, but improved version with some additions. Gemini has two row landsacpe mode for use with Planet Computers Gemini/Cosmo or other devices with physical keyboard containing number keys. Tablet keyboard has additional column in portrait mode and additional row in landscape mode.

Amount of rows and columns behave differently depending on platform. In all systems button height is fixed. In desktop systems button width is determined by the widest button label and new rows increase keyboard height. In mobile systems button width is calculated by dividing screen width with the amount of buttons. In mobile landscape mode keyboard height behaves like in desktop: new rows increase keyboard height. In mobile portrait mode keyboard height is currently fixed to five rows. This is supposed to match with device virtual keyboard height, but will need more adjustment.

Tooltips are displayed in desktop systems. If button value is function, tooltip is its localized name (e.g. arctan() tooltip is "Arkustangentti" in finnish). Otherwise, tooltip is just button value. This means that key √ has tooltip explanation, because it is actually function sqrt(), but key n! has not, because it is just an operator. Maybe operators could have localisations like functions. If button has secondary long press value, tooltip displays second row with the same rule. Old desktop fixed keypad tooltip localisations in Keypad::setButtonTooltips() are removed. Angle bracket special commands (e.g. <left> or <clear>) will need localisation later.

Keyboard configuration files in desktop systems are installed like color scheme files and searched with same rules. User defined keyboards in Linux are searched from ~/.local/share/SpeedCrunch/keyboards/ and in Windows from C:/Users/<USERNAME>/AppData/Roaming/SpeedCrunch/keyboards/. These directories can be used for testing without installation by copying keyboard files there from the source directory speedcrunch/src/resources/keyboards/.

In Sailfish systems user settings are stored in ~/.config/harbour-speedcrunch/ and keyboards use subdirectory under it just like in desktop systems. Because Sailfish is more or less standard Linux system, there are both command line and GUI tools available for configuration file handling.

In Android systems things are little bit more complicated. User configuration is stored in /data/data/org.syrja.speedcrunch/files/settings/libandroid-speedcrunch.so/. This directory comes from QStandardPaths::ConfigLocation and is not accessible by other programs without rooting. So, manual configuration of files is not possible. To make user keyboards accessible QStandardPaths::GenericDataLocation is used instead. It maps to /storage/emulated/0 or something like that depending on the device. User defined keyboards are stored under it in subdirectory Android/data/org.syrja.speedcrunch/files/keyboards/, which can be accessed by normal Android file managers.

Currently known problems:

  • Decimal separator button label localization (point/comma) does not work:

    • Possible implementation: "label": "<separator>"
  • No context specific labels in mobile systems:

    • Key label "1" is not changed to "1 A" in hexadecimal mode.
    • Have not yet figured out good system for this, too many keys and possible modes.
  • Windows touchscreen long press does not work:

    • Tested with Windows 10 and Dell XPS 15, seems to bring up empty context menu.
    • Linux touchscreen in OpenSuse Tumbleweed, KDE and Dell XPS 13 works.
    • Under investigation, maybe popup context menus in all systems?
  • Have no idea if any of this works in Mac systems, will need someone for testing.

  • Keyboard files are not yet included in desktop installation packages.
  • No documentation yet, but will be added after everything works.

Because of multi platform nature of this thing, I will continue to document also mobile changes here. Some of them may bring up ideas for desktop too.

Comments (10)

  1. Helder Correia repo owner

    Hi, Mikko. Thanks for doing this, great. I need some more time in order to review this properly, sorry. BTW, it seems that you need to fix the merge conflicts.

  2. Mikko Syrjä author

    Updated the pull request with dynamic key label support. Key label values can now contain some predefined tags which are replaced after loading the keyboard. Their main purpose is to show secondary (long press) options and in most cases they just replace the label. However, if values are left empty, they can also change the meaning (like [XO]).

    Currently supported labels:

    [1H] normally "1", in hexadecimal mode "1 A"
    [2H] normally "2", in hexadecimal mode "1 B"
    [3H] normally "3", in hexadecimal mode "1 C"
    [4H] normally "4", in hexadecimal mode "1 D"
    [5H] normally "5", in hexadecimal mode "1 E"
    [6H] normally "6", in hexadecimal mode "1 F"
    [9C] normally "9", in complex number mode "9 j"
    [0S] normally "0", in sexagecimal mode "0 °"
    [.S] normally ".", in sexagecimal mode ". '"
    [,S] normally ",", in sexagecimal mode ", '"
    [;S] normally ";", in sexagecimal mode "; :"
    normally "0x", in octal mode "0o"
    [.,] or [,.] radix character from settings

    Classic keyboard uses [.,] for radix character, all new keyboards use sexagecimal version [.S]. Except the radix character, all others work also in mobile versions.

  3. Helder Correia repo owner

    Hi, @Mikko Syrjä . I’ve checkout out your branch and I get this compilation error:

    /home/helder/dev/speedcrunch-test/speedcrunch/src/gui/mainwindow.cpp: In member function ‘void MainWindow::handleKeypadButtonPress(QString)’:
    /home/helder/dev/speedcrunch-test/speedcrunch/src/gui/mainwindow.cpp:2181:19: error: ‘class QString’ has no member named ‘back’
             if (value.back() == ')') {

  4. Mikko Syrjä author

    Finally figured out how to turn off press and hold popup in Windows touchscreens. Now button secondary values work correctly with long press.

    Also added initial sphinx documentation for user defined keyboards.

  5. Helder Correia repo owner

    @Mikko Syrjä Have you created a ticket for this feature? I don’t see a linked one in the PR.