using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Windows.Forms; using System.ComponentModel; using System.Collections; using System.Runtime.InteropServices; using POSV.Keyboard.Touch; namespace POSV.Keyboard { /// /// Represents virtual keyboard control. /// [ToolboxItem(true)] public class KeyboardControl : Control { private TouchHandler _TouchHandler = null; /// /// Creates a new VirtualKeyboard control. /// public KeyboardControl() { _ColorTable = _DefaultColorTable; // Do not allow the control to receive focus. SetStyle(ControlStyles.Selectable, false); // Do not call OnPaintBackground(). SetStyle(ControlStyles.Opaque, true); // Enable double buffering. OptimizedDoubleBuffer and AllPaintingInWmPaint are set by the DoubleBuffered property below. //SetStyle(ControlStyles.OptimizedDoubleBuffer, true); //SetStyle(ControlStyles.AllPaintingInWmPaint, true); DoubleBuffered = true; ResizeRedraw = true; _RepeatTimer = new Timer(); _RepeatTimer.Tick += new EventHandler(RepeatTimer_Tick); } // The model for the Keyboard. This object contains all the information about the keys // and the possible layouts of the keyboard. private Keyboard _Keyboard = null; //Keyboard.CreateDefaultKeyboard(); /// /// Gets or sets the Keyboard object that describes the VirtualKeyboard. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Keyboard Keyboard { get { if(_Keyboard==null) _Keyboard = Keyboard.CreateDefaultKeyboard(); return _Keyboard; } set { _Keyboard = value; } } /// /// Default width for the keyboard in pixels. /// public static int DefaultWidth { get { return 740; } } /// /// Default height for the keyboard in pixels. /// public static int DefaultHeight { get { return 250; } } protected override Size DefaultSize { get { return Dpi.Size(new Size(DefaultWidth, DefaultHeight)); } } private VirtualKeyboardColorTable _DefaultColorTable = new VirtualKeyboardColorTable(); private VirtualKeyboardColorTable _ColorTable; /// /// Gets or set the ColorTable used to draw the keyboard. /// public VirtualKeyboardColorTable ColorTable { get { return _ColorTable; } set { _ColorTable = value; Invalidate(); // Ok to invalidate entire control, to render everything with the new colors. } } private Renderer _Renderer = new FlatStyleRenderer(); /// /// Gets or sets the Renderer used to render the keyboard. /// public Renderer Renderer { get { return _Renderer; } set { if (value == null) value = new FlatStyleRenderer(); _Renderer = value; Invalidate(); // Ok to invalidate entire control, to redraw everything with the new renderer. } } // Through this method, the keys generated by the Virtual Keyboard are sent to the target control. // In this version, the current target control is always the control with input focus and the keyboard // is only visible when the control (to which the keyboard was previously attached) has input focus. // Therefore, SendKeys.Send offers all the logic needed for this version of the keyboard. // // For future versions, we might consider using the Win32 API SendInput function for greater flexibility. private void SendKeysToTarget(string info) { KeyboardKeyCancelEventArgs cancelArgs = new KeyboardKeyCancelEventArgs(info); OnSendingKey(cancelArgs); if (cancelArgs.Cancel) return; SendKeys.Send(info); OnKeySent(new KeyboardKeyEventArgs(info)); } #region Send repeating key if mouse is hold down private Timer _RepeatTimer; // Used to send repeated keys to the target control. private Key _PressedKey; // Used to keep the keys that is repeatedly sent to the target control. private void RepeatTimer_Tick(object sender, EventArgs e) { if (_PressedKey != null) { if (_RepeatTimer.Interval == KeyboardDelay) _RepeatTimer.Interval = KeyboardSpeed; SendKeysToTarget(_PressedKey.Info); } } private void CancelRepeatKey() { _PressedKey = null; _RepeatTimer.Stop(); } private void StartRepeatKey(Key key) { _PressedKey = key; _RepeatTimer.Start(); _RepeatTimer.Interval = KeyboardDelay; } private static int KeyboardDelay { get { // SystemInformation.KeyboardDelay is between 0 (2.5 repetitions per second) and 31 (about 30 per second) // but these values arre hardware dependendant, so the fomula below is a fast and accurate enough not to feel // any difference between this and a real keyboard at the same values. return 1000 / (SystemInformation.KeyboardDelay + 1); } } private static int KeyboardSpeed { get { // See KeyboardDelay for details. return 1000 / (SystemInformation.KeyboardSpeed + 1); } } #endregion private bool _IsTopBarVisible = true; /// /// Gets or sets a value indicating if the top bar of the keyboard is visible. /// [DefaultValue(true)] [Description("Indicates if the top bar of the keyboard is visible.")] [Category("Appearance")] public bool IsTopBarVisible { get { return _IsTopBarVisible; } set { _IsTopBarVisible = value; this.Invalidate(); } } private int _CaptionHeight = 20; /// /// Indicates the height of the keyboard caption which hosts the close button. /// [DefaultValue(20), Category("Appearance"), Description("Indicates the height of the keyboard caption which hosts the close button.")] public int CaptionHeight { get { return _CaptionHeight; } set { if (value != _CaptionHeight) { int oldValue = _CaptionHeight; _CaptionHeight = value; this.Invalidate(); } } } //private const int TopBarHeight = 20; private int TopBarVisibleHeight { get { return IsTopBarVisible ? ActualCaptionHeight + Border : 0; } } public int Border { get { return Dpi.Width10; } } private readonly Key HideKeyboardKey = new Key("Hide", info: null); private int ActualCaptionHeight { get { return Dpi.Height(_CaptionHeight); } } private Rectangle TopBarRectangle { get { return new Rectangle(Border, Border, Width - 2 * Border, ActualCaptionHeight); } } private Rectangle CloseButtonRectangle { get { return new Rectangle(Width - ActualCaptionHeight - Border, Border, ActualCaptionHeight, ActualCaptionHeight); } } private Rectangle KeysRectangle { get { Rectangle rect = new Rectangle(Border, Border, Width - 2 * Border, Height - 2 * Border); if (IsTopBarVisible) { rect.Y += ActualCaptionHeight + Border; rect.Height -= ActualCaptionHeight + Border; } return rect; } } protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { Dpi.SetScaling(factor); base.ScaleControl(factor, specified); } protected override void OnPaint(PaintEventArgs e) { KeyboardLayout currentLayout = Keyboard.CurrentLayout; if (currentLayout.LogicalWidth <= 0 || currentLayout.LogicalHeight <= 0) return; Rectangle rectKeys = KeysRectangle; float sfx = (float)rectKeys.Width / currentLayout.LogicalWidth; float sfy = (float)rectKeys.Height / currentLayout.LogicalHeight; Renderer.ColorTable = ColorTable; Renderer.BeginFrame(new BeginFrameRendererEventArgs(e.Graphics, e.ClipRectangle, rectKeys, Font, currentLayout)); Renderer.DrawBackground(new BackgroundRendererEventArgs(e.Graphics, e.ClipRectangle, new Rectangle(0, 0, Width, Height))); if (IsTopBarVisible) { Renderer.DrawTopBar(new TopBarRendererEventArgs(e.Graphics, e.ClipRectangle, TopBarRectangle, Text, Font)); Renderer.DrawCloseButton(new CloseButtonRendererEventArgs(e.Graphics, _PressedKey == HideKeyboardKey, e.ClipRectangle, CloseButtonRectangle)); } foreach (Key key in currentLayout.Keys) { Rectangle rectKey = new Rectangle((int)(key.Bounds.X * sfx), (int)(key.Bounds.Y * sfy), (int)(key.Bounds.Width * sfx), (int)(key.Bounds.Height * sfy)); rectKey.Offset(rectKeys.Left, rectKeys.Top); Renderer.DrawKey(new KeyRendererEventArgs(e.Graphics, key, key == _PressedKey, e.ClipRectangle, rectKey)); } Renderer.EndFrame(); #if TRIAL using (Font font = new System.Drawing.Font(this.Font.FontFamily, 7, FontStyle.Regular)) { using (SolidBrush brush = new SolidBrush(Color.FromArgb(24, Color.White))) e.Graphics.DrawString("DotNetBar Trial Version", font, brush, (this.ClientRectangle.Width - 80) / 2, this.ClientRectangle.Height - 32); } #endif } private void PerformMoveAction(Point location) { if (IsTopBarVisible && CloseButtonRectangle.Contains(location)) { Cursor = Cursors.Hand; } else { KeyboardLayout currentLayout = Keyboard.CurrentLayout; Rectangle rectKeys = KeysRectangle; float fx = (float)rectKeys.Width / currentLayout.LogicalWidth; float fy = (float)rectKeys.Height / currentLayout.LogicalHeight; if (_PressedKey == HideKeyboardKey) { if (_PressedKey != null) { Invalidate(GetKeyPhysicalBounds(_PressedKey, fx, fy)); } _PressedKey = null; Invalidate(CloseButtonRectangle); this.Update(); } Key key = null; if (KeysRectangle.Contains(location)) key = currentLayout.KeyHitTest((int)((location.X - rectKeys.Left) / fx), (int)((location.Y - rectKeys.Top) / fy)); // If the mouse is over a key, change the cursor to the Hand cursor, to provide // a visual feedback for the user to know that it can interact with this key. Key pressedKey = _PressedKey; if (key == null) { Cursor = Cursors.Arrow; CancelRepeatKey(); if (pressedKey != null) { Invalidate(GetKeyPhysicalBounds(pressedKey, fx, fy)); // Only invalidate area under the currently pressed key. this.Update(); } } else if (key != pressedKey) { Cursor = Cursors.Hand; CancelRepeatKey(); if (pressedKey != null) { Invalidate(GetKeyPhysicalBounds(pressedKey, fx, fy)); // Only invalidate area under the currently pressed key. this.Update(); } } else { Cursor = Cursors.Hand; } } } protected override void OnMouseMove(MouseEventArgs e) { if (ProcessMouseAction) { PerformMoveAction(e.Location); _LastTouchAction = null; } base.OnMouseMove(e); } protected override void OnMouseLeave(EventArgs e) { KeyboardLayout currentLayout = Keyboard.CurrentLayout; Rectangle rectKeys = KeysRectangle; float fx = (float)rectKeys.Width / currentLayout.LogicalWidth; float fy = (float)rectKeys.Height / currentLayout.LogicalHeight; if (_PressedKey != HideKeyboardKey) Invalidate(CloseButtonRectangle); else if (_PressedKey != null) { Invalidate(GetKeyPhysicalBounds(_PressedKey, fx, fy)); // Only invalidate area under the currently pressed key. this.Update(); } CancelRepeatKey(); } private bool _TouchEnabled = true; /// /// Indicates whether touch support is enabled when provided by hardware. /// [DefaultValue(true), Category("Behavior"), Description("Indicates whether touch support is enabled when provided by hardware.")] public bool TouchEnabled { get { return _TouchEnabled; } set { if (value != _TouchEnabled) { bool oldValue = _TouchEnabled; _TouchEnabled = value; OnTouchEnabledChanged(oldValue, value); } } } /// /// Called when TouchEnabled property has changed. /// /// Old property value /// New property value protected virtual void OnTouchEnabledChanged(bool oldValue, bool newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("TouchEnabled")); } protected override void OnHandleCreated(EventArgs e) { if (TouchHandler.IsTouchEnabled) { _TouchHandler = new TouchHandler(this, eTouchHandlerType.Touch); _TouchHandler.TouchDown += new EventHandler(TouchHandlerTouchDown); _TouchHandler.TouchUp += new EventHandler(TouchHandlerTouchUp); // Don't need touch move action for keyboard handling //_TouchHandler.TouchMove += new EventHandler(TouchHandlerTouchMove); } base.OnHandleCreated(e); } private DateTime? _LastTouchAction = null; //void TouchHandlerTouchMove(object sender, TouchEventArgs e) //{ // PerformMoveAction(e.Location); // _LastTouchAction = DateTime.Now; //} void TouchHandlerTouchUp(object sender, TouchEventArgs e) { if (_TouchEnabled) { // Process keybaord hiding through the MouseUp event instead of touch event since if closed using touch the mouse up will occur on control // which is under keyboard after keyboard is hidden which is not desired if (IsTopBarVisible && CloseButtonRectangle.Contains(e.Location)) { _LastTouchAction = null; return; } PerformUpAction(e.Location); _LastTouchAction = DateTime.Now; } } void TouchHandlerTouchDown(object sender, TouchEventArgs e) { if (_TouchEnabled) { PerformDownAction(e.Location); _LastTouchAction = DateTime.Now; } } /// /// Returns the Key at given location. Location expected is in control coordinates. /// /// Location is control coordinates. /// Key if there is a key at given location or null/nothing if no key exists at given location. public Key HitTest(Point location) { KeyboardLayout currentLayout = Keyboard.CurrentLayout; Rectangle rectKeys = KeysRectangle; float fx = (float)rectKeys.Width / currentLayout.LogicalWidth; float fy = (float)rectKeys.Height / currentLayout.LogicalHeight; Key key = null; if (KeysRectangle.Contains(location)) key = currentLayout.KeyHitTest((int)((location.X - rectKeys.Left) / fx), (int)((location.Y - rectKeys.Top) / fy)); return key; } private void PerformDownAction(Point location) { if (_PressedKey != null) // This can happen because multi-touch can send multiple down messages { PerformUpAction(location); } if (IsTopBarVisible && CloseButtonRectangle.Contains(location)) { _PressedKey = HideKeyboardKey; Invalidate(CloseButtonRectangle); } else { KeyboardLayout currentLayout = Keyboard.CurrentLayout; Rectangle rectKeys = KeysRectangle; float fx = (float)rectKeys.Width / currentLayout.LogicalWidth; float fy = (float)rectKeys.Height / currentLayout.LogicalHeight; Key key = HitTest(location); if (key != null) { TimeSpan ts = DateTime.Now.Subtract(_LastMouseUpEvent); if (_LastKeyUp != null && key.Caption == _LastKeyUp.Caption && ts < TimeSpan.FromMilliseconds(SystemInformation.DoubleClickTime) && key.ChangeToLayoutEx >= 0) { Keyboard.CurrentLayoutIndex = key.ChangeToLayoutEx; _LastMouseUpEvent = DateTime.MinValue; Invalidate(); } else { _PressedKey = key; if (_PressedKey.Info != null && _PressedKey.Info != "") { StartRepeatKey(key); SendKeysToTarget(key.Info); } } Invalidate(GetKeyPhysicalBounds(key, fx, fy)); // Only invalidate area under the currently pressed key. } } this.Update(); } private int TouchMouseInputFilterDelay = 2000; private bool ProcessMouseAction { get { return _LastTouchAction == null || (_LastTouchAction != null && DateTime.Now.Subtract(_LastTouchAction.Value).TotalMilliseconds > TouchMouseInputFilterDelay); } } protected override void OnMouseDown(MouseEventArgs e) { if (ProcessMouseAction) { PerformDownAction(e.Location); _LastTouchAction = null; } base.OnMouseDown(e); } private DateTime _LastMouseUpEvent = DateTime.MinValue; private Key _LastKeyUp = null; private void PerformUpAction(Point location) { if (IsTopBarVisible && CloseButtonRectangle.Contains(location)) { if (_PressedKey == HideKeyboardKey) { HideKeyboard(); _PressedKey = null; } CancelRepeatKey(); } else { Key pressedKey = _PressedKey; CancelRepeatKey(); if (pressedKey != null) { KeyboardLayout currentLayout = Keyboard.CurrentLayout; Rectangle rectKeys = KeysRectangle; float fx = (float)rectKeys.Width / currentLayout.LogicalWidth; float fy = (float)rectKeys.Height / currentLayout.LogicalHeight; _LastMouseUpEvent = DateTime.Now; _LastKeyUp = pressedKey; if (pressedKey.ChangeToLayout >= 0) { Keyboard.CurrentLayoutIndex = pressedKey.ChangeToLayout; Invalidate(); // Ok to invalidate entire control because we need to draw a whole new layout. } else if (pressedKey.ChangeToLayout == KeyboardLayout.PreviousLayout) { Keyboard.ChangeToPreviousLayout(); Invalidate(); // Ok to invalidate entire control because we need to draw a whole new layout. } else { Invalidate(GetKeyPhysicalBounds(pressedKey, fx, fy)); // Only invalidate area under the currently pressed key. } } } this.Update(); } protected override void OnMouseUp(MouseEventArgs e) { if (ProcessMouseAction) { PerformUpAction(e.Location); base.OnMouseUp(e); } _LastTouchAction = null; } private Rectangle GetKeyPhysicalBounds(Key key, float scaleFactorX, float scaleFactorY) { Rectangle r = key.Bounds; r.X = (int)(r.X * scaleFactorX) + Border; r.Y = (int)(r.Y * scaleFactorY) + Border + TopBarVisibleHeight; r.Width = (int)(r.Width * scaleFactorX); r.Height = (int)(r.Height * scaleFactorY); return r; } protected override void Dispose(bool disposing) { if (disposing) { if (_DefaultColorTable != null) _DefaultColorTable.Dispose(); if (_RepeatTimer != null) _RepeatTimer.Dispose(); } base.Dispose(disposing); } /// /// Attaches the Keyboard to the specified control. The keyboard will automatically appear when the control receives input focus. /// /// The control to which the Keyboard will be attached. public void AttachTo(Control control) { control.GotFocus += new EventHandler(control_GotFocus); control.LostFocus += new EventHandler(control_LostFocus); } /// /// Detaches the Keyboard from the specified control. /// /// The control from which the Keyboard will be detached. public void DetachFrom(Control control) { control.GotFocus -= new EventHandler(control_GotFocus); control.LostFocus -= new EventHandler(control_LostFocus); } void control_GotFocus(object sender, EventArgs e) { Show(); } void control_LostFocus(object sender, EventArgs e) { HideKeyboard(); } private void HideKeyboard() { CancelEventArgs ce = new CancelEventArgs(); OnKeyboardClosing(ce); if (ce.Cancel) return; this.Hide(); OnKeyboardClosed(EventArgs.Empty); } /// /// Occurs before the keyboard is closed and allows canceling of keyboard closing. /// [Description("Occurs before the keyboard is closed and allows canceling of keyboard closing.")] public event CancelEventHandler KeyboardClosing; /// /// Raises KeyboardClosing event. /// /// Provides event arguments. protected virtual void OnKeyboardClosing(CancelEventArgs e) { CancelEventHandler handler = KeyboardClosing; if (handler != null) handler(this, e); } /// /// Occurs after keyboard is closed. /// [Description("Occurs after keyboard is closed.")] public event EventHandler KeyboardClosed; /// /// Raises KeyboardClosed event. /// /// Provides event arguments. protected virtual void OnKeyboardClosed(EventArgs e) { EventHandler handler = KeyboardClosed; if (handler != null) handler(this, e); } /// /// Occurs before the key pressed on keyboard is sent to target control and allows cancellation of the message /// [Description("Occurs before the key pressed on keyboard is sent to target control and allows cancellation of the message.")] public event KeyCancelEventHandler SendingKey; /// /// Raises SendingKey event. /// /// Provides event arguments. protected virtual void OnSendingKey(KeyboardKeyCancelEventArgs e) { KeyCancelEventHandler handler = SendingKey; if (handler != null) handler(this, e); } /// /// Occurs after the key is sent to target control. /// [Description("Occurs after the key is sent to target control.")] public event KeyEventHandler KeySent; /// /// Raises KeySent event. /// /// Provides event arguments. protected virtual void OnKeySent(KeyboardKeyEventArgs e) { KeyEventHandler handler = KeySent; if (handler != null) handler(this, e); } protected override void OnTextChanged(EventArgs e) { this.Invalidate(); base.OnTextChanged(e); } } /// /// Defines delegate for key-related events. /// /// /// public delegate void KeyEventHandler(object sender, KeyboardKeyEventArgs e); /// /// Provides data for key related events. /// public class KeyboardKeyEventArgs : EventArgs { /// /// Gets the key being pressed. The format of this information /// must confirm to the description for the SendKeys.Send function. For more details, see: /// http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx /// public readonly string Key; /// /// Initializes a new instance of the KeyboardKeyEventArgs class. /// /// public KeyboardKeyEventArgs(string key) { Key = key; } } /// /// Defines delegate for key-related events. /// /// /// public delegate void KeyCancelEventHandler(object sender, KeyboardKeyCancelEventArgs e); /// /// Provides data for key related events. /// public class KeyboardKeyCancelEventArgs : CancelEventArgs { /// /// Gets the key being pressed. The format of this information /// must confirm to the description for the SendKeys.Send function. For more details, see: /// http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx /// public readonly string Key; /// /// Initializes a new instance of the KeyCancelEventArgs class. /// /// public KeyboardKeyCancelEventArgs(string key) { Key = key; } } }