private static void StartTextBoxBlinker(TimedAction timedAction, SoftwareKeyboardUiState state, object stateLock) { timedAction.Reset(() => { lock (stateLock) { // The blinker is on half of the time and events such as input // changes can reset the blinker. state.TextBoxBlinkCounter = (state.TextBoxBlinkCounter + 1) % (2 * SoftwareKeyboardRendererBase.TextBoxBlinkThreshold); // Tell the render thread there is something new to render. Monitor.PulseAll(stateLock); } }, TextBoxBlinkSleepMilliseconds); }
private static void StartRenderer(TimedAction timedAction, SoftwareKeyboardRendererBase renderer, SoftwareKeyboardUiState state, object stateLock) { SoftwareKeyboardUiState internalState = new SoftwareKeyboardUiState(); bool canCreateSurface = false; bool needsUpdate = true; timedAction.Reset(() => { lock (stateLock) { if (!Monitor.Wait(stateLock, RendererWaitTimeoutMilliseconds)) { return; } needsUpdate = UpdateStateField(ref state.InputText, ref internalState.InputText); needsUpdate |= UpdateStateField(ref state.CursorBegin, ref internalState.CursorBegin); needsUpdate |= UpdateStateField(ref state.CursorEnd, ref internalState.CursorEnd); needsUpdate |= UpdateStateField(ref state.AcceptPressed, ref internalState.AcceptPressed); needsUpdate |= UpdateStateField(ref state.CancelPressed, ref internalState.CancelPressed); needsUpdate |= UpdateStateField(ref state.OverwriteMode, ref internalState.OverwriteMode); needsUpdate |= UpdateStateField(ref state.TypingEnabled, ref internalState.TypingEnabled); needsUpdate |= UpdateStateField(ref state.ControllerEnabled, ref internalState.ControllerEnabled); needsUpdate |= UpdateStateField(ref state.TextBoxBlinkCounter, ref internalState.TextBoxBlinkCounter); canCreateSurface = state.SurfaceInfo != null && internalState.SurfaceInfo == null; if (canCreateSurface) { internalState.SurfaceInfo = state.SurfaceInfo; } } if (canCreateSurface) { renderer.CreateSurface(internalState.SurfaceInfo); } if (needsUpdate) { renderer.DrawMutableElements(internalState); renderer.CopyImageToBuffer(); needsUpdate = false; } }); }
public void DrawMutableElements(SoftwareKeyboardUiState state) { if (_surface == null) { return; } _surface.Mutate(context => { var messageRectangle = MeasureString(MessageText, _messageFont); float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X; float messagePositionY = _messagePositionY - messageRectangle.Y; var messagePosition = new PointF(messagePositionX, messagePositionY); var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height); SetGraphicsOptions(context); context.Fill(_panelBrush, messageBoundRectangle); context.DrawText(MessageText, _messageFont, _textNormalColor, messagePosition); if (!state.TypingEnabled) { // Just draw a semi-transparent rectangle on top to fade the component with the background. // TODO (caian): This will not work if one decides to add make background semi-transparent as well. context.Fill(_disabledBrush, messageBoundRectangle); } DrawTextBox(context, state); float halfWidth = _panelRectangle.Width / 2; float buttonsY = _panelRectangle.Y + 185; PointF acceptButtonPosition = new PointF(halfWidth - 180, buttonsY); PointF cancelButtonPosition = new PointF(halfWidth, buttonsY); PointF disableButtonPosition = new PointF(halfWidth + 180, buttonsY); DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled); DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled); }); }
private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUiState state) { var inputTextRectangle = MeasureString(state.InputText, _inputTextFont); float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8)); float boxHeight = 32; float boxY = _panelRectangle.Y + 110; float boxX = (int)((_panelRectangle.Width - boxWidth) / 2); RectangleF boxRectangle = new RectangleF(boxX, boxY, boxWidth, boxHeight); RectangleF boundRectangle = new RectangleF(_panelRectangle.X, boxY - _textBoxOutlineWidth, _panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth); context.Fill(_panelBrush, boundRectangle); context.Draw(_textBoxOutlinePen, boxRectangle); float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X; float inputTextY = boxY + 5; var inputTextPosition = new PointF(inputTextX, inputTextY); context.DrawText(state.InputText, _inputTextFont, _textNormalColor, inputTextPosition); // Draw the cursor on top of the text and redraw the text with a different color if necessary. Color cursorTextColor; IBrush cursorBrush; Pen cursorPen; float cursorPositionYTop = inputTextY + 1; float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1; float cursorPositionXLeft; float cursorPositionXRight; bool cursorVisible = false; if (state.CursorBegin != state.CursorEnd) { Debug.Assert(state.InputText.Length > 0); cursorTextColor = _textSelectedColor; cursorBrush = _selectionBoxBrush; cursorPen = _selectionBoxPen; string textUntilBegin = state.InputText.Substring(0, state.CursorBegin); string textUntilEnd = state.InputText.Substring(0, state.CursorEnd); var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont); var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont); cursorVisible = true; cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X; cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X; } else { cursorTextColor = _textOverCursorColor; cursorBrush = _cursorBrush; cursorPen = _cursorPen; if (state.TextBoxBlinkCounter < TextBoxBlinkThreshold) { // Show the blinking cursor. int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); string textUntilCursor = state.InputText.Substring(0, cursorBegin); var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); cursorVisible = true; cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; if (state.OverwriteMode) { // The blinking cursor is in overwrite mode so it takes the size of a character. if (state.CursorBegin < state.InputText.Length) { textUntilCursor = state.InputText.Substring(0, cursorBegin + 1); cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; } else { cursorPositionXRight = cursorPositionXLeft + _inputTextFontSize / 2; } } else { // The blinking cursor is in insert mode so it is only a line. cursorPositionXRight = cursorPositionXLeft; } } else { cursorPositionXLeft = inputTextX; cursorPositionXRight = inputTextX; } } if (state.TypingEnabled && cursorVisible) { float cursorWidth = cursorPositionXRight - cursorPositionXLeft; float cursorHeight = cursorPositionYBottom - cursorPositionYTop; if (cursorWidth == 0) { PointF[] points = new PointF[] { new PointF(cursorPositionXLeft, cursorPositionYTop), new PointF(cursorPositionXLeft, cursorPositionYBottom), }; context.DrawLines(cursorPen, points); } else { var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight); context.Draw(cursorPen, cursorRectangle); context.Fill(cursorBrush, cursorRectangle); Image <Argb32> textOverCursor = new Image <Argb32>((int)cursorRectangle.Width, (int)cursorRectangle.Height); textOverCursor.Mutate(context => { var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y); context.DrawText(state.InputText, _inputTextFont, cursorTextColor, textRelativePosition); }); var cursorPosition = new Point((int)cursorRectangle.X, (int)cursorRectangle.Y); context.DrawImage(textOverCursor, cursorPosition, 1); } } else if (!state.TypingEnabled) { // Just draw a semi-transparent rectangle on top to fade the component with the background. // TODO (caian): This will not work if one decides to add make background semi-transparent as well. context.Fill(_disabledBrush, boundRectangle); } }