Skip to content

Commit 8c6752f

Browse files
committed
UI: experimental keypad to replace android keypad
1 parent 2709c3f commit 8c6752f

File tree

8 files changed

+260
-46
lines changed

8 files changed

+260
-46
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ set(SOURCES
185185
${UI_DIR}/textedit.cpp
186186
${UI_DIR}/keypad.cpp
187187
${UI_DIR}/image.cpp
188+
${UI_DIR}/lodepng_codec.cpp
188189
${UI_DIR}/strlib.cpp
189190
${UI_DIR}/audio.cpp
190191
${SDL_DIR}/main.cpp

images/keypad/build.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
3+
declare -a IMAGE_FILES=(\
4+
"proicons-cut.png"\
5+
"proicons-copy.png"\
6+
"proicons-clipboard-paste.png"\
7+
"proicons-save.png"\
8+
"proicons-bug-play.png"\
9+
"proicons-book-info-2.png"\
10+
"proicons-backspace.png"\
11+
"proicons-arrow-enter.png"\
12+
)
13+
14+
echo "#pragma once" > keypad-icons.h
15+
echo "// https://procode-software.github.io/proicons/icons" >> keypad-icons.h
16+
for imageFile in "${IMAGE_FILES[@]}"
17+
do
18+
name=`ls -1 ${imageFile} | sed -e 's/proicons-/img-/' | sed -e 's/\.png//'`
19+
xxd -n ${name} -i ${imageFile} >> keypad-icons.h
20+
done

src/platform/sdl/editor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ void System::editSource(String loadPath, bool restoreOnExit) {
229229
_output->clearScreen();
230230
_output->addInput(editWidget);
231231
_output->addInput(helpWidget);
232-
_output->addInput(new KeypadInput(false, true));
232+
_output->addInput(new KeypadInput(false, false, charWidth, charHeight));
233233

234234
// to layout inputs
235235
_output->resize(w, h);

src/ui/image_codec.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// This file is part of SmallBASIC
2+
//
3+
// Copyright(C) 2001-2025 Chris Warren-Smith.
4+
//
5+
// This program is distributed under the terms of the GPL v2.0 or later
6+
// Download the GNU Public License (GPL) from www.gnu.org
7+
//
8+
9+
#ifndef IMG_CODEC_H
10+
#define IMG_CODEC_H
11+
12+
#include "config.h"
13+
#include <cstddef>
14+
#include <cstdint>
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
20+
struct ImageData {
21+
ImageData();
22+
~ImageData();
23+
24+
unsigned _width;
25+
unsigned _height;
26+
uint8_t *_pixels;
27+
};
28+
29+
//
30+
// Decode IMG to raw RGBA image
31+
// Caller allocates and passes in imgData and its size.
32+
// The function allocates ImageData.pixels (must be freed with img_free).
33+
//
34+
int img_decode(const uint8_t *imgData, size_t imgSize, ImageData *outImage);
35+
36+
//
37+
// Encode raw RGBA image to IMG
38+
// Caller allocates and passes in image.
39+
// The function allocates *outImgData which must be freed.
40+
//
41+
int img_encode(const ImageData *image, uint8_t **outImgData, size_t *outImgSize);
42+
43+
//
44+
// Get last error string
45+
//
46+
const char *img_get_last_error(void);
47+
48+
#ifdef __cplusplus
49+
}
50+
#endif
51+
52+
#endif // IMG_CODEC_H

src/ui/keypad.cpp

Lines changed: 83 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "common/sbapp.h"
1111
#include "common/keymap.h"
1212
#include "lib/maapi.h"
13+
#include "ui/image_codec.h"
14+
#include "ui/keypad-icons.h"
1315
#include "keypad.h"
1416

1517
constexpr char cutIcon[] = {'\346', '\0'};
@@ -95,6 +97,41 @@ constexpr int rowCharLengths[][5] = {
9597
{6, 10, 8, 14, 0}, // symbols
9698
};
9799

100+
//
101+
// KeypadDrawContext
102+
//
103+
KeypadDrawContext::KeypadDrawContext(int charWidth, int charHeight) :
104+
_charWidth(charWidth),
105+
_charHeight(charHeight),
106+
_shiftActive(false),
107+
_capsLockActive(false) {
108+
if (img_decode(img_cut, img_cut_len, &_cutImage) != 0 ||
109+
img_decode(img_copy, img_copy_len, &_copyImage) != 0 ||
110+
img_decode(img_clipboard_paste, img_clipboard_paste_len, &_pasteImage) != 0 ||
111+
img_decode(img_save, img_save_len, &_saveImage) != 0 ||
112+
img_decode(img_bug_play, img_bug_play_len, &_runImage) != 0 ||
113+
img_decode(img_book_info_2, img_book_info_2_len, &_helpImage) != 0 ||
114+
img_decode(img_backspace, img_backspace_len, &_backImage) != 0 ||
115+
img_decode(img_arrow_enter, img_arrow_enter_len, &_enterImage) != 0) {
116+
deviceLog("Failed to load image [%s]\n", img_get_last_error());
117+
}
118+
}
119+
120+
void KeypadDrawContext::toggleShift() {
121+
_shiftActive = !_shiftActive;
122+
}
123+
124+
bool KeypadDrawContext::useShift(bool specialKey) {
125+
bool useShift = _shiftActive ^ _capsLockActive;
126+
if (_shiftActive && !specialKey) {
127+
_shiftActive = false;
128+
}
129+
return useShift;
130+
}
131+
132+
//
133+
// Key
134+
//
98135
Key::Key(const RawKey &k) :
99136
_label(k._normal),
100137
_altLabel(k._shifted) {
@@ -118,7 +155,19 @@ int Key::color(const KeypadTheme *theme, bool shiftActive) const {
118155
return result;
119156
}
120157

121-
void Key::drawButton(const KeypadTheme *theme) const {
158+
void Key::drawImage(const ImageData *image) const {
159+
MAPoint2d dstPoint;
160+
MARect srcRect;
161+
dstPoint.x = _x;
162+
dstPoint.y = _y;
163+
srcRect.left = 0;
164+
srcRect.top = 0;
165+
srcRect.width = image->_width;
166+
srcRect.height = image->_height;
167+
maDrawRGB(&dstPoint, image->_pixels, &srcRect, 0, image->_width);
168+
}
169+
170+
void Key::draw(const KeypadTheme *theme, const KeypadDrawContext *context) const {
122171
int rc = 5;
123172
int pad = 2;
124173
int rx = _x + _w - pad; // right x
@@ -151,6 +200,26 @@ void Key::drawButton(const KeypadTheme *theme) const {
151200
maArc(xcR, ycT, rc, PI * 3 / 2, 0, aspect); // Top-right corner
152201
maArc(xcR, ycB, rc, 0, PI / 2, aspect); // Bottom-right corner
153202
maArc(xcL, ycB, rc, PI / 2, PI, aspect); // Bottom-left corner
203+
204+
String label;
205+
if (_special) {
206+
label = _label;
207+
} else {
208+
bool useShift = context->_shiftActive ^ context->_capsLockActive;
209+
label = useShift ? _altLabel : _label;
210+
}
211+
212+
int xOffset = (_w - (context->_charWidth * _labelLength)) / 2;
213+
int yOffset = (_h - context->_charHeight) / 2;
214+
int textX = _x + xOffset;
215+
int textY = _y + yOffset;
216+
maSetColor(color(theme, context->_shiftActive));
217+
218+
if (_special && _labelLength == 1) {
219+
drawImage(&context->_enterImage);
220+
} else {
221+
maDrawText(textX, textY, label.c_str(), _labelLength);
222+
}
154223
}
155224

156225
bool Key::inside(int x, int y) const {
@@ -210,10 +279,7 @@ Keypad::Keypad(int charWidth, int charHeight)
210279
_posY(0),
211280
_width(0),
212281
_height(0),
213-
_charWidth(charWidth),
214-
_charHeight(charHeight),
215-
_shiftActive(false),
216-
_capsLockActive(false),
282+
_context(charWidth, charHeight),
217283
_theme(&modernDarkTheme),
218284
_currentLayout(LayoutLetters) {
219285
generateKeys();
@@ -256,17 +322,20 @@ void Keypad::layout(int x, int y, int w, int h) {
256322
_width = w;
257323
_height = h;
258324

325+
const int charWidth = _context._charWidth;
326+
const int charHeight = _context._charHeight;
327+
259328
// start with optimum padding, then reduce to fit width
260329
int padding = defaultPadding;
261-
int width = maxCols * (_charWidth + (padding * 2));
330+
int width = maxCols * (charWidth + (padding * 2));
262331

263332
while (width > w && padding > 0) {
264333
padding--;
265-
width = maxCols * (_charWidth + (padding * 2));
334+
width = maxCols * (charWidth + (padding * 2));
266335
}
267336

268-
int keyW = _charWidth + (padding * 2);
269-
int keyH = _charHeight + (padding * 2);
337+
int keyW = charWidth + (padding * 2);
338+
int keyH = charHeight + (padding * 2);
270339
int xStart = _posX + ((w - width) / 2);
271340
int yPos = _posY;
272341
int index = 0;
@@ -277,7 +346,7 @@ void Keypad::layout(int x, int y, int w, int h) {
277346
int xPos = xStart;
278347
if (cols < maxCols) {
279348
// center narrow row
280-
int rowWidth = (chars * _charWidth) + (cols * padding * 2);
349+
int rowWidth = (chars * charWidth) + (cols * padding * 2);
281350
if (rowWidth > width) {
282351
xPos -= (rowWidth - width) / 2;
283352
} else {
@@ -292,7 +361,7 @@ void Keypad::layout(int x, int y, int w, int h) {
292361
int length = key->_labelLength;
293362
int keyWidth = keyW;
294363
if (length > 1) {
295-
keyWidth = (length * _charWidth) + (padding * 2);
364+
keyWidth = (length * charWidth) + (padding * 2);
296365
}
297366
key->_x = xPos;
298367
key->_y = yPos;
@@ -311,49 +380,25 @@ void Keypad::clicked(int x, int y, bool pressed) {
311380

312381
if (pressed && inside) {
313382
if (key->_label.equals(shiftKey)) {
314-
toggleShift();
383+
_context.toggleShift();
315384
} else if (key->_label.equals(toggleKey)) {
316385
_currentLayout = static_cast<KeypadLayout>((_currentLayout + 1) % 3);
317386
generateKeys();
318387
layout(_posX, _posY, _width, _height);
319388
break;
320389
} else {
321-
bool useShift = _shiftActive ^ _capsLockActive;
322-
if (_shiftActive && !key->_special) {
323-
_shiftActive = false;
324-
}
325-
key->onClick(useShift);
390+
key->onClick(_context.useShift(key->_special));
326391
}
327392
break;
328393
}
329394
}
330395
}
331396

332-
void Keypad::toggleShift() {
333-
_shiftActive = !_shiftActive;
334-
}
335-
336397
void Keypad::draw() const {
337398
maSetColor(_theme->_bg);
338399
maFillRect(_posX, _posY, _width, _height);
339400
for (const auto &key : _keys) {
340-
key->drawButton(_theme);
341-
342-
String label;
343-
if (key->_special) {
344-
label = key->_label;
345-
} else {
346-
bool useShift = _shiftActive ^ _capsLockActive;
347-
label = useShift ? key->_altLabel : key->_label;
348-
}
349-
350-
int labelLength = key->_labelLength;
351-
int xOffset = (key->_w - (_charWidth * labelLength)) / 2;
352-
int yOffset = (key->_h - _charHeight) / 2;
353-
int textX = key->_x + xOffset;
354-
int textY = key->_y + yOffset;
355-
maSetColor(key->color(_theme, _shiftActive));
356-
maDrawText(textX, textY, label.c_str(), labelLength);
401+
key->draw(_theme, &_context);
357402
}
358403
}
359404

src/ui/keypad.h

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "ui/strlib.h"
1212
#include "ui/inputs.h"
13+
#include "ui/image_codec.h"
1314
#include "lib/maapi.h"
1415

1516
using namespace strlib;
@@ -25,6 +26,26 @@ struct KeypadTheme {
2526
int _funcText;
2627
};
2728

29+
struct KeypadDrawContext {
30+
explicit KeypadDrawContext(int charWidth, int charHeight);
31+
void toggleShift();
32+
bool useShift(bool specialKey);
33+
34+
bool _shiftActive;
35+
bool _capsLockActive;
36+
int _charWidth;
37+
int _charHeight;
38+
39+
ImageData _cutImage;
40+
ImageData _copyImage;
41+
ImageData _pasteImage;
42+
ImageData _saveImage;
43+
ImageData _runImage;
44+
ImageData _helpImage;
45+
ImageData _backImage;
46+
ImageData _enterImage;
47+
};
48+
2849
enum KeypadLayout {
2950
LayoutLetters = 0, LayoutNumbers = 1, LayoutSymbols = 2
3051
};
@@ -38,7 +59,8 @@ struct Key {
3859
explicit Key(const RawKey &k);
3960

4061
int color(const KeypadTheme *theme, bool shiftActive) const;
41-
void drawButton(const KeypadTheme *theme) const;
62+
void draw(const KeypadTheme *theme, const KeypadDrawContext *context) const;
63+
void drawImage(const ImageData *image) const;
4264
bool inside(int x, int y) const;
4365
void onClick(bool useShift);
4466

@@ -68,16 +90,12 @@ struct Keypad {
6890
int _posY;
6991
int _width;
7092
int _height;
71-
int _charWidth;
72-
int _charHeight;
7393
strlib::List<Key *> _keys;
74-
bool _shiftActive;
75-
bool _capsLockActive;
7694
KeypadTheme *_theme;
95+
KeypadDrawContext _context;
7796
KeypadLayout _currentLayout;
7897

7998
void generateKeys();
80-
void toggleShift();
8199
};
82100

83101
struct KeypadInput : public FormInput {

0 commit comments

Comments
 (0)