diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index 047bb47..d0e5f88 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -241,9 +241,6 @@ Component - - Component - Component diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index deb9380..2818a81 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs @@ -12,7 +12,7 @@ using System.Windows.Forms; namespace Acacia.Controls { - public abstract class KAbstractComboBox : ContainerControl + public abstract class KAbstractComboBox : ContainerControl, IMessageFilter { #region Properties @@ -41,6 +41,19 @@ namespace Acacia.Controls set { _edit.PlaceholderFont = value; } } + protected Control DropControl + { + get + { + return _dropControl; + } + set + { + _dropControl = value; + SetupDropDown(); + } + } + #endregion @@ -64,8 +77,28 @@ namespace Acacia.Controls _edit.TextChanged += _edit_TextChanged; _edit.LostFocus += _edit_LostFocus; _edit.PreviewKeyDown += _edit_PreviewKeyDown; + _edit.Leave += _edit_Leave; + _edit.Enter += _edit_Enter; + + Application.AddMessageFilter(this); } + private void _edit_Enter(object sender, EventArgs e) + { + System.Diagnostics.Trace.WriteLine(string.Format("_edit_Enter")); + } + + private void _edit_Leave(object sender, EventArgs e) + { + System.Diagnostics.Trace.WriteLine(string.Format("_edit_Leave")); + DroppedDown = false; + } + + protected override void OnHandleDestroyed(EventArgs e) + { + Application.RemoveMessageFilter(this); + base.OnHandleDestroyed(e); + } #endregion @@ -96,7 +129,7 @@ namespace Acacia.Controls if (DroppedDown) { DroppedDown = false; - e.IsInputKey = false; + e.IsInputKey = true; return; } break; @@ -113,24 +146,10 @@ namespace Acacia.Controls OnPreviewKeyDown(e); } - [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)] - internal static extern IntPtr GetFocus(); - - private Control GetFocusedControl() - { - Control focusedControl = null; - // To get hold of the focused control: - IntPtr focusedHandle = GetFocus(); - if (focusedHandle != IntPtr.Zero) - // Note that if the focused Control is not a .Net control, then this will return null. - focusedControl = Control.FromHandle(focusedHandle); - return focusedControl; - } - private void _edit_LostFocus(object sender, EventArgs e) { - System.Diagnostics.Trace.WriteLine("_edit_LostFocus: " + GetFocusedControl()?.Name); DroppedDown = false; + System.Diagnostics.Trace.WriteLine(string.Format("_edit_LostFocus")); } protected override void OnGotFocus(EventArgs e) @@ -148,33 +167,49 @@ namespace Acacia.Controls #region Drop down - public Control DropControl + private class DropDownRenderer : ToolStripRenderer { - get + private readonly KVisualStyle.Part _style; + + public DropDownRenderer(KVisualStyle.Part style) { - return _dropControl; + this._style = style; } - set + + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { - _dropControl = value; - SetupDropDown(); + _style.DrawBackground(e.Graphics, State.Pressed, e.AffectedBounds); } } - private ToolStripDropDown _dropDown; + private class DropDown : ToolStripDropDown + { + public DropDown(DropDownRenderer renderer) + { + Renderer = renderer; + } + } + + private DropDown _dropDown; private Control _dropControl; private ToolStripControlHost _dropListHost; private void SetupDropDown() { _dropListHost = new ToolStripControlHost(_dropControl); + _dropListHost.Padding = new Padding(0); _dropListHost.Margin = new Padding(0); - _dropListHost.AutoSize = false; + _dropListHost.AutoSize = true; _dropListHost.GotFocus += (s, e) => System.Diagnostics.Trace.WriteLine("_dropListHost.GotFocus"); - _dropDown = new ToolStripDropDown(); + _dropDown = new DropDown(new DropDownRenderer(_style[COMBOBOXPARTS.CP_BORDER])); _dropDown.Padding = new Padding(0); + using (Graphics graphics = CreateGraphics()) + { + Padding insets = _style[COMBOBOXPARTS.CP_BORDER]?.GetMargins(graphics, State.Hot) ?? new Padding(); + _dropDown.Padding = insets; + } _dropDown.Margin = new Padding(0); _dropDown.AutoSize = true; _dropDown.DropShadowEnabled = false; @@ -213,20 +248,27 @@ namespace Acacia.Controls { if (value) { - // Calculate the height of the control + // Calculate the dimensions of the dropdown int maxHeight = GetDropDownHeightMax(); int minHeight = GetDropDownHeightMin(); - Size prefSize = _dropControl.GetPreferredSize(new Size(Width, maxHeight)); - _dropControl.Width = Util.Bound(prefSize.Width, Width, Width * 2); - _dropControl.Height = Util.Bound(prefSize.Height, minHeight, maxHeight); + //Size prefSize = new Size(minHeight, maxHeight); + Size prefSize = _dropControl.GetPreferredSize(new Size(Width - _dropDown.Padding.Horizontal, maxHeight - _dropDown.Padding.Vertical)); + int width = Util.Bound(prefSize.Width, Width - _dropDown.Padding.Horizontal, Width * 2); + int height = Util.Bound(prefSize.Height, minHeight, maxHeight); + + System.Diagnostics.Trace.WriteLine(string.Format("DROPDOWN1: {0} - {1} - {2}", prefSize, width, + ((ListBox)_dropControl).ItemHeight)); + _dropControl.MaximumSize = _dropControl.MinimumSize = new Size(width, height); + // Show the drop down below the current control - _dropDown.Show(this.PointToScreen(new Point(0, Height))); - _dropControl.Capture = true; + _dropDown.Show(this.PointToScreen(new Point(0, Height - 1))); + //_dropListHost.Height = _dropDown.Height - _dropDown.Padding.Vertical; + System.Diagnostics.Trace.WriteLine(string.Format("DROPDOWN2: {0} - {1} - {2} - {3}: {4}", + _dropDown.Width, _dropListHost.Width, _dropControl.Width, width, this.Width)); } else { _dropDown.Close(); - _dropControl.Capture = false; } _isDroppedDown = value; } @@ -348,5 +390,60 @@ namespace Acacia.Controls } #endregion + + #region Message filtering + + public bool PreFilterMessage(ref Message m) + { + switch ((WM)m.Msg) + { + case WM.LBUTTONDOWN: + case WM.RBUTTONDOWN: + case WM.MBUTTONDOWN: + return CheckMouseDown(m, false); + case WM.NCLBUTTONDOWN: + case WM.NCRBUTTONDOWN: + case WM.NCMBUTTONDOWN: + return CheckMouseDown(m, true); + + } + return false; + } + + private bool CheckMouseDown(Message m, bool nonClient) + { + if (_dropDown.Visible) + { + // + // When a mouse button is pressed, we should determine if it is within the client coordinates + // of the active dropdown. If not, we should dismiss it. + // + int i = unchecked((int)(long)m.LParam); + short x = (short)(i & 0xFFFF); + short y = (short)((i >> 16) & 0xffff); + Point pt = new Point(x, y); + Point ptOrig = pt; + if (!nonClient) + { + // Map to global coordinates + User32.MapWindowPoints(m.HWnd, IntPtr.Zero, ref pt, 1); + } + System.Diagnostics.Trace.WriteLine(string.Format("MOUSE: {0} - {1} - {2}", pt, _dropDown.Bounds, ptOrig)); + if (!_dropDown.Bounds.Contains(pt)) + { + System.Diagnostics.Trace.WriteLine(string.Format("CONTAINS1")); + User32.MapWindowPoints(m.HWnd, Handle, ref pt, 1); + if (!ClientRectangle.Contains(pt)) + { + DroppedDown = false; + return false; + } + } + } + return false; + } + + #endregion + } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index bc950ca..6de6d78 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -14,7 +14,111 @@ namespace Acacia.Controls { public class KComboBox : KAbstractComboBox { - protected readonly KListBox _list; + private class KListBox : ListBox + { + private readonly KComboBox _owner; + private int _hoverIndex = -1; + + public KListBox(KComboBox owner) + { + this._owner = owner; + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + DrawMode = DrawMode.OwnerDrawFixed; + SetStyle(ControlStyles.Selectable, false); + //ItemHeight = 23; + BorderStyle = BorderStyle.None; + } + + protected override void OnMouseMove(MouseEventArgs e) + { + int newIndex = IndexFromPoint(PointToClient(Cursor.Position)); + if (newIndex != _hoverIndex) + { + int oldIndex = _hoverIndex; + _hoverIndex = newIndex; + InvalidateItem(oldIndex); + InvalidateItem(_hoverIndex); + if (SelectedIndex != oldIndex && SelectedIndex != _hoverIndex) + InvalidateItem(SelectedIndex); + } + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + _hoverIndex = -1; + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + // Perform the select here. + // TODO: this really is for ComboBox, where the list hides before the event is handled + SelectedIndex = IndexFromPoint(PointToClient(Cursor.Position)); + } + + protected override void OnVisibleChanged(EventArgs e) + { + base.OnVisibleChanged(e); + _hoverIndex = -1; + } + + private void InvalidateItem(int index) + { + if (index < 0 || index >= Items.Count) + return; + Invalidate(GetItemRectangle(index)); + } + + protected override void OnDrawItem(DrawItemEventArgs e) + { + // Create a custom event instance to be able to set the selected state for mouse hover + DrawItemState state = e.State; + if (_hoverIndex >= 0) + { + state = _hoverIndex == e.Index ? DrawItemState.Selected : DrawItemState.None; + } + DrawItemEventArgs draw = new DrawItemEventArgs(e.Graphics, e.Font, e.Bounds, e.Index, state); + draw.DrawBackground(); + + string text = Items[draw.Index].ToString(); + using (StringFormat format = new StringFormat()) + { + format.LineAlignment = StringAlignment.Center; + using (Brush brush = new SolidBrush(draw.ForeColor)) + { + draw.Graphics.DrawString(text, + draw.Font, brush, + draw.Bounds, + format); + } + } + } + + protected override void DefWndProc(ref Message m) + { + const int WM_MOUSEACTIVATE = 0x21; + const int MA_NOACTIVATE = 0x0003; + + switch (m.Msg) + { + // Prevent mouse activity from grabbing the focus away from the edit + case WM_MOUSEACTIVATE: + m.Result = (IntPtr)MA_NOACTIVATE; + return; + } + base.DefWndProc(ref m); + } + + public override Size GetPreferredSize(Size proposedSize) + { + Size prefSize = base.GetPreferredSize(proposedSize); + return new Size(prefSize.Width, ItemHeight * _owner.MaxDropDownItems); + } + } + + private readonly KListBox _list; + private int _ignoreListEvents; #region Items properties @@ -45,7 +149,7 @@ namespace Acacia.Controls public KComboBox() { MaxDropDownItems = 8; - _list = new KListBox(); + _list = new KListBox(this); _list.IntegralHeight = true; _list.TabStop = false; DropControl = _list; @@ -62,7 +166,7 @@ namespace Acacia.Controls private void _list_SelectedIndexChanged(object sender, EventArgs e) { - if (_list.SelectedIndex >= 0) + if (_list.SelectedIndex >= 0 && _ignoreListEvents == 0) { Text = _list.SelectedItem.ToString(); } @@ -88,7 +192,16 @@ namespace Acacia.Controls set { _list.BindingContext = new BindingContext(); - _list.DataSource = value; + ++_ignoreListEvents; + try + { + _list.DataSource = value; + _list.SelectedIndex = -1; + } + finally + { + --_ignoreListEvents; + } } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs deleted file mode 100644 index 9738892..0000000 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace Acacia.Controls -{ - public class KListBox : ListBox - { - private int _hoverIndex = -1; - - public KListBox() - { - SetStyle(ControlStyles.OptimizedDoubleBuffer, true); - DrawMode = DrawMode.OwnerDrawFixed; - SetStyle(ControlStyles.Selectable, true); - } - - protected override void OnMouseMove(MouseEventArgs e) - { - int newIndex = IndexFromPoint(PointToClient(Cursor.Position)); - if (newIndex != _hoverIndex) - { - int oldIndex = _hoverIndex; - _hoverIndex = newIndex; - InvalidateItem(oldIndex); - InvalidateItem(_hoverIndex); - if (SelectedIndex != oldIndex && SelectedIndex != _hoverIndex) - InvalidateItem(SelectedIndex); - } - } - - protected override void OnMouseLeave(EventArgs e) - { - base.OnMouseLeave(e); - _hoverIndex = -1; - } - - protected override void OnMouseDown(MouseEventArgs e) - { - base.OnMouseDown(e); - // Perform the select here. - // TODO: this really is for ComboBox, where the list hides before the event is handled - SelectedIndex = IndexFromPoint(PointToClient(Cursor.Position)); - } - - protected override void OnVisibleChanged(EventArgs e) - { - base.OnVisibleChanged(e); - _hoverIndex = -1; - } - - private void InvalidateItem(int index) - { - if (index < 0) - return; - Invalidate(GetItemRectangle(index)); - } - - protected override void OnDrawItem(DrawItemEventArgs e) - { - // Create a custom event instance to be able to set the selected state for mouse hover - DrawItemState state = e.State; - if (_hoverIndex >= 0) - { - state = _hoverIndex == e.Index ? DrawItemState.Selected : DrawItemState.None; - } - DrawItemEventArgs draw = new DrawItemEventArgs(e.Graphics, e.Font, e.Bounds, e.Index, state); - draw.DrawBackground(); - - string text = Items[draw.Index].ToString(); - using (StringFormat format = new StringFormat()) - { - format.LineAlignment = StringAlignment.Center; - using (Brush brush = new SolidBrush(draw.ForeColor)) - { - draw.Graphics.DrawString(text, - draw.Font, brush, - draw.Bounds, - format); - } - } - } - } -} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs index d7c5b89..0821ad2 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs @@ -218,7 +218,7 @@ namespace Acacia.UI BeginUpdate(); DataSource = users; //SetItemsCore(users); - if (dropDown) + if (dropDown && text.Length != 0) DroppedDown = true; //Cursor.Current = Cursors.Default; //Text = _lastText;