Any suggestion is highly appreciated.
Let me begin by saying I am not an expert in text controls, but by now I'm sure that this doesn't matter, because I can help you get into the woods and out safely. These things are complicated in nature and require plenty of intuition and knowledge of how things work. However, you can inspect the code that runs in the senpai-js/senpai-stage repository here.
We should define a few things up front:
/^.$/uInsert, Selection, Basic (I use the SelectionState enum in my library and inspect the insertMode property on stage)textScroll which is always a negative numberNow I will go over each function to describe it's behavior to describe exactly how a textbox control should work.
Collision detection is a monster. Normalizing point movement between mouse and touch events is a complicated beast not covered in this text. Once you handle point events, you have to perform some sort of general collision detection for a rectangle. This means doing AABB collision. If the textbox sprite itself is rotated, you will have to "un-rotate" the point itself. However, we bypass this check if the mouse/touch point is already down over the textbox. This is because once you start selecting text, you want this function to always return true. Then we move to narrowPhase collision, which actually checks to see if the "un-transformed" mouse/touch point is within the padding of the textbox. If it is, or the textbox is active, we return a truthy value here.
Once we know that the mouse/touch point is within the bounds of our textbox, we change the css of the canvas to cursor: text; visually.
When we press the mouse button down over the textbox, we need to calculate where to move the caret. The caret can exist in a range from 0 to text.length inclusive. Note that this isn't exactly right because unicode characters can have a length of 2. You must keep track of each character added to your text inside an array to assert that you aren't measuring faulty unicode characters. Calculating the target index means looping over each character of the current text and appending it to a temporary string, measuring each time until the measured width is greater than the current textScroll + the measured textWidth.
Once we have garunteed that the point has gone down on top of the textbox and the starting point is set, we can start the "selection" mode. Dragging the point should move the selection from the starting caretIndex to the new calculated end index. This goes in both directions.
An example of this is shown here.
The solution for web key presses is to inspect the key property on the KeyEvent. Despite a lot of what everyone says, it's possible to test that text property by testing it against the aforementioned unicode regex. If it matches, chances are that key has actually been pressed on the keyboard. This doesn't account for key combinations like ctrl + c and ctrl + v for copy and pasting. These features are trivial and are left up to the reader to decide how to implement these.
The few exceptions are the arrow keys: "ArrowLeft", "ArrowRight" etc. These keys actually modify the state of your control, and change how it functions. It's important to remember that key events should only be handled by the currently focused control. This means you should check and make sure the control is focused during text input. This of course happens at a higher level than I have coded in my library, so this is trivial.
The next problem that needs to be solved is how each character input should modify the state of your control. The keyDown method discerns the selectionState and calls a different function based on it's state. This is not optimized pseudo-code, but is used for clarity, and is perfect for our purposes in describing the behavior.
selectionStart, and insert the new key into the text arrayNormal or CaretselectionState to Normal again0 and text.length indexes respectively
caretIndex once againcaretIndex - 1caretIndex0text.lengthcaretIndexCaret mode, you should restart the flash mechanism so that it shows exactly where they are each time the caret movesctx.measureText to the caret index by slicing the text to the caret position unless the mode is SelectionSelection, because we always want the end of the text selection to be visible to the userctx.save() (basic canvas)textScroll value which should be a negative numbermidline value which should be the middle of the textbox verticallymiddle and fill the text by calling text.join("") on your text array