private static void MoveCaretKeyboardCallBack(InputTextContext textBox) { if (Keyboard.Instance.KeyDown(Key.Home)) { textBox.SelectIndex = textBox.CaretIndex = 0; } if (Keyboard.Instance.KeyDown(Key.End)) { textBox.SelectIndex = textBox.CaretIndex = (uint)textBox.Text.Length; } if (Keyboard.Instance.KeyPressed(Key.Left, true)) { if (textBox.CaretIndex > 0) { textBox.CaretIndex -= 1; } if (!Keyboard.Instance.KeyDown(Key.LeftShift)) { textBox.SelectIndex = textBox.CaretIndex; } } else if (Keyboard.Instance.KeyPressed(Key.Right, true)) { if (textBox.CaretIndex < textBox.Text.Length) { textBox.CaretIndex += 1; } if (!Keyboard.Instance.KeyDown(Key.LeftShift)) { textBox.SelectIndex = textBox.CaretIndex; } } }
/// <summary> /// Move state according to the command /// </summary> /// <param name="command">command</param> /// <param name="context">context</param> /// <returns>true:valid command/false:invalid command</returns> public bool MoveNext(string command, InputTextContext context) { Transition transition = new Transition(CurrentState, command); Result result; if (!transitions.TryGetValue(transition, out result)) { return(false); } CurrentState = result.State; result.CallBack?.Invoke(context); return(true); }
private static void MoveCaretCallBack(InputTextContext textBox) { var g = Form.current.uiContext; var rect = textBox.Rect; var style = GUIStyle.Basic; ITextContext textContext = TextMeshUtil.GetTextContext(textBox.Text, rect.Size, style, GUIState.Normal); var contentRect = style.GetContentRect(rect); var mousePos = Mouse.Instance.Position; var offsetOfTextRect = contentRect.TopLeft; uint caretIndex; bool isInside; caretIndex = textContext.XyToIndex( (float)(mousePos.X - offsetOfTextRect.X), (float)(mousePos.Y - offsetOfTextRect.Y), out isInside); textBox.SelectIndex = textBox.CaretIndex = caretIndex; }
public static void DrawTextBox(Rect rect, int id, string text, InputTextContext context, GUIState state) { GUIContext g = Form.current.uiContext; WindowManager w = g.WindowManager; Window window = w.CurrentWindow; var d = window.DrawList; var style = GUIStyle.Basic; // draw text, selection and caret var contentRect = style.GetContentRect(rect); d.PushClipRect(contentRect, true); if (g.ActiveId == id) { //Calculate positions and sizes var textContext = TextMeshUtil.GetTextContext(context.Text, rect.Size, style, GUIState.Normal); var offsetOfTextRect = contentRect.TopLeft; float pointX, pointY; float caretHeight; textContext.IndexToXY(context.CaretIndex, false, out pointX, out pointY, out caretHeight); var caretTopPoint = new Point(pointX, pointY); var caretBottomPoint = new Point(pointX, pointY + caretHeight); caretTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); caretBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); byte caretAlpha = context.CaretAlpha; // Check if the caret is outside the rect. If so, move the text so the caret is always shown. FIXME this should be done in TextBoxBehaviour var caretX = caretTopPoint.X; if (caretX < contentRect.X || caretX > contentRect.Right) { var offsetX = -(caretX - contentRect.Width - rect.X); contentRect.Offset(offsetX, 0); caretTopPoint.Offset(offsetX, 0); caretBottomPoint.Offset(offsetX, 0); } //Draw text d.DrawText(contentRect, context.Text, style, GUIState.Normal); //Draw selection rect /* * Note: Design * * left bound right bound * ↓ ↓ * | A-----------------+ * | |CONTENT_CONTENT_C| => Line 1 => rect1 * +------B | * |ONTENT_CONTENT_CONTENT_C| => Line 2 (represents inner lines) => rect2 * | C------+ * |ONTENT_CONTENT_CO| => Line 3 => rect3 * +-----------------D * * left bound = l * right bound = r */ if (context.SelectIndex != context.CaretIndex) { float selectPointX, selectPointY; textContext.IndexToXY(context.SelectIndex, false, out selectPointX, out selectPointY, out float dummyHeight); var selectTopPoint = new Point(selectPointX, selectPointY); var selectBottomPoint = new Point(selectPointX, selectPointY + caretHeight); selectTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); selectBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); var delta = Math.Abs(selectTopPoint.Y - caretTopPoint.Y); if (delta < caretHeight) // single line { var selectionRect = new Rect( new Point(pointX, pointY), new Point(selectPointX, selectPointY + caretHeight)); selectionRect.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); d.AddRectFilled(selectionRect.Min, selectionRect.Max, Color.Argb(100, 10, 102, 214)); } else//mutiple line { var l = contentRect.Left; var r = contentRect.Right; Point A; Point B; Point C; Point D; if (selectTopPoint.Y > caretTopPoint.Y) { A = caretTopPoint; B = caretBottomPoint; C = selectTopPoint; D = selectBottomPoint; } else { A = selectTopPoint; B = selectBottomPoint; C = caretTopPoint; D = caretBottomPoint; } // Line 1 var rect1 = new Rect(A, r - A.X, caretHeight); d.AddRectFilled(rect1, Color.Argb(100, 10, 102, 214), 12); d.AddRect(rect1.Min, rect1.Max, Color.White, 12, 15, 2); // Line 2 var rect2 = new Rect(new Point(l, B.Y), new Point(r, C.Y)); if (rect2.Height > 0.5 * caretHeight)//TODO There should more a more reasonable way to detect this: If it only has two lines, we don't draw the inner rectangle. { d.AddRectFilled(rect2, Color.Argb(100, 10, 102, 214), 12); d.AddRect(rect2.Min, rect2.Max, Color.White, 12, 15, 2); } // Line 3 var rect3 = new Rect(new Point(l, C.Y), D); d.AddRectFilled(rect3, Color.Argb(100, 10, 102, 214), 12); d.AddRect(rect3.Min, rect3.Max, Color.White, 12, 15, 2); } } //Draw caret d.PathMoveTo(caretTopPoint); d.PathLineTo(caretBottomPoint); d.PathStroke(Color.Argb(caretAlpha, 0, 0, 0), false, 2); } else { d.DrawText(contentRect, text, style, GUIState.Normal); } d.PopClipRect(); // draw the box { d.AddRect(rect.Min, rect.Max, style.GetBorderColor(state)); } }
public static string TextBoxBehavior(int id, Rect rect, string text, out bool hovered, out bool active, out InputTextContext context, InputTextFlags flags = 0, Func <char, bool> checker = null) { GUIContext g = Form.current.uiContext; active = false; hovered = g.IsHovered(rect, id); if (hovered) { g.SetHoverID(id); if (Mouse.Instance.LeftButtonPressed) { g.SetActiveID(id); } } else { if (Mouse.Instance.LeftButtonPressed) { if (g.ActiveId == id) { g.SetActiveID(0); } } } if (g.ActiveId == id && g.InputTextState.inputTextContext.Id != id) //editing text box changed to TextBox<id> { g.InputTextState.stateMachine.CurrentState = "Active"; //reset state g.InputTextState.inputTextContext = new InputTextContext() //reset input text context data { Id = id, CaretIndex = 0, SelectIndex = 0, Selecting = false, CaretPosition = Point.Zero, Flags = flags, Checker = checker, Rect = rect, Text = text }; } context = null; if (g.ActiveId == id) { active = true; var stateMachine = g.InputTextState.stateMachine; context = g.InputTextState.inputTextContext; context.Rect = rect; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. g.ActiveIdAllowOverlap = !Mouse.Instance.LeftButtonPressed; #if INSPECT_STATE var A = stateMachine.CurrentState; #endif if (Mouse.Instance.LeftButtonPressed) { stateMachine.MoveNext(TextBoxCommand.MoveCaret, context); } if (hovered && Mouse.Instance.LeftButtonState == KeyState.Down && stateMachine.CurrentState != TextBoxState.ActiveSelecting) { stateMachine.MoveNext(TextBoxCommand.MoveCaret, context); stateMachine.MoveNext(TextBoxCommand.BeginSelect, context); } if (hovered && Mouse.Instance.LeftButtonState == KeyState.Up) { stateMachine.MoveNext(TextBoxCommand.EndSelect, context); } if (stateMachine.CurrentState == TextBoxState.Active) { stateMachine.MoveNext(TextBoxCommand.DoEdit, context); } if (stateMachine.CurrentState == TextBoxState.ActiveSelecting) { stateMachine.MoveNext(TextBoxCommand.DoSelect, context); } stateMachine.MoveNext(TextBoxCommand.MoveCaretKeyboard, context); #if INSPECT_STATE var B = stateMachine.CurrentState; Debug.WriteLineIf(A != B, string.Format("TextBox<{0}> {1}=>{2} CaretIndex: {3}, SelectIndex: {4}", id, A, B, context.CaretIndex, context.SelectIndex)); #endif return(context.Text); } return(text); }
public InputTextState() { stateMachine = new StateMachineEx(TextBoxState.Normal, states, callBacks); inputTextContext = new InputTextContext(); }
private static void DoEditCallBack(InputTextContext textBox) { if (CurrentOS.IsDesktopPlatform) { var CaretIndex = textBox.CaretIndex; var SelectIndex = textBox.SelectIndex; string textBeforeCaret; //Input characters if (Ime.ImeBuffer.Count != 0) { char[] textSequence = ApplyFilter(Ime.ImeBuffer, textBox.Flags, textBox.Checker); var inputText = new string(textSequence); if (CaretIndex != SelectIndex) //Replace selected text with inputText { //TODO check whether convert int and uint back and forth is appropriate var minIndex = (int)Math.Min(CaretIndex, SelectIndex); var maxIndex = (int)Math.Max(CaretIndex, SelectIndex); textBox.Text = textBox.Text.Substring(0, minIndex) + inputText + textBox.Text.Substring(maxIndex); textBox.MoveCaret((uint)minIndex); textBeforeCaret = textBox.Text.Substring(0, minIndex) + inputText; } else //Insert inputText into caret position { if (textBox.Text.Length == 0) { textBox.Text = inputText; textBeforeCaret = textBox.Text; } else { textBox.Text = textBox.Text.Substring(0, (int)CaretIndex) + inputText + textBox.Text.Substring((int)CaretIndex); textBeforeCaret = textBox.Text.Substring(0, (int)CaretIndex) + inputText; } } textBox.MoveCaret((uint)textBeforeCaret.Length); Ime.ImeBuffer.Clear(); } //Backspace, delete one character before the caret else if (Keyboard.Instance.KeyPressed(Key.Back, true)) { if (CaretIndex != SelectIndex) { var minIndex = (int)Math.Min(CaretIndex, SelectIndex); var maxIndex = (int)Math.Max(CaretIndex, SelectIndex); textBox.Text = textBox.Text.Substring(0, minIndex) + textBox.Text.Substring(maxIndex); textBox.MoveCaret((uint)minIndex); textBeforeCaret = textBox.Text.Substring(0, minIndex); textBox.MoveCaret((uint)textBeforeCaret.Length); } else if (CaretIndex > 0) { var newText = textBox.Text.Remove((int)(CaretIndex - 1), 1); if (CaretIndex == 0) { textBeforeCaret = ""; } else { textBeforeCaret = textBox.Text.Substring(0, (int)(CaretIndex - 1)); } textBox.MoveCaret((uint)textBeforeCaret.Length); textBox.Text = newText; } } //Delete, delete one character after the caret else if (Keyboard.Instance.KeyPressed(Key.Delete, true)) { if (CaretIndex != SelectIndex) { var minIndex = (int)Math.Min(CaretIndex, SelectIndex); var maxIndex = (int)Math.Max(CaretIndex, SelectIndex); textBox.Text = textBox.Text.Substring(0, minIndex) + textBox.Text.Substring(maxIndex); textBox.MoveCaret((uint)minIndex); textBeforeCaret = textBox.Text.Substring(0, minIndex); textBox.MoveCaret((uint)textBeforeCaret.Length); } else if (CaretIndex < textBox.Text.Length) { textBox.Text = textBox.Text.Remove((int)CaretIndex, 1); } } } else { if (keyboardTask == null) { keyboardTask = Keyboard.Show(textBox.Text); } else { if (keyboardTask.IsCompleted) { var inputText = keyboardTask.Result; textBox.Text = inputText; keyboardTask = null; // deactive the textbox we are editing var g = Form.current.uiContext; g.SetActiveID(0); } } } }
public static void DrawTextBox(Node node, int id, string text, InputTextContext context, GUIState state) { GUIContext g = Form.current.uiContext; Rect rect = node.Rect; var d = node.RenderOpen(); var style = node.RuleSet; // draw text, selection and caret var contentRect = node.ContentRect; // clip text rendering to content-box //d.PushClip(contentRect); if (g.ActiveId == id) { //Calculate positions and sizes var textContext = TextMeshUtil.GetTextContext(context.Text, rect.Size, style, GUIState.Normal); var offsetOfTextRect = contentRect.TopLeft; float pointX, pointY; float caretHeight; textContext.IndexToXY(context.CaretIndex, false, out pointX, out pointY, out caretHeight); var caretTopPoint = new Point(pointX, pointY); var caretBottomPoint = new Point(pointX, pointY + caretHeight); caretTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); caretBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); byte caretAlpha = context.CaretAlpha; // Check if the caret is outside the rect. If so, move the text so the caret is always shown. FIXME this should be done in TextBoxBehaviour var caretX = caretTopPoint.X; if (caretX < contentRect.X || caretX > contentRect.Right) { var offsetX = -(caretX - contentRect.Width - rect.X); contentRect.Offset(offsetX, 0); caretTopPoint.Offset(offsetX, 0); caretBottomPoint.Offset(offsetX, 0); } //Draw text d.DrawText(style, context.Text, contentRect); //Draw selection rect /* * Note: Design * * left bound right bound * ↓ ↓ * | A-----------------+ * | |CONTENT_CONTENT_C| => Line 1 => rect1 * +------B | * |ONTENT_CONTENT_CONTENT_C| => Line 2 (represents inner lines) => rect2 * | C------+ * |ONTENT_CONTENT_CO| => Line 3 => rect3 * +-----------------D * * left bound = l * right bound = r */ if (context.SelectIndex != context.CaretIndex) { float selectPointX, selectPointY; textContext.IndexToXY(context.SelectIndex, false, out selectPointX, out selectPointY, out float dummyHeight); var selectTopPoint = new Point(selectPointX, selectPointY); var selectBottomPoint = new Point(selectPointX, selectPointY + caretHeight); selectTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); selectBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); var delta = Math.Abs(selectTopPoint.Y - caretTopPoint.Y); if (delta < caretHeight) // single line { var selectionRect = new Rect( new Point(pointX, pointY), new Point(selectPointX, selectPointY + caretHeight)); selectionRect.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, selectionRect); } else//multiple lines { var l = contentRect.Left; var r = contentRect.Right; Point A; Point B; Point C; Point D; if (selectTopPoint.Y > caretTopPoint.Y) { A = caretTopPoint; B = caretBottomPoint; C = selectTopPoint; D = selectBottomPoint; } else { A = selectTopPoint; B = selectBottomPoint; C = caretTopPoint; D = caretBottomPoint; } // Line 1 var rect1 = new Rect(A, r - A.X, caretHeight); d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, rect1); d.DrawRectangle(null, new Pen(Color.White, 2), rect1); // Line 2 var rect2 = new Rect(new Point(l, B.Y), new Point(r, C.Y)); if (rect2.Height > 0.5 * caretHeight)//TODO There should more a more reasonable way to detect this: If it only has two lines, we don't draw the inner rectangle. { d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, rect2); d.DrawRectangle(null, new Pen(Color.White, 2), rect2); } // Line 3 var rect3 = new Rect(new Point(l, C.Y), D); d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, rect3); d.DrawRectangle(null, new Pen(Color.White, 2), rect3); } } //Draw caret d.DrawLine(new Pen(Color.Argb(caretAlpha, 0, 0, 0), 2), caretTopPoint, caretBottomPoint); } else { d.DrawText(style, text, contentRect); } //d.Pop(); d.Close(); }