This is a COM in-proc server that provides for command-line input. It is intended to be invoked by a Console program, and input will take place at the cursor position. That's not to say that it can't throw up other (temporary) windows to help with the process, but the result should still be placed at the current position in the Console, just as if input were read by ReadFile from standard input.
It must handle full Unicode input. It allows for attributes (color) along with the edited text. It can be given initial contents (for editing) as well as prompting for input starting from blank.
The main engine is nothing more than a coordinator for all the components. One component does the actual I/O. Other components provide handlers for keystrokes (or groups of keystrokes). The components are all COM servers, and the engine may be configured to use whatever handlers are desired.
The engine loads all the parts, then calls (1) the IO handler. The IO handler waits for a key stroke, then calls (2) an engine-supplied function that passes it to the key handlers (3a, 3b, ...). The first key handler that likes it will update the state, and return (to 2) a code saying "I got it". When 2 returns to the IO handler (1), the IO handler than redraws the display as necessary. A particular state means "done" and (1) itself returns to the caller.
A variable-length array of Unicode characters.
A variable-length array of 16-bit values. Currently only the least significant 8 bits are used, matching the Console's idea of attributes.
Each position in the attribute string describes the attribute of the corresponding character in the character string. For surrogate pairs, which take two positions in the character string, both positions in the attribute string should match.
Two positions are important: The logical position, which counts how many characters from the left you are pointing to; and the physical string position which counts how many 16-bit code points you are from the left (in the character string).
Specifically, "surrogate pairs" in Unicode provide for over a million distinct characters by using two-code sequences. The only use for them currently is for language tags embedded in text, and that might be a neet thing to support in an input/output module. After consideration, I decided it's better to build support for multi-word characters right in from the beginning than to hope each extension treats them in a uniform way. The latter could be a real mess, and the mess would hit in a few years as more language are added to Unicode/10646 and high characters get more use. One inspiration for this project is the lack of modern-ness in older programs, so I should plan ahead. I'm starting where other programs leave off, and need a lot of headroom for future considerations.
Various handlers may need to store instance data of their own.
To accomplish this, the handler asks the engine for a slot. The slot number is class-data to the engine, so the handler can use the same slot number for all instances. Armed with a slot number, the handler can get and set a 4-byte per-instance value.
The Normal Edit Color is the attribute generally used by the input field. For example, the user might configure "yellow on black". The Current Edit Color is the attribute to use for newly-inserted characters. A handler might change the Current color based on the cursor position and context, while still letting the engine insert the character using the default behavior.
The actual mapping process is controlled by the handlers themselves. That is, a key is passed to each handler, and the handler decides if it wants to process it.
However, there is a central and consistant manner of assignning keys to commands. Editing one file lets you customise the mappings for all loaded handlers. This means that each handler should respect the data in the mapping file when testing to see if it likes that key, but it still has the responsibility. Note that items not listed in the config file still operate with default settings, so the engine doesn't have to be aware of all the mappings.
Original idea was to have all COM classes registered under a specific component category automatically be loaded as handlers. But now I think a single file that describes handlers and their key mappings would be better.
Something like this, perhaps:
handler "clsid:12345678-1234-1234-1234-123412341234" # history list processor @1 UP #previous history item @2 DOWN #next history item @3 CTRL+UP #history window handler "clsid: .....
The first line introduces a handler. It is named using the display name of a moniker. This may be a simple CLSID as shown, a composite moniker for giving additional information to the handler or restoring complex settings from a persistant file, or anything else.
The # character introduces a comment.
Subsequent lines map a virtual key code to each command. You don't have to list them all, but only those that are not using their default values. A complete list can be handy though in helping you find the setting you want to change.
A single instance of the command input prompt could have more than one history list, with different history used in different calls. This multi-context for one instance is available for any handler. The call to activate a command input prompt includes an integer, and handlers (specifically including the history handler) can use this number to maintain multiple per-instance states.
I've written a module to deal with console keyboard input. A VK value represents a keycode. The low 8 bits are Windows virtual keycode values. The high 8 bits are modifiers showing shift states. The manner of noting left/right modifier keys is different from how ReadConsoleInput and other Windows events report it, to make it easier to use in (1) representing a key assignment, and (2) matching the key against the assignment.See Key_test program