This is my preliminary idea for a command interface.
It’s probably still flawed, but I think it’s going to be easier to maintain and work with:
#include <cstdint>
enum class KeyboardCommandType : std::uint8_t
{
// Does nothing
None = 0x00,
// Character
Char = 0x01,
// Arrow keys
ArrowUp = 0x10,
ArrowDown = 0x11,
ArrowLeft = 0x12,
ArrowRight = 0x13,
Backspace = 0x14,
Enter = 0x15,
Tab = 0x16,
CapsLock = 0x17
Control = 0x18,
Alt = 0x19,
AltGr = 0x1A,
Escape = 0x1F,
// Grouping of 6
Insert = 0x20,
Delete = 0x21,
Home = 0x22,
End = 0x23,
PageUp = 0x24,
PageDown = 0x25,
// Function keys
F1 = 0xF1,
F2 = 0xF2,
F3 = 0xF3,
F4 = 0xF4,
F5 = 0xF5,
F6 = 0xF6,
F7 = 0xF7,
F8 = 0xF8,
F9 = 0xF9,
F10 = 0xFA,
F11 = 0xFB,
F12 = 0xFC,
}
struct KeyboardCommand
{
KeyboardCommandType type = KeyboardCommandType::None;
char32_t character = '\0';
constexpr KeyboardCommand(void) = default;
constexpr KeyboardCommand(KeyboardCommandType type)
: type(type), character('\0')
{
}
constexpr KeyboardCommand(char32_t chararacter)
: type(KeyboardCommandType::InsertChar), character(character)
{
}
constexpr KeyboardCommand(KeyboardCommandType type, char32_t chararacter)
: type(type), character(character)
{
}
};
class CommandInputStream
{
public:
virtual std::size_t getCommandsPending(void) const = 0;
virtual KeyboardCommand peekNextCommand(void) const = 0;
virtual KeyboardCommand readNextCommand(void) = 0;
virtual bool hasCommandsPending(void) const
{
return (this->getCommandsPending > 0);
}
};
class CommandOutputStream
{
public:
virtual std::size_t getCommandsPending(void) const = 0;
virtual void emitCommand(KeyboardCommand command) = 0;
virtual void flush(void)
{
}
virtual bool hasCommandsPending(void) const
{
return (this->getCommandsPending > 0);
}
};
By representing a keystroke as a command type and a character it allows the entire unicode range to be used, thus giving the keyboard flexibility, but also allows common command keys to be used.
At the moment I’m thinking that for Control
, the command won’t be queued until the keyboard side detects that the user has pressed another key whilst holding control, after which it either submits Control
as the type
and the other key pressed as the character
, or submits two commands in a row.
Either way, this allows keyboard shortcuts to be used.
I was thinking that instead of a ‘switch page’ command, there could be a number of pages and the F1
-F12
keys could be used for selecting pages.
Either that or set up a shortcut like Control
+'<'
for ‘previous page’ and Control
+'>'
for ‘next page’.
The important thing about this system is that rather than separating the implementation into ‘keyboard input’ and ‘screen output’, it’s establishing the notion of commands and command streams, which is more abstract and thus a bit more flexible.
Instead of just the keyboard and the screen, you can now have the keyboard, a keyboard input processor to convert keystrokes to commands and a command reader that takes the keyboard commands and uses them to manipulate the text field.
Ideally it would be good to not have to tie the KeyboardCommandType
to keyboard keys, but I found that the alternative results in making assumptions about what the text editor is capable of, which results in less flexibility.
I’ll post the result of that approach in a details block:
Other approach
#include <cstdint>
enum class KeyboardCommandType : std::uint8_t
{
//
// 0x0N - General
//
// Does nothing
None = 0x00,
//
// 0x1N - Insert and Delete
//
// Insert character at cursor position
InsertForwards = 0x10,
// Insert character before cursor position
InsertBackwards = 0x11,
// Overwrite character at cursor position
OverwriteForwards = 0x12,
// Overwrite character before cursor position
OverwriteBackwards = 0x13,
// Delete the character at cursor, i.e. delete
DeleteForwards = 0x14,
// Delete the character before cursor, i.e. backspace
DeleteBackwards = 0x15,
//
// 0x2N - Navigation
//
// To allow menu navigation?
Enter = 0x20,
Cancel = 0x21,
// Move the cursor
Up = 0x22,
RightUp = 0x23,
Right = 0x24,
LeftDown = 0x25,
Down = 0x26,
RightDown = 0x27,
Left = 0x28,
LeftUp = 0x29,
// Function as 'Home' and 'End' keys
LineStart = 0x2A,
LineEnd = 0x2B,
// Move the cursor to the start of the previous word
PreviousWord = 0x2C,
// Move the cursor to the end of the next word
NextWord = 0x2D,
//
// 0x3N - Select, copy, cut and paste
//
// To begin selecting text or to cancel selecting text
BeginSelect = 0x30,
EndSelect = 0x31,
// Standard
Copy = 0x32,
Cut = 0x33,
Paste = 0x34,
//
// 0x4N - Standard document operations
//
Save = 0x40,
Undo = 0x41,
Redo = 0x42,
// Find
Find = 0x50,
FindNext = 0x51,
FindPrevious = 0x52,
ReplaceCurrent = 0x53,
}
Tomorrow I’ll look at getting something on the screen that can use both ends of this.