From 500499a67e19bf6ebeb1986cb89eaa9cb83d8691 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Wed, 28 Jun 2017 08:54:51 +0200 Subject: [PATCH 01/14] Implemented key forwarding from combo box edit to list --- .../Controls/KAbstractComboBox.cs | 34 +++++++++++++++---- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 28 ++++++++++++++- .../AcaciaZPushPlugin/Utils/Util.cs | 10 ++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index d335623..f57563b 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs @@ -1,4 +1,5 @@ using Acacia.Native; +using Acacia.Utils; using System; using System.Collections.Generic; using System.ComponentModel; @@ -61,6 +62,12 @@ namespace Acacia.Controls Controls.Add(_edit); _state.AddControl(_edit); _edit.TextChanged += _edit_TextChanged; + _edit.LostFocus += _edit_LostFocus; + } + + private void _edit_LostFocus(object sender, EventArgs e) + { + DroppedDown = false; } @@ -135,7 +142,6 @@ namespace Acacia.Controls switch ((WM)m.Msg) { case WM.KEYDOWN: - System.Diagnostics.Trace.WriteLine("KEYMESSAGE: " + m); switch((VirtualKeys)m.WParam.ToInt32()) { case VirtualKeys.Escape: @@ -155,10 +161,11 @@ namespace Acacia.Controls } break; } + ForwardKeyMessage(m); break; case WM.CHAR: case WM.KEYUP: - System.Diagnostics.Trace.WriteLine("KEYMESSAGE: " + m); + ForwardKeyMessage(m); break; case WM.LBUTTONDOWN: case WM.RBUTTONDOWN: @@ -196,6 +203,15 @@ namespace Acacia.Controls return false; } + private void ForwardKeyMessage(Message m) + { + System.Diagnostics.Trace.WriteLine("KEY: " + m); + if ((WM)m.Msg == WM.KEYDOWN) + { + OnPreviewKeyDown(new PreviewKeyDownEventArgs((Keys)m.WParam.ToInt32())); + } + } + // Cannot use visibility of _dropDown to keep the open state, as clicking on the button already // hides the popup before the event handler is shown. private bool _isDroppedDown; @@ -234,9 +250,13 @@ namespace Acacia.Controls { if (value) { - _dropListHost.Control.Width = this.Width; - _dropListHost.Control.Height = 200; - _dropListHost.Control.Refresh(); + // Calculate the height of the control + 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); + // Show the drop down below the current control _dropDown.Show(this.PointToScreen(new Point(0, Height))); _dropDown.Capture = true; } @@ -249,6 +269,9 @@ namespace Acacia.Controls } } + protected abstract int GetDropDownHeightMax(); + protected abstract int GetDropDownHeightMin(); + #endregion #region Rendering @@ -306,7 +329,6 @@ namespace Acacia.Controls protected override void OnPaint(PaintEventArgs e) { _style[COMBOBOXPARTS.CP_BORDER]?.DrawBackground(e.Graphics, _state.Root.State, ClientRectangle); - System.Diagnostics.Trace.WriteLine(string.Format("BUTTON: {0}", _stateButton.State)); _style[COMBOBOXPARTS.CP_DROPDOWNBUTTON]?.DrawBackground(e.Graphics, _stateButton.State, _stateButton.Rectangle); } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 463d29a..58017fc 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -1,4 +1,6 @@ -using System; +using Acacia.Native; +using Acacia.Utils; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; @@ -72,5 +74,29 @@ namespace Acacia.Controls _list.DataSource = value; } } + + protected override int GetDropDownHeightMax() + { + return Util.Bound(Items.Count, 1, MaxDropDownItems) * ItemHeight + _list.Margin.Vertical; + } + + protected override int GetDropDownHeightMin() + { + return ItemHeight; + } + + protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e) + { + switch(e.KeyCode) + { + case Keys.Down: + case Keys.Up: + User32.SendMessage(_list.Handle, (int)WM.KEYDOWN, new IntPtr((int)e.KeyCode), IntPtr.Zero); + break; + default: + base.OnPreviewKeyDown(e); + break; + } + } } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs index 27bb50c..921da7a 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs @@ -179,5 +179,15 @@ namespace Acacia.Utils int additional = (align - (size % align)) % align; return size + additional; } + + public static NumType Bound(NumType value, NumType min, NumType max) + where NumType : IComparable + { + if (value.CompareTo(min) < 0) + return min; + if (value.CompareTo(max) > 0) + return max; + return value; + } } } From ddec4f5bfc8c43502b97742ad233af63f5717323 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Wed, 28 Jun 2017 10:10:34 +0200 Subject: [PATCH 02/14] Added highlight of dropdown items on mouse move --- .../AcaciaZPushPlugin.csproj | 3 + .../Controls/KAbstractComboBox.cs | 10 --- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 10 ++- .../AcaciaZPushPlugin/Controls/KListBox.cs | 78 +++++++++++++++++++ 4 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index d0e5f88..047bb47 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -241,6 +241,9 @@ Component + + Component + Component diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index f57563b..71df804 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs @@ -215,23 +215,14 @@ namespace Acacia.Controls // Cannot use visibility of _dropDown to keep the open state, as clicking on the button already // hides the popup before the event handler is shown. private bool _isDroppedDown; - private bool _clickedButton; private void _dropDown_Closed(object sender, ToolStripDropDownClosedEventArgs e) { - /*if (_stateButton.IsMouseOver) - { - _clickedButton = true; - }*/ _isDroppedDown = false; } private void Button_Clicked() { - /*if (_clickedButton) - _clickedButton = false; - else - DroppedDown = true;*/ DroppedDown = !DroppedDown; this._edit.Focus(); } @@ -258,7 +249,6 @@ namespace Acacia.Controls _dropControl.Height = Util.Bound(prefSize.Height, minHeight, maxHeight); // Show the drop down below the current control _dropDown.Show(this.PointToScreen(new Point(0, Height))); - _dropDown.Capture = true; } else { diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 58017fc..ac0b30d 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -14,7 +14,7 @@ namespace Acacia.Controls { public class KComboBox : KAbstractComboBox { - private readonly ListBox _list; + protected readonly KListBox _list; #region Items properties @@ -45,10 +45,16 @@ namespace Acacia.Controls public KComboBox() { MaxDropDownItems = 8; - _list = new ListBox(); + _list = new KListBox(); _list.IntegralHeight = true; DropControl = _list; _list.DisplayMember = "DisplayName"; // TODO: remove from here + _list.SelectedIndexChanged += _list_SelectedIndexChanged; + } + + private void _list_SelectedIndexChanged(object sender, EventArgs e) + { + System.Diagnostics.Trace.WriteLine("SELECTED: " + _list.SelectedIndex); } public void BeginUpdate() diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs new file mode 100644 index 0000000..b22b389 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs @@ -0,0 +1,78 @@ +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; + } + + protected override void OnMouseMove(MouseEventArgs e) + { + Point point = PointToClient(Cursor.Position); + int newIndex = IndexFromPoint(point); + if (newIndex != _hoverIndex) + { + int oldIndex = _hoverIndex; + _hoverIndex = newIndex; + InvalidateItem(oldIndex); + InvalidateItem(_hoverIndex); + } + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + _hoverIndex = -1; + } + + 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); + } + } + } + } +} From 2490952c0bc2d9ef2497b0306f45431f8a349320 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Wed, 28 Jun 2017 11:58:25 +0200 Subject: [PATCH 03/14] Simplified event handling for combo box, without message filter --- .../Controls/KAbstractComboBox.cs | 164 ++++++++---------- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 15 +- .../AcaciaZPushPlugin/Controls/KListBox.cs | 14 +- 3 files changed, 95 insertions(+), 98 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index 71df804..deb9380 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, IMessageFilter + public abstract class KAbstractComboBox : ContainerControl { #region Properties @@ -63,11 +63,7 @@ namespace Acacia.Controls _state.AddControl(_edit); _edit.TextChanged += _edit_TextChanged; _edit.LostFocus += _edit_LostFocus; - } - - private void _edit_LostFocus(object sender, EventArgs e) - { - DroppedDown = false; + _edit.PreviewKeyDown += _edit_PreviewKeyDown; } @@ -75,15 +71,15 @@ namespace Acacia.Controls #region Text edit - private void _edit_TextChanged(object sender, EventArgs e) - { - OnTextChanged(new EventArgs()); - } - override public string Text { get { return _edit.Text; } - set { _edit.Text = value; } + set + { + _edit.Text = value; + // Set the cursor after the text + _edit.Select(_edit.Text.Length, 0); + } } public void FocusEdit() @@ -91,6 +87,63 @@ namespace Acacia.Controls _edit.Select(); } + private void _edit_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + switch (e.KeyCode) + { + case Keys.Escape: + // Escape closes the dropdown + if (DroppedDown) + { + DroppedDown = false; + e.IsInputKey = false; + return; + } + break; + case Keys.Down: + // Down opens the drop down + if (!DroppedDown) + { + DroppedDown = true; + e.IsInputKey = false; + return; + } + break; + } + 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; + } + + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + System.Diagnostics.Trace.WriteLine("OnGotFocus"); + } + + private void _edit_TextChanged(object sender, EventArgs e) + { + OnTextChanged(new EventArgs()); + } + #endregion #region Drop down @@ -118,6 +171,7 @@ namespace Acacia.Controls _dropListHost.Padding = new Padding(0); _dropListHost.Margin = new Padding(0); _dropListHost.AutoSize = false; + _dropListHost.GotFocus += (s, e) => System.Diagnostics.Trace.WriteLine("_dropListHost.GotFocus"); _dropDown = new ToolStripDropDown(); _dropDown.Padding = new Padding(0); @@ -127,89 +181,7 @@ namespace Acacia.Controls _dropDown.Items.Add(_dropListHost); _dropDown.Closed += _dropDown_Closed; _dropDown.AutoClose = false; - - Application.AddMessageFilter(this); - } - - protected override void OnHandleDestroyed(EventArgs e) - { - Application.RemoveMessageFilter(this); - base.OnHandleDestroyed(e); - } - - public bool PreFilterMessage(ref Message m) - { - switch ((WM)m.Msg) - { - case WM.KEYDOWN: - switch((VirtualKeys)m.WParam.ToInt32()) - { - case VirtualKeys.Escape: - // Escape closes the popup - if (DroppedDown) - { - DroppedDown = false; - return true; - } - break; - case VirtualKeys.Down: - // Down opens the drop down - if (!DroppedDown) - { - DroppedDown = true; - return true; - } - break; - } - ForwardKeyMessage(m); - break; - case WM.CHAR: - case WM.KEYUP: - ForwardKeyMessage(m); - break; - case WM.LBUTTONDOWN: - case WM.RBUTTONDOWN: - case WM.MBUTTONDOWN: - case WM.NCLBUTTONDOWN: - case WM.NCRBUTTONDOWN: - case WM.NCMBUTTONDOWN: - 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); - - // Map to global coordinates - User32.MapWindowPoints(m.HWnd, IntPtr.Zero, ref pt, 1); - System.Diagnostics.Trace.WriteLine(string.Format("MOUSE: {0} - {1}", pt, _dropDown.Bounds)); - if (!_dropDown.Bounds.Contains(pt)) - { - // the user has clicked outside the dropdown - User32.MapWindowPoints(m.HWnd, Handle, ref pt, 1); - if (!ClientRectangle.Contains(pt)) - { - // the user has clicked outside the combo - DroppedDown = false; - } - } - } - break; - } - return false; - } - - private void ForwardKeyMessage(Message m) - { - System.Diagnostics.Trace.WriteLine("KEY: " + m); - if ((WM)m.Msg == WM.KEYDOWN) - { - OnPreviewKeyDown(new PreviewKeyDownEventArgs((Keys)m.WParam.ToInt32())); - } + _dropDown.GotFocus += (s, e) => System.Diagnostics.Trace.WriteLine("_dropDown.GotFocus"); } // Cannot use visibility of _dropDown to keep the open state, as clicking on the button already @@ -249,10 +221,12 @@ namespace Acacia.Controls _dropControl.Height = Util.Bound(prefSize.Height, minHeight, maxHeight); // Show the drop down below the current control _dropDown.Show(this.PointToScreen(new Point(0, Height))); + _dropControl.Capture = true; } else { _dropDown.Close(); + _dropControl.Capture = false; } _isDroppedDown = value; } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index ac0b30d..bc950ca 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -47,14 +47,25 @@ namespace Acacia.Controls MaxDropDownItems = 8; _list = new KListBox(); _list.IntegralHeight = true; + _list.TabStop = false; DropControl = _list; _list.DisplayMember = "DisplayName"; // TODO: remove from here _list.SelectedIndexChanged += _list_SelectedIndexChanged; + _list.GotFocus += _list_GotFocus; + } + + private void _list_GotFocus(object sender, EventArgs e) + { + System.Diagnostics.Trace.WriteLine("_list_GotFocus"); + } private void _list_SelectedIndexChanged(object sender, EventArgs e) { - System.Diagnostics.Trace.WriteLine("SELECTED: " + _list.SelectedIndex); + if (_list.SelectedIndex >= 0) + { + Text = _list.SelectedItem.ToString(); + } } public void BeginUpdate() @@ -95,9 +106,11 @@ namespace Acacia.Controls { switch(e.KeyCode) { + // Forward cursor keys to the list case Keys.Down: case Keys.Up: User32.SendMessage(_list.Handle, (int)WM.KEYDOWN, new IntPtr((int)e.KeyCode), IntPtr.Zero); + e.IsInputKey = false; break; default: base.OnPreviewKeyDown(e); diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs index b22b389..9738892 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs @@ -16,18 +16,20 @@ namespace Acacia.Controls { SetStyle(ControlStyles.OptimizedDoubleBuffer, true); DrawMode = DrawMode.OwnerDrawFixed; + SetStyle(ControlStyles.Selectable, true); } protected override void OnMouseMove(MouseEventArgs e) { - Point point = PointToClient(Cursor.Position); - int newIndex = IndexFromPoint(point); + 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); } } @@ -37,6 +39,14 @@ namespace Acacia.Controls _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); From 5371e0ee435d6565752d2162d1e1332676435c6f Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Wed, 28 Jun 2017 14:47:12 +0200 Subject: [PATCH 04/14] Fixed dimensions of combo box dropdown --- .../AcaciaZPushPlugin.csproj | 3 - .../Controls/KAbstractComboBox.cs | 163 ++++++++++++++---- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 121 ++++++++++++- .../AcaciaZPushPlugin/Controls/KListBox.cs | 88 ---------- .../AcaciaZPushPlugin/UI/GABLookupControl.cs | 2 +- 5 files changed, 248 insertions(+), 129 deletions(-) delete mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KListBox.cs 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; From a0640739e8b56cb6293678f6fc03f1ba542e4570 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 11:35:44 +0200 Subject: [PATCH 05/14] Finalised mouse handling for combo box --- .../Controls/KAbstractComboBox.cs | 244 +++++++++--------- .../AcaciaZPushPlugin/Native/User32.cs | 15 ++ 2 files changed, 143 insertions(+), 116 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index 2818a81..8300883 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, IMessageFilter + public abstract class KAbstractComboBox : ContainerControl { #region Properties @@ -79,8 +79,6 @@ namespace Acacia.Controls _edit.PreviewKeyDown += _edit_PreviewKeyDown; _edit.Leave += _edit_Leave; _edit.Enter += _edit_Enter; - - Application.AddMessageFilter(this); } private void _edit_Enter(object sender, EventArgs e) @@ -94,12 +92,6 @@ namespace Acacia.Controls DroppedDown = false; } - protected override void OnHandleDestroyed(EventArgs e) - { - Application.RemoveMessageFilter(this); - base.OnHandleDestroyed(e); - } - #endregion #region Text edit @@ -167,56 +159,125 @@ namespace Acacia.Controls #region Drop down - private class DropDownRenderer : ToolStripRenderer + /// + /// Custom drop down. Registers a message filter when shown to close on clicks outside the dropdown. + /// This is required as the default AutoClose behaviour consumes all keyboard events. + /// + private class DropDown : ToolStripDropDown, IMessageFilter { - private readonly KVisualStyle.Part _style; - - public DropDownRenderer(KVisualStyle.Part style) + /// + /// Custom renderer that renders the border using the combo focus style. + /// + private class DropDownRenderer : ToolStripRenderer { - this._style = style; + private readonly KVisualStyle.Part _style; + + public DropDownRenderer(KVisualStyle.Part style) + { + this._style = style; + } + + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) + { + _style.DrawBackground(e.Graphics, State.Pressed, e.AffectedBounds); + } } - protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) - { - _style.DrawBackground(e.Graphics, State.Pressed, e.AffectedBounds); - } - } + private readonly KAbstractComboBox _owner; - private class DropDown : ToolStripDropDown - { - public DropDown(DropDownRenderer renderer) + public DropDown(KAbstractComboBox owner) { - Renderer = renderer; + this._owner = owner; + + KVisualStyle.Part style = owner._style[COMBOBOXPARTS.CP_BORDER]; + Renderer = new DropDownRenderer(style); + using (Graphics graphics = CreateGraphics()) + { + Padding = style?.GetMargins(graphics, State.Pressed) ?? new Padding(); + } + + Margin = new Padding(0); + AutoSize = true; + DropShadowEnabled = false; + AutoClose = false; + + // Add a host for the control + ToolStripControlHost host = new ToolStripControlHost(owner._dropControl); + host.Padding = new Padding(0); + host.Margin = new Padding(0); + host.AutoSize = true; + Items.Add(host); + } + + protected override void OnVisibleChanged(EventArgs e) + { + base.OnVisibleChanged(e); + + if (Visible) + Application.AddMessageFilter(this); + else + Application.RemoveMessageFilter(this); + } + + 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) + { + Point pt = User32.GetPointLParam(m.LParam); + Point ptOrig = pt; + if (!nonClient) + { + // Map to global coordinates, non-client ones already are + User32.MapWindowPoints(m.HWnd, IntPtr.Zero, ref pt, 1); + } + + // Check if the click was inside the dropdown + if (!Bounds.Contains(pt)) + { + // Outside the dropdown, check if it was inside the combo box + + // Map to the combo box coordinates + User32.MapWindowPoints(IntPtr.Zero, _owner.Handle, ref pt, 1); + if (_owner.ClientRectangle.Contains(pt)) + { + // Clicked inside the combo box. If the click was on the button, return true to prevent opening + // the popup again. + if (_owner._stateButton.Rectangle.Contains(pt)) + { + return true; + } + } + else + { + // Outside the dropdown, close it + Close(); + } + } + return false; } } 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 = true; - _dropListHost.GotFocus += (s, e) => System.Diagnostics.Trace.WriteLine("_dropListHost.GotFocus"); - - _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; - _dropDown.Items.Add(_dropListHost); + _dropDown = new DropDown(this); _dropDown.Closed += _dropDown_Closed; - _dropDown.AutoClose = false; - _dropDown.GotFocus += (s, e) => System.Diagnostics.Trace.WriteLine("_dropDown.GotFocus"); } // Cannot use visibility of _dropDown to keep the open state, as clicking on the button already @@ -230,6 +291,7 @@ namespace Acacia.Controls private void Button_Clicked() { + System.Diagnostics.Trace.WriteLine("Button_Clicked"); DroppedDown = !DroppedDown; this._edit.Focus(); } @@ -248,23 +310,7 @@ namespace Acacia.Controls { if (value) { - // Calculate the dimensions of the dropdown - int maxHeight = GetDropDownHeightMax(); - int minHeight = GetDropDownHeightMin(); - //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 - 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)); + ShowDropDown(); } else { @@ -275,6 +321,27 @@ namespace Acacia.Controls } } + private void ShowDropDown() + { + // Calculate the dimensions of the dropdown + int maxHeight = GetDropDownHeightMax(); + int minHeight = GetDropDownHeightMin(); + //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 - 1))); + //_dropListHost.Height = _dropDown.Height - _dropDown.Padding.Vertical; + System.Diagnostics.Trace.WriteLine(string.Format("DROPDOWN2: {0} - {1} - {2} - {3}: {4}", + _dropDown.Width, 0, _dropControl.Width, width, this.Width)); + } + protected abstract int GetDropDownHeightMax(); protected abstract int GetDropDownHeightMin(); @@ -390,60 +457,5 @@ 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/Native/User32.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/User32.cs index c8f0687..9a00021 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/User32.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/User32.cs @@ -78,6 +78,21 @@ namespace Acacia.Native [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] ref Point pt, int cPoints); + public static int GetXLParam(IntPtr lParam) + { + return lParam.ToInt32() & 0xFFFF; + } + + public static int GetYLParam(IntPtr lParam) + { + return (lParam.ToInt32() >> 16) & 0xFFFF; + } + + public static Point GetPointLParam(IntPtr lParam) + { + return new Point(GetXLParam(lParam), GetYLParam(lParam)); + } + #endregion #region Messages From af97aae748c859b095db3cf7d21ed1f64a603e5c Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 12:36:56 +0200 Subject: [PATCH 06/14] Clean ups to combo box code --- .../Controls/KAbstractComboBox.cs | 151 ++++++++---------- 1 file changed, 63 insertions(+), 88 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index 8300883..098c547 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs @@ -16,6 +16,9 @@ namespace Acacia.Controls { #region Properties + /// + /// Hide the AutoSize property, it is always enabled + /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] override public bool AutoSize { get { return base.AutoSize; } set { base.AutoSize = value; } } @@ -41,61 +44,22 @@ namespace Acacia.Controls set { _edit.PlaceholderFont = value; } } + /// + /// The control to set in the drop-down + /// protected Control DropControl { get { - return _dropControl; + return _dropDown?.Control; } set { - _dropControl = value; - SetupDropDown(); + _dropDown = new DropDown(this, value); } } - #endregion - - #region Components - - private KTextBox _edit; - - #endregion - - #region Init - - public KAbstractComboBox() - { - AutoSize = true; - SetupRenderer(); - - _edit = new KTextBox(); - _edit.BorderStyle = BorderStyle.None; - Controls.Add(_edit); - _state.AddControl(_edit); - _edit.TextChanged += _edit_TextChanged; - _edit.LostFocus += _edit_LostFocus; - _edit.PreviewKeyDown += _edit_PreviewKeyDown; - _edit.Leave += _edit_Leave; - _edit.Enter += _edit_Enter; - } - - 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; - } - - #endregion - - #region Text edit - override public string Text { get { return _edit.Text; } @@ -107,6 +71,34 @@ namespace Acacia.Controls } } + #endregion + + #region Init + + public KAbstractComboBox() + { + AutoSize = true; + SetupRenderer(); + SetupEdit(); + } + + #endregion + + #region Text edit + + private KTextBox _edit; + + private void SetupEdit() + { + _edit = new KTextBox(); + _edit.BorderStyle = BorderStyle.None; + Controls.Add(_edit); + _state.AddControl(_edit); + _edit.TextChanged += _edit_TextChanged; + _edit.LostFocus += _edit_LostFocus; + _edit.PreviewKeyDown += _edit_PreviewKeyDown; + } + public void FocusEdit() { _edit.Select(); @@ -117,10 +109,11 @@ namespace Acacia.Controls switch (e.KeyCode) { case Keys.Escape: - // Escape closes the dropdown + // Escape closes the drop-down if (DroppedDown) { DroppedDown = false; + // Grab the keypress to prevent closing a dialog e.IsInputKey = true; return; } @@ -130,7 +123,7 @@ namespace Acacia.Controls if (!DroppedDown) { DroppedDown = true; - e.IsInputKey = false; + e.IsInputKey = true; return; } break; @@ -140,14 +133,9 @@ namespace Acacia.Controls private void _edit_LostFocus(object sender, EventArgs e) { + // Close the drop down when losing focus. This also handles the case when another window is selected, + // as that causes the focus to be taken away DroppedDown = false; - System.Diagnostics.Trace.WriteLine(string.Format("_edit_LostFocus")); - } - - protected override void OnGotFocus(EventArgs e) - { - base.OnGotFocus(e); - System.Diagnostics.Trace.WriteLine("OnGotFocus"); } private void _edit_TextChanged(object sender, EventArgs e) @@ -160,7 +148,7 @@ namespace Acacia.Controls #region Drop down /// - /// Custom drop down. Registers a message filter when shown to close on clicks outside the dropdown. + /// Custom drop down. Registers a message filter when shown to close on clicks outside the drop-down. /// This is required as the default AutoClose behaviour consumes all keyboard events. /// private class DropDown : ToolStripDropDown, IMessageFilter @@ -185,7 +173,15 @@ namespace Acacia.Controls private readonly KAbstractComboBox _owner; - public DropDown(KAbstractComboBox owner) + public Control Control + { + get + { + return ((ToolStripControlHost)Items[0]).Control; + } + } + + public DropDown(KAbstractComboBox owner, Control control) { this._owner = owner; @@ -202,7 +198,7 @@ namespace Acacia.Controls AutoClose = false; // Add a host for the control - ToolStripControlHost host = new ToolStripControlHost(owner._dropControl); + ToolStripControlHost host = new ToolStripControlHost(control); host.Padding = new Padding(0); host.Margin = new Padding(0); host.AutoSize = true; @@ -213,6 +209,7 @@ namespace Acacia.Controls { base.OnVisibleChanged(e); + // Only register the message filter when it can do something useful if (Visible) Application.AddMessageFilter(this); else @@ -221,6 +218,7 @@ namespace Acacia.Controls public bool PreFilterMessage(ref Message m) { + // Handle mouse clicks to close the popup switch ((WM)m.Msg) { case WM.LBUTTONDOWN: @@ -245,10 +243,10 @@ namespace Acacia.Controls User32.MapWindowPoints(m.HWnd, IntPtr.Zero, ref pt, 1); } - // Check if the click was inside the dropdown + // Check if the click was inside the drop-down if (!Bounds.Contains(pt)) { - // Outside the dropdown, check if it was inside the combo box + // Outside the drop-down, check if it was inside the combo box // Map to the combo box coordinates User32.MapWindowPoints(IntPtr.Zero, _owner.Handle, ref pt, 1); @@ -263,7 +261,7 @@ namespace Acacia.Controls } else { - // Outside the dropdown, close it + // Outside the drop-down, close it Close(); } } @@ -272,26 +270,9 @@ namespace Acacia.Controls } private DropDown _dropDown; - private Control _dropControl; - - private void SetupDropDown() - { - _dropDown = new DropDown(this); - _dropDown.Closed += _dropDown_Closed; - } - - // Cannot use visibility of _dropDown to keep the open state, as clicking on the button already - // hides the popup before the event handler is shown. - private bool _isDroppedDown; - - private void _dropDown_Closed(object sender, ToolStripDropDownClosedEventArgs e) - { - _isDroppedDown = false; - } private void Button_Clicked() { - System.Diagnostics.Trace.WriteLine("Button_Clicked"); DroppedDown = !DroppedDown; this._edit.Focus(); } @@ -301,12 +282,12 @@ namespace Acacia.Controls { get { - return _isDroppedDown; + return _dropDown?.Visible == true; } set { - if (value != _isDroppedDown) + if (value != DroppedDown) { if (value) { @@ -316,30 +297,24 @@ namespace Acacia.Controls { _dropDown.Close(); } - _isDroppedDown = value; } } } private void ShowDropDown() { - // Calculate the dimensions of the dropdown + // Calculate the dimensions of the drop-down int maxHeight = GetDropDownHeightMax(); int minHeight = GetDropDownHeightMin(); - //Size prefSize = new Size(minHeight, maxHeight); - Size prefSize = _dropControl.GetPreferredSize(new Size(Width - _dropDown.Padding.Horizontal, maxHeight - _dropDown.Padding.Vertical)); + + 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); + DropControl.MaximumSize = DropControl.MinimumSize = new Size(width, height); // Show the drop down below the current control _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, 0, _dropControl.Width, width, this.Width)); } protected abstract int GetDropDownHeightMax(); From fdf8350afb933b5f0ff7e0a38b742219c52007a0 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 13:07:14 +0200 Subject: [PATCH 07/14] Clean ups to combobox --- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 62 +++++++++---------- .../AcaciaZPushPlugin/Native/WM.cs | 7 +++ 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 6de6d78..895a3db 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -14,31 +14,35 @@ namespace Acacia.Controls { public class KComboBox : KAbstractComboBox { - private class KListBox : ListBox + #region Drop-down list + + private class DropList : ListBox { private readonly KComboBox _owner; - private int _hoverIndex = -1; + private int _highlightIndex = -1; - public KListBox(KComboBox owner) + public DropList(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) { + // Use the mouse to highlight the current item int newIndex = IndexFromPoint(PointToClient(Cursor.Position)); - if (newIndex != _hoverIndex) + if (newIndex != _highlightIndex) { - int oldIndex = _hoverIndex; - _hoverIndex = newIndex; + int oldIndex = _highlightIndex; + _highlightIndex = newIndex; + + // Invalidate the affected items, which may include a previously selected one InvalidateItem(oldIndex); - InvalidateItem(_hoverIndex); - if (SelectedIndex != oldIndex && SelectedIndex != _hoverIndex) + InvalidateItem(_highlightIndex); + if (SelectedIndex != oldIndex && SelectedIndex != _highlightIndex) InvalidateItem(SelectedIndex); } } @@ -46,21 +50,21 @@ namespace Acacia.Controls protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); - _hoverIndex = -1; + _highlightIndex = -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 + + // Perform the select when the mouse is clicked SelectedIndex = IndexFromPoint(PointToClient(Cursor.Position)); } protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); - _hoverIndex = -1; + _highlightIndex = -1; } private void InvalidateItem(int index) @@ -74,9 +78,9 @@ namespace Acacia.Controls { // Create a custom event instance to be able to set the selected state for mouse hover DrawItemState state = e.State; - if (_hoverIndex >= 0) + if (_highlightIndex >= 0) { - state = _hoverIndex == e.Index ? DrawItemState.Selected : DrawItemState.None; + state = _highlightIndex == e.Index ? DrawItemState.Selected : DrawItemState.None; } DrawItemEventArgs draw = new DrawItemEventArgs(e.Graphics, e.Font, e.Bounds, e.Index, state); draw.DrawBackground(); @@ -97,14 +101,11 @@ namespace Acacia.Controls protected override void DefWndProc(ref Message m) { - const int WM_MOUSEACTIVATE = 0x21; - const int MA_NOACTIVATE = 0x0003; - - switch (m.Msg) + switch ((WM)m.Msg) { // Prevent mouse activity from grabbing the focus away from the edit - case WM_MOUSEACTIVATE: - m.Result = (IntPtr)MA_NOACTIVATE; + case WM.MOUSEACTIVATE: + m.Result = (IntPtr)MA.NOACTIVATE; return; } base.DefWndProc(ref m); @@ -112,12 +113,15 @@ namespace Acacia.Controls public override Size GetPreferredSize(Size proposedSize) { + // Preferred size is simply the size of the (maximum) number of items Size prefSize = base.GetPreferredSize(proposedSize); - return new Size(prefSize.Width, ItemHeight * _owner.MaxDropDownItems); + return new Size(prefSize.Width, ItemHeight * Math.Min(Items.Count, _owner.MaxDropDownItems)); } } - private readonly KListBox _list; + #endregion + + private readonly DropList _list; private int _ignoreListEvents; #region Items properties @@ -149,19 +153,11 @@ namespace Acacia.Controls public KComboBox() { MaxDropDownItems = 8; - _list = new KListBox(this); + _list = new DropList(this); _list.IntegralHeight = true; _list.TabStop = false; - DropControl = _list; - _list.DisplayMember = "DisplayName"; // TODO: remove from here _list.SelectedIndexChanged += _list_SelectedIndexChanged; - _list.GotFocus += _list_GotFocus; - } - - private void _list_GotFocus(object sender, EventArgs e) - { - System.Diagnostics.Trace.WriteLine("_list_GotFocus"); - + DropControl = _list; } private void _list_SelectedIndexChanged(object sender, EventArgs e) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs index ab75ce2..9b53dff 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs @@ -23,8 +23,15 @@ using System.Text; namespace Acacia.Native { + public enum MA : int + { + NOACTIVATE = 0x0003 + } + public enum WM : int { + MOUSEACTIVATE = 0x0021, + NCHITTEST = 0x0084, NCPAINT = 0x0085, From acfc47f018c3fde140658e621e43f1608b0b77a4 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 13:37:55 +0200 Subject: [PATCH 08/14] Fixed keyboard handling in combo box --- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 110 +++++++----------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 895a3db..38146fe 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -16,87 +16,49 @@ namespace Acacia.Controls { #region Drop-down list + /// + /// Custom list for the drop-down. Performs a few functions: + /// - Prevents grabbing the focus away from the edit when clicked + /// - Adds hover highlighting + /// - Only commits selection when clicked or externally (through enter in the edit). + /// This prevents updating the text and associated filters when scrolling through the combo. + /// private class DropList : ListBox { private readonly KComboBox _owner; - private int _highlightIndex = -1; + private int _committedIndex = -1; public DropList(KComboBox owner) { this._owner = owner; SetStyle(ControlStyles.OptimizedDoubleBuffer, true); - DrawMode = DrawMode.OwnerDrawFixed; SetStyle(ControlStyles.Selectable, false); BorderStyle = BorderStyle.None; } protected override void OnMouseMove(MouseEventArgs e) { - // Use the mouse to highlight the current item - int newIndex = IndexFromPoint(PointToClient(Cursor.Position)); - if (newIndex != _highlightIndex) - { - int oldIndex = _highlightIndex; - _highlightIndex = newIndex; - - // Invalidate the affected items, which may include a previously selected one - InvalidateItem(oldIndex); - InvalidateItem(_highlightIndex); - if (SelectedIndex != oldIndex && SelectedIndex != _highlightIndex) - InvalidateItem(SelectedIndex); - } + // Perform the select to highlight + SelectedIndex = IndexFromPoint(PointToClient(Cursor.Position)); } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); - _highlightIndex = -1; - } - - protected override void OnMouseDown(MouseEventArgs e) - { - base.OnMouseDown(e); - - // Perform the select when the mouse is clicked - SelectedIndex = IndexFromPoint(PointToClient(Cursor.Position)); + SelectedIndex = _committedIndex; } protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); - _highlightIndex = -1; + SelectedIndex = _committedIndex; } - private void InvalidateItem(int index) + protected override void OnMouseDown(MouseEventArgs e) { - 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 (_highlightIndex >= 0) - { - state = _highlightIndex == 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); - } - } + // Select the item under the mouse and commit + SelectedIndex = IndexFromPoint(PointToClient(Cursor.Position)); + CommitSelection(); } protected override void DefWndProc(ref Message m) @@ -117,12 +79,22 @@ namespace Acacia.Controls Size prefSize = base.GetPreferredSize(proposedSize); return new Size(prefSize.Width, ItemHeight * Math.Min(Items.Count, _owner.MaxDropDownItems)); } + + public void CommitSelection() + { + _committedIndex = SelectedIndex; + base.OnSelectedIndexChanged(new EventArgs()); + } + + protected override void OnSelectedIndexChanged(EventArgs e) + { + // Don't notify until committed + } } #endregion private readonly DropList _list; - private int _ignoreListEvents; #region Items properties @@ -162,10 +134,14 @@ namespace Acacia.Controls private void _list_SelectedIndexChanged(object sender, EventArgs e) { - if (_list.SelectedIndex >= 0 && _ignoreListEvents == 0) + if (_list.SelectedIndex >= 0) { Text = _list.SelectedItem.ToString(); } + else + { + Text = ""; + } } public void BeginUpdate() @@ -188,16 +164,8 @@ namespace Acacia.Controls set { _list.BindingContext = new BindingContext(); - ++_ignoreListEvents; - try - { - _list.DataSource = value; - _list.SelectedIndex = -1; - } - finally - { - --_ignoreListEvents; - } + _list.DataSource = value; + _list.SelectedIndex = -1; } } @@ -219,7 +187,15 @@ namespace Acacia.Controls case Keys.Down: case Keys.Up: User32.SendMessage(_list.Handle, (int)WM.KEYDOWN, new IntPtr((int)e.KeyCode), IntPtr.Zero); - e.IsInputKey = false; + e.IsInputKey = true; + break; + + // Enter commits the selected index and closes the drop down + case Keys.Enter: + case Keys.Tab: + _list.CommitSelection(); + DroppedDown = false; + e.IsInputKey = e.KeyCode == Keys.Enter; break; default: base.OnPreviewKeyDown(e); From 27f1eb7b8a5db1faed753703bcb912821e0eee80 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 15:01:35 +0200 Subject: [PATCH 09/14] Implemented basic filtering for combo box items --- .../AcaciaZPushPlugin.csproj | 1 + .../Controls/KAbstractComboBox.cs | 30 ++++- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 117 +++++++++++++++--- .../AcaciaZPushPlugin/Controls/KDataSource.cs | 79 ++++++++++++ .../AcaciaZPushPlugin/UI/GABLookupControl.cs | 2 +- 5 files changed, 203 insertions(+), 26 deletions(-) create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index d0e5f88..e95a424 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -229,6 +229,7 @@ Component + UserControl diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index 098c547..96a95d0 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs @@ -175,10 +175,12 @@ namespace Acacia.Controls public Control Control { - get - { - return ((ToolStripControlHost)Items[0]).Control; - } + get { return ControlHost.Control; } + } + + public ToolStripControlHost ControlHost + { + get { return (ToolStripControlHost)Items[0]; } } public DropDown(KAbstractComboBox owner, Control control) @@ -303,6 +305,17 @@ namespace Acacia.Controls private void ShowDropDown() { + UpdateDropDownLayout(); + + // Show the drop down below the current control + _dropDown.Show(this.PointToScreen(new Point(0, Height - 1))); + } + + protected void UpdateDropDownLayout() + { + if (_dropDown == null) + return; + // Calculate the dimensions of the drop-down int maxHeight = GetDropDownHeightMax(); int minHeight = GetDropDownHeightMin(); @@ -313,8 +326,13 @@ namespace Acacia.Controls DropControl.MaximumSize = DropControl.MinimumSize = new Size(width, height); - // Show the drop down below the current control - _dropDown.Show(this.PointToScreen(new Point(0, Height - 1))); + _dropDown.Control.Bounds = _dropDown.ControlHost.Bounds; + System.Diagnostics.Trace.WriteLine(string.Format( + "Layout: {0}, host: {1}, control: {2}", + height, + _dropDown.ControlHost.Bounds, + _dropDown.Control.Bounds + )); } protected abstract int GetDropDownHeightMax(); diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 38146fe..3f14873 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -45,13 +45,17 @@ namespace Acacia.Controls protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); - SelectedIndex = _committedIndex; + ResetSelectedIndex(); } protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); - SelectedIndex = _committedIndex; + } + + private void ResetSelectedIndex() + { + SelectedIndex = _committedIndex >= Items.Count ? -1 : _committedIndex; } protected override void OnMouseDown(MouseEventArgs e) @@ -90,12 +94,18 @@ namespace Acacia.Controls { // Don't notify until committed } + + public void ItemsChanged(int selectIndex) + { + _committedIndex = SelectedIndex = selectIndex; + + } } - #endregion - private readonly DropList _list; + #endregion + #region Items properties [DefaultValue(true)] @@ -108,13 +118,6 @@ namespace Acacia.Controls [Category("Behavior")] public int ItemHeight { get { return _list.ItemHeight; } set { _list.ItemHeight = value; } } - [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] - [Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))] - [Localizable(true)] - [MergableProperty(false)] - [Category("Behavior")] - public ListBox.ObjectCollection Items { get { return _list.Items; } } - [DefaultValue(8)] [Localizable(true)] [Category("Behavior")] @@ -122,6 +125,8 @@ namespace Acacia.Controls #endregion + private DisplayItem _selectedItem; + public KComboBox() { MaxDropDownItems = 8; @@ -136,11 +141,13 @@ namespace Acacia.Controls { if (_list.SelectedIndex >= 0) { - Text = _list.SelectedItem.ToString(); + _selectedItem = (DisplayItem)_list.SelectedItem; + Text = _selectedItem.ToString(); } else { Text = ""; + _selectedItem = null; } } @@ -154,24 +161,96 @@ namespace Acacia.Controls _list.EndUpdate(); } - public object DataSource + /// + /// Wrapper for list items to use custom string formatting + /// + private class DisplayItem { - get + private readonly KComboBox _owner; + private readonly object _item; + + public DisplayItem(KComboBox owner, object item) { - return _list.DataSource; + this._owner = owner; + this._item = item; } + public override string ToString() + { + return _owner.DataSource.GetItemText(_item); + } + + public override bool Equals(object obj) + { + bool result = obj is DisplayItem && ((DisplayItem)obj)._item == _item; + return result; + } + + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + } + + private KDataSourceRaw _dataSource; + public KDataSourceRaw DataSource + { + get { return _dataSource; } set { - _list.BindingContext = new BindingContext(); - _list.DataSource = value; - _list.SelectedIndex = -1; + if (_dataSource != value) + { + _dataSource = value; + UpdateItems(); + } + } + } + + private void UpdateItems() + { + int oldCount = _list.Items.Count; + _list.BeginUpdate(); + try + { + _list.Items.Clear(); + int selected = -1; + foreach (object item in _dataSource.FilteredItems) + { + DisplayItem displayItem = new DisplayItem(this, item); + if (displayItem.Equals(_selectedItem)) + selected = _list.Items.Count; + _list.Items.Add(displayItem); + } + System.Diagnostics.Trace.WriteLine(string.Format("FILTER: {0}", _list.Items.Count, selected)); + + // Select the current item only if new number of items is smaller. This means we don't keep selection + // when the user is removing text, only when they are typing more. + _list.ItemsChanged(_list.Items.Count < oldCount ? selected : -1); + } + finally + { + _list.EndUpdate(); + } + UpdateDropDownLayout(); + } + + protected override void OnTextChanged(EventArgs e) + { + base.OnTextChanged(e); + + // Update the filter + if (DataSource != null) + { + DataSource.Filter = new KDataFilter(Text); + UpdateItems(); + + DroppedDown = true; } } protected override int GetDropDownHeightMax() { - return Util.Bound(Items.Count, 1, MaxDropDownItems) * ItemHeight + _list.Margin.Vertical; + return Util.Bound(_list.Items.Count, 1, MaxDropDownItems) * ItemHeight + _list.Margin.Vertical; } protected override int GetDropDownHeightMin() diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs new file mode 100644 index 0000000..779738d --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Acacia.Controls +{ + public class KDataFilter + { + public readonly string FilterText; + + public KDataFilter(string filterText) + { + this.FilterText = filterText; + } + } + + public interface KDataSourceRaw + { + System.Collections.IEnumerable Items { get; } + System.Collections.IEnumerable FilteredItems { get; } + KDataFilter Filter { get; set; } + string GetItemText(object item); + } + + abstract public class KDataSource : KDataSourceRaw + { + /// + /// Returns all the items + /// + abstract public IEnumerable Items + { + get; + } + + public IEnumerable FilteredItems + { + get + { + if (string.IsNullOrWhiteSpace(Filter?.FilterText)) + return Items; + + return ApplyFilter(); + } + } + + private IEnumerable ApplyFilter() + { + foreach (T item in Items) + { + if (MatchesFilter(item)) + yield return item; + } + } + + virtual protected bool MatchesFilter(T item) + { + return GetItemText(item).StartsWith(Filter.FilterText); + } + + abstract protected string GetItemText(T item); + + public string GetItemText(object item) + { + return GetItemText((T)item); + } + + public KDataFilter Filter + { + get; + set; + } + + IEnumerable KDataSourceRaw.Items { get{return Items;}} + IEnumerable KDataSourceRaw.FilteredItems { get { return FilteredItems; } } + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs index 0821ad2..6e2fbca 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs @@ -216,7 +216,7 @@ namespace Acacia.UI // Setting the datasource will trigger a select if there is a match BeginUpdate(); - DataSource = users; + //DataSource = users; //SetItemsCore(users); if (dropDown && text.Length != 0) DroppedDown = true; From a27f65e3d11c37eef16bd1393459051566d87e66 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 15:50:21 +0200 Subject: [PATCH 10/14] Added owner drawing of combo box items --- .../AcaciaZPushPlugin.csproj | 3 + .../AcaciaZPushPlugin/Controls/KComboBox.cs | 74 +++++++++++++--- .../Controls/KComboBoxCustomDraw.cs | 85 +++++++++++++++++++ .../AcaciaZPushPlugin/Controls/KUIUtil.cs | 7 +- 4 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index e95a424..e9e4b33 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -226,6 +226,9 @@ Component + + Component + Component diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 3f14873..309b49c 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -28,12 +28,22 @@ namespace Acacia.Controls private readonly KComboBox _owner; private int _committedIndex = -1; - public DropList(KComboBox owner) + public DropList(KComboBox owner, bool ownerDraw) { this._owner = owner; SetStyle(ControlStyles.OptimizedDoubleBuffer, true); SetStyle(ControlStyles.Selectable, false); BorderStyle = BorderStyle.None; + + if (ownerDraw) + { + DrawMode = DrawMode.OwnerDrawFixed; + } + } + + protected override void OnDrawItem(DrawItemEventArgs e) + { + _owner.OnDrawItem(e); } protected override void OnMouseMove(MouseEventArgs e) @@ -98,7 +108,6 @@ namespace Acacia.Controls public void ItemsChanged(int selectIndex) { _committedIndex = SelectedIndex = selectIndex; - } } @@ -127,10 +136,14 @@ namespace Acacia.Controls private DisplayItem _selectedItem; - public KComboBox() + public KComboBox() : this(false) { + } + + protected internal KComboBox(bool ownerDraw) + { MaxDropDownItems = 8; - _list = new DropList(this); + _list = new DropList(this, ownerDraw); _list.IntegralHeight = true; _list.TabStop = false; _list.SelectedIndexChanged += _list_SelectedIndexChanged; @@ -164,31 +177,30 @@ namespace Acacia.Controls /// /// Wrapper for list items to use custom string formatting /// - private class DisplayItem + public class DisplayItem { private readonly KComboBox _owner; - private readonly object _item; + public readonly object Item; public DisplayItem(KComboBox owner, object item) { this._owner = owner; - this._item = item; + this.Item = item; } public override string ToString() { - return _owner.DataSource.GetItemText(_item); + return _owner.DataSource.GetItemText(Item); } public override bool Equals(object obj) { - bool result = obj is DisplayItem && ((DisplayItem)obj)._item == _item; - return result; + return obj is DisplayItem && ((DisplayItem)obj).Item == Item; } public override int GetHashCode() { - return ToString().GetHashCode(); + return Item.GetHashCode(); } } @@ -226,12 +238,46 @@ namespace Acacia.Controls // Select the current item only if new number of items is smaller. This means we don't keep selection // when the user is removing text, only when they are typing more. _list.ItemsChanged(_list.Items.Count < oldCount ? selected : -1); + + MeasureItems(); + UpdateDropDownLayout(); } finally { _list.EndUpdate(); } - UpdateDropDownLayout(); + } + + protected IEnumerable DisplayItems + { + get + { + foreach (object item in _list.Items) + yield return (DisplayItem)item; + } + } + + protected DisplayItem GetDisplayItem(int index) + { + return (DisplayItem)_list.Items[index]; + } + + protected int DisplayItemCount + { + get { return _list.Items.Count; } + } + + virtual protected void OnDrawItem(DrawItemEventArgs e) { } + + protected virtual void MeasureItems() + { + // Virtual placeholder + } + + protected void SetItemSize(Size size) + { + ItemHeight = size.Height; + _list.Width = size.Width; } protected override void OnTextChanged(EventArgs e) @@ -265,6 +311,10 @@ namespace Acacia.Controls // Forward cursor keys to the list case Keys.Down: case Keys.Up: + case Keys.PageDown: + case Keys.PageUp: + case Keys.Home: + case Keys.End: User32.SendMessage(_list.Handle, (int)WM.KEYDOWN, new IntPtr((int)e.KeyCode), IntPtr.Zero); e.IsInputKey = true; break; diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs new file mode 100644 index 0000000..cb15187 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs @@ -0,0 +1,85 @@ +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 +{ + abstract public class KComboBoxCustomDraw : KComboBox + { + public class MeasureItemEventArgs : EventArgs + { + public readonly Graphics Graphics; + public readonly DisplayItem DisplayItem; + public object Item { get { return DisplayItem.Item; } } + public int ItemWidth { get; set; } + public int ItemHeight { get; set; } + + public MeasureItemEventArgs(Graphics graphics, DisplayItem item) + { + this.Graphics = graphics; + this.DisplayItem = item; + } + } + + public class DrawItemEventArgs : System.Windows.Forms.DrawItemEventArgs + { + public readonly DisplayItem DisplayItem; + + public object Item { get { return DisplayItem.Item; } } + + public DrawItemEventArgs(System.Windows.Forms.DrawItemEventArgs e, DisplayItem item) + : + base(e.Graphics, e.Font, e.Bounds, e.Index, e.State, e.ForeColor, e.BackColor) + { + DisplayItem = item; + } + + } + + public KComboBoxCustomDraw() : base(true) + { + } + + sealed protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e) + { + OnDrawItem(new DrawItemEventArgs(e, GetDisplayItem(e.Index))); + } + + abstract protected void OnDrawItem(DrawItemEventArgs e); + + protected abstract void OnMeasureItem(MeasureItemEventArgs e); + + private readonly Dictionary _sizeCache = new Dictionary(); + + protected override void MeasureItems() + { + int maxWidth = 0, maxHeight = 0; + using (Graphics graphics = CreateGraphics()) + { + foreach (DisplayItem item in DisplayItems) + { + Size s; + if (!_sizeCache.TryGetValue(item, out s)) + { + MeasureItemEventArgs e = new MeasureItemEventArgs(graphics, item); + OnMeasureItem(e); + s = new Size(e.ItemWidth, e.ItemHeight); + _sizeCache.Add(item, s); + } + + maxWidth = Math.Max(maxWidth, s.Width); + maxHeight = Math.Max(maxHeight, s.Height); + } + } + + if (maxHeight > 0) + { + SetItemSize(new Size(maxWidth, maxHeight)); + } + } + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs index 2e4d15f..07ad8be 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KUIUtil.cs @@ -24,7 +24,7 @@ using System.Windows.Forms; namespace Acacia.Controls { - internal static class KUIUtil + public static class KUIUtil { #region Geometry @@ -126,6 +126,11 @@ namespace Acacia.Controls return r; } + public static Point TopLeft(this Rectangle _this) + { + return new Point(_this.Left, _this.Top); + } + #endregion } } From 2ac4ae623cd065f3c9d0f6a21bd84d9d288a88ff Mon Sep 17 00:00:00 2001 From: KOE_Export_Strings_Commit Date: Thu, 29 Jun 2017 16:51:37 +0200 Subject: [PATCH 11/14] Commit strings #201 --- translations/KOE.pot | 24 ++++++++++++++++++------ translations/de.po | 12 ++++++++++++ translations/en.po | 24 ++++++++++++++++++------ translations/fr.po | 12 ++++++++++++ translations/hu.po | 12 ++++++++++++ translations/it.po | 12 ++++++++++++ translations/nb.po | 12 ++++++++++++ translations/nl.po | 12 ++++++++++++ translations/pt_br.po | 12 ++++++++++++ 9 files changed, 120 insertions(+), 12 deletions(-) diff --git a/translations/KOE.pot b/translations/KOE.pot index 414a6ca..24eae6b 100644 --- a/translations/KOE.pot +++ b/translations/KOE.pot @@ -172,6 +172,18 @@ msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\labelS msgid "Open folders for user" msgstr "" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + #: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\buttonOpenUser.Text #, csharp-format msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\buttonOpenUser.Text" @@ -190,6 +202,12 @@ msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_label msgid "Send as owner" msgstr "" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\_labelReminders.Text +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_labelReminders.Text" +msgid "Show reminders" +msgstr "" + #: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\_labelPermissions.Text #, csharp-format msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_labelPermissions.Text" @@ -202,12 +220,6 @@ msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\labelP msgid "Permissions" msgstr "" -#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\_labelReminders.Text -#, csharp-format -msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_labelReminders.Text" -msgid "Show reminders" -msgstr "" - #: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\$this.Text #, csharp-format msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\$this.Text" diff --git a/translations/de.po b/translations/de.po index dd491ab..cc6647e 100644 --- a/translations/de.po +++ b/translations/de.po @@ -1211,3 +1211,15 @@ msgctxt "AcaciaZPushPlugin\\Properties\\Resources\\SharedFolders_PrivateEvent_Ti msgid "Private event" msgstr "Privater Termin" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + diff --git a/translations/en.po b/translations/en.po index 51b6004..68b4eda 100644 --- a/translations/en.po +++ b/translations/en.po @@ -172,6 +172,18 @@ msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\labelS msgid "Open folders for user" msgstr "Open folders for user" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "The user was not found" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "Start typing name" + #: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\buttonOpenUser.Text #, csharp-format msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\buttonOpenUser.Text" @@ -190,6 +202,12 @@ msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_label msgid "Send as owner" msgstr "Send as owner" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\_labelReminders.Text +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_labelReminders.Text" +msgid "Show reminders" +msgstr "Show reminders" + #: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\_labelPermissions.Text #, csharp-format msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_labelPermissions.Text" @@ -202,12 +220,6 @@ msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\labelP msgid "Permissions" msgstr "Permissions" -#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\_labelReminders.Text -#, csharp-format -msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\_labelReminders.Text" -msgid "Show reminders" -msgstr "Show reminders" - #: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\$this.Text #, csharp-format msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\$this.Text" diff --git a/translations/fr.po b/translations/fr.po index 939527c..4a4b509 100644 --- a/translations/fr.po +++ b/translations/fr.po @@ -1209,3 +1209,15 @@ msgctxt "AcaciaZPushPlugin\\Properties\\Resources\\SharedFolders_PrivateEvent_Ti msgid "Private event" msgstr "" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + diff --git a/translations/hu.po b/translations/hu.po index 6a669cb..ec970e7 100644 --- a/translations/hu.po +++ b/translations/hu.po @@ -1212,3 +1212,15 @@ msgctxt "AcaciaZPushPlugin\\Properties\\Resources\\SharedFolders_PrivateEvent_Ti msgid "Private event" msgstr "" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + diff --git a/translations/it.po b/translations/it.po index 95ae03b..2b6769c 100644 --- a/translations/it.po +++ b/translations/it.po @@ -1176,3 +1176,15 @@ msgctxt "AcaciaZPushPlugin\\Properties\\Resources\\SharedFolders_PrivateEvent_Ti msgid "Private event" msgstr "" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + diff --git a/translations/nb.po b/translations/nb.po index 092e0fd..00a458e 100644 --- a/translations/nb.po +++ b/translations/nb.po @@ -964,3 +964,15 @@ msgctxt "AcaciaZPushPlugin\\Properties\\Resources\\SharedFolders_PrivateEvent_Ti msgid "Private event" msgstr "" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + diff --git a/translations/nl.po b/translations/nl.po index 4389be7..c08d08f 100644 --- a/translations/nl.po +++ b/translations/nl.po @@ -1211,3 +1211,15 @@ msgctxt "AcaciaZPushPlugin\\Properties\\Resources\\SharedFolders_PrivateEvent_Ti msgid "Private event" msgstr "Privé afspraak" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + diff --git a/translations/pt_br.po b/translations/pt_br.po index 99461b7..6c42c38 100644 --- a/translations/pt_br.po +++ b/translations/pt_br.po @@ -966,3 +966,15 @@ msgctxt "AcaciaZPushPlugin\\Properties\\Resources\\SharedFolders_PrivateEvent_Ti msgid "Private event" msgstr "Evento particular" +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.NotFoundText +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.NotFoundText" +msgid "The user was not found" +msgstr "" + +#: AcaciaZPushPlugin\Features\SharedFolders\SharedFoldersDialog\gabLookup.Placeholder +#, csharp-format +msgctxt "AcaciaZPushPlugin\\Features\\SharedFolders\\SharedFoldersDialog\\gabLookup.Placeholder" +msgid "Start typing name" +msgstr "" + From d7c7c94a7e0cabd39ec35c840a851655e40cec67 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 18:27:48 +0200 Subject: [PATCH 12/14] Implemented GABLookupControl using new combo box --- .../Controls/KAbstractComboBox.cs | 21 +- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 64 +++- .../Controls/KComboBoxCustomDraw.cs | 2 +- .../SharedFoldersDialog.Designer.cs | 49 +-- .../SharedFolders/SharedFoldersDialog.resx | 241 +++++++------- .../AcaciaZPushPlugin/UI/GABLookupControl.cs | 300 ++++++++---------- .../AcaciaZPushPlugin/ZPush/GABUser.cs | 15 +- 7 files changed, 362 insertions(+), 330 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs index 96a95d0..7d7fd52 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KAbstractComboBox.cs @@ -59,15 +59,24 @@ namespace Acacia.Controls } } + protected int _settingText = 0; override public string Text { get { return _edit.Text; } set { - _edit.Text = value; - // Set the cursor after the text - _edit.Select(_edit.Text.Length, 0); + ++_settingText; + try + { + _edit.Text = value; + // Set the cursor after the text + _edit.Select(_edit.Text.Length, 0); + } + finally + { + --_settingText; + } } } @@ -327,12 +336,6 @@ namespace Acacia.Controls DropControl.MaximumSize = DropControl.MinimumSize = new Size(width, height); _dropDown.Control.Bounds = _dropDown.ControlHost.Bounds; - System.Diagnostics.Trace.WriteLine(string.Format( - "Layout: {0}, host: {1}, control: {2}", - height, - _dropDown.ControlHost.Bounds, - _dropDown.Control.Bounds - )); } protected abstract int GetDropDownHeightMax(); diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 309b49c..00aa0eb 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -27,6 +27,7 @@ namespace Acacia.Controls { private readonly KComboBox _owner; private int _committedIndex = -1; + public int ItemWidth { get; set; } public DropList(KComboBox owner, bool ownerDraw) { @@ -91,7 +92,8 @@ namespace Acacia.Controls { // Preferred size is simply the size of the (maximum) number of items Size prefSize = base.GetPreferredSize(proposedSize); - return new Size(prefSize.Width, ItemHeight * Math.Min(Items.Count, _owner.MaxDropDownItems)); + int w = Math.Max(prefSize.Width, ItemWidth); + return new Size(w, ItemHeight * Math.Min(Items.Count, _owner.MaxDropDownItems)); } public void CommitSelection() @@ -162,18 +164,41 @@ namespace Acacia.Controls Text = ""; _selectedItem = null; } + OnSelectedItemChanged(); } - public void BeginUpdate() + public DisplayItem SelectedItem { - _list.BeginUpdate(); + get { return _selectedItem; } } - public void EndUpdate() + public void Select(object data) { - _list.EndUpdate(); + _list.SelectedIndex = -1; + Text = null; + _selectedItem = null; + if (data != null) + { + foreach (DisplayItem item in DisplayItems) + { + if (item.Item.Equals(data)) + { + _list.SelectedItem = item; + _selectedItem = item; + break; + } + } + } } + public event EventHandler SelectedItemChanged; + + protected virtual void OnSelectedItemChanged() + { + SelectedItemChanged?.Invoke(this, new EventArgs()); + } + + /// /// Wrapper for list items to use custom string formatting /// @@ -213,11 +238,14 @@ namespace Acacia.Controls if (_dataSource != value) { _dataSource = value; + _displayItemCache.Clear(); UpdateItems(); } } } + private readonly Dictionary _displayItemCache = new Dictionary(); + private void UpdateItems() { int oldCount = _list.Items.Count; @@ -228,12 +256,17 @@ namespace Acacia.Controls int selected = -1; foreach (object item in _dataSource.FilteredItems) { - DisplayItem displayItem = new DisplayItem(this, item); - if (displayItem.Equals(_selectedItem)) + DisplayItem displayItem; + if (!_displayItemCache.TryGetValue(item, out displayItem)) + { + displayItem = new DisplayItem(this, item); + _displayItemCache.Add(item, displayItem); + } + + if (displayItem == _selectedItem) selected = _list.Items.Count; _list.Items.Add(displayItem); } - System.Diagnostics.Trace.WriteLine(string.Format("FILTER: {0}", _list.Items.Count, selected)); // Select the current item only if new number of items is smaller. This means we don't keep selection // when the user is removing text, only when they are typing more. @@ -277,7 +310,7 @@ namespace Acacia.Controls protected void SetItemSize(Size size) { ItemHeight = size.Height; - _list.Width = size.Width; + _list.ItemWidth = size.Width; } protected override void OnTextChanged(EventArgs e) @@ -290,7 +323,10 @@ namespace Acacia.Controls DataSource.Filter = new KDataFilter(Text); UpdateItems(); - DroppedDown = true; + if (_settingText == 0) + { + DroppedDown = true; + } } } @@ -322,8 +358,12 @@ namespace Acacia.Controls // Enter commits the selected index and closes the drop down case Keys.Enter: case Keys.Tab: - _list.CommitSelection(); - DroppedDown = false; + if (DroppedDown) + { + if (_list.SelectedIndex >= 0) + _list.CommitSelection(); + DroppedDown = false; + } e.IsInputKey = e.KeyCode == Keys.Enter; break; default: diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs index cb15187..5f5f86a 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBoxCustomDraw.cs @@ -49,7 +49,7 @@ namespace Acacia.Controls OnDrawItem(new DrawItemEventArgs(e, GetDisplayItem(e.Index))); } - abstract protected void OnDrawItem(DrawItemEventArgs e); + protected abstract void OnDrawItem(DrawItemEventArgs e); protected abstract void OnMeasureItem(MeasureItemEventArgs e); diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs index 479ae06..ae3b9b3 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs @@ -35,19 +35,19 @@ this._layoutMain = new System.Windows.Forms.TableLayoutPanel(); this._layoutSelectUser = new System.Windows.Forms.TableLayoutPanel(); this.labelSelectUser = new System.Windows.Forms.Label(); - this.buttonOpenUser = new System.Windows.Forms.Button(); this._layoutCenterGABLookup = new System.Windows.Forms.TableLayoutPanel(); this.gabLookup = new Acacia.UI.GABLookupControl(); + this.buttonOpenUser = new System.Windows.Forms.Button(); this.kTreeFolders = new Acacia.Controls.KTree(); this._layoutOptions = new System.Windows.Forms.TableLayoutPanel(); this._labelName = new System.Windows.Forms.Label(); this.textName = new System.Windows.Forms.TextBox(); this._labelSendAs = new System.Windows.Forms.Label(); this.checkSendAs = new System.Windows.Forms.CheckBox(); - this._labelPermissions = new System.Windows.Forms.Label(); - this.labelPermissionsValue = new System.Windows.Forms.Label(); this._labelReminders = new System.Windows.Forms.Label(); this.checkReminders = new System.Windows.Forms.CheckBox(); + this._labelPermissions = new System.Windows.Forms.Label(); + this.labelPermissionsValue = new System.Windows.Forms.Label(); this.dialogButtons = new Acacia.Controls.KDialogButtons(); this._layout.SuspendLayout(); this._mainBusyHider.SuspendLayout(); @@ -85,8 +85,8 @@ // resources.ApplyResources(this._layoutSelectUser, "_layoutSelectUser"); this._layoutSelectUser.Controls.Add(this.labelSelectUser, 0, 0); - this._layoutSelectUser.Controls.Add(this.buttonOpenUser, 2, 0); this._layoutSelectUser.Controls.Add(this._layoutCenterGABLookup, 1, 0); + this._layoutSelectUser.Controls.Add(this.buttonOpenUser, 2, 0); this._layoutSelectUser.Name = "_layoutSelectUser"; // // labelSelectUser @@ -94,13 +94,6 @@ resources.ApplyResources(this.labelSelectUser, "labelSelectUser"); this.labelSelectUser.Name = "labelSelectUser"; // - // buttonOpenUser - // - resources.ApplyResources(this.buttonOpenUser, "buttonOpenUser"); - this.buttonOpenUser.Name = "buttonOpenUser"; - this.buttonOpenUser.UseVisualStyleBackColor = true; - this.buttonOpenUser.Click += new System.EventHandler(this.buttonOpenUser_Click); - // // _layoutCenterGABLookup // resources.ApplyResources(this._layoutCenterGABLookup, "_layoutCenterGABLookup"); @@ -114,9 +107,18 @@ this.gabLookup.DroppedDown = false; this.gabLookup.GAB = null; this.gabLookup.Name = "gabLookup"; + this.gabLookup.PlaceholderColor = System.Drawing.Color.Gray; + this.gabLookup.PlaceholderFont = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.gabLookup.SelectedUser = null; this.gabLookup.SelectedUserChanged += new Acacia.UI.GABLookupControl.SelectedUserEventHandler(this.gabLookup_SelectedUserChanged); // + // buttonOpenUser + // + resources.ApplyResources(this.buttonOpenUser, "buttonOpenUser"); + this.buttonOpenUser.Name = "buttonOpenUser"; + this.buttonOpenUser.UseVisualStyleBackColor = true; + this.buttonOpenUser.Click += new System.EventHandler(this.buttonOpenUser_Click); + // // kTreeFolders // this.kTreeFolders.BackColor = System.Drawing.SystemColors.Window; @@ -140,10 +142,10 @@ this._layoutOptions.Controls.Add(this.textName, 1, 0); this._layoutOptions.Controls.Add(this._labelSendAs, 0, 1); this._layoutOptions.Controls.Add(this.checkSendAs, 1, 1); - this._layoutOptions.Controls.Add(this._labelPermissions, 0, 3); - this._layoutOptions.Controls.Add(this.labelPermissionsValue, 1, 3); this._layoutOptions.Controls.Add(this._labelReminders, 0, 2); this._layoutOptions.Controls.Add(this.checkReminders, 1, 2); + this._layoutOptions.Controls.Add(this._labelPermissions, 0, 3); + this._layoutOptions.Controls.Add(this.labelPermissionsValue, 1, 3); this._layoutOptions.Name = "_layoutOptions"; // // _labelName @@ -170,16 +172,6 @@ this.checkSendAs.UseVisualStyleBackColor = true; this.checkSendAs.CheckedChanged += new System.EventHandler(this.checkSendAs_CheckedChanged); // - // _labelPermissions - // - resources.ApplyResources(this._labelPermissions, "_labelPermissions"); - this._labelPermissions.Name = "_labelPermissions"; - // - // labelPermissionsValue - // - resources.ApplyResources(this.labelPermissionsValue, "labelPermissionsValue"); - this.labelPermissionsValue.Name = "labelPermissionsValue"; - // // _labelReminders // resources.ApplyResources(this._labelReminders, "_labelReminders"); @@ -192,6 +184,16 @@ this.checkReminders.UseVisualStyleBackColor = true; this.checkReminders.CheckedChanged += new System.EventHandler(this.checkReminders_CheckedChanged); // + // _labelPermissions + // + resources.ApplyResources(this._labelPermissions, "_labelPermissions"); + this._labelPermissions.Name = "_labelPermissions"; + // + // labelPermissionsValue + // + resources.ApplyResources(this.labelPermissionsValue, "labelPermissionsValue"); + this.labelPermissionsValue.Name = "labelPermissionsValue"; + // // dialogButtons // resources.ApplyResources(this.dialogButtons, "dialogButtons"); @@ -220,6 +222,7 @@ this._layoutSelectUser.ResumeLayout(false); this._layoutSelectUser.PerformLayout(); this._layoutCenterGABLookup.ResumeLayout(false); + this._layoutCenterGABLookup.PerformLayout(); this._layoutOptions.ResumeLayout(false); this._layoutOptions.PerformLayout(); this.ResumeLayout(false); diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx index 9d25f1f..2d9e44e 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx @@ -148,7 +148,7 @@ 3, 0 - 105, 31 + 105, 33 0 @@ -171,48 +171,6 @@ 0 - - True - - - GrowAndShrink - - - Fill - - - False - - - NoControl - - - 380, 3 - - - 8, 0, 8, 0 - - - 59, 25 - - - 1 - - - Open - - - buttonOpenUser - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - _layoutSelectUser - - - 1 - True @@ -228,8 +186,11 @@ 200, 0 + + + - 256, 21 + 256, 23 0 @@ -259,10 +220,10 @@ 3 - 262, 27 + 262, 29 - 2 + 1 _layoutCenterGABLookup @@ -274,11 +235,53 @@ _layoutSelectUser - 2 + 1 <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="gabLookup" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100" /><Rows Styles="Percent,50,AutoSize,0,Percent,50" /></TableLayoutSettings> + + True + + + GrowAndShrink + + + Fill + + + False + + + NoControl + + + 380, 3 + + + 8, 0, 8, 0 + + + 59, 27 + + + 2 + + + Open + + + buttonOpenUser + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _layoutSelectUser + + + 2 + Fill @@ -289,7 +292,7 @@ 1 - 442, 31 + 442, 33 0 @@ -307,16 +310,16 @@ 0 - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="labelSelectUser" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="buttonOpenUser" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="_layoutCenterGABLookup" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100,AutoSize,0" /><Rows Styles="Percent,100,Absolute,31" /></TableLayoutSettings> + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="labelSelectUser" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="_layoutCenterGABLookup" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="buttonOpenUser" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100,AutoSize,0" /><Rows Styles="Percent,100,Absolute,33" /></TableLayoutSettings> Fill - 3, 40 + 3, 42 - 442, 275 + 442, 273 0 @@ -468,6 +471,72 @@ 3 + + True + + + Fill + + + 3, 53 + + + 82, 27 + + + 6 + + + Show reminders + + + MiddleLeft + + + _labelReminders + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _layoutOptions + + + 4 + + + True + + + Left + + + 94, 57 + + + 6, 4, 3, 3 + + + 0, 3, 0, 3 + + + 15, 20 + + + 7 + + + checkReminders + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _layoutOptions + + + 5 + True @@ -502,7 +571,7 @@ _layoutOptions - 4 + 6 True @@ -541,72 +610,6 @@ _layoutOptions - 5 - - - True - - - Fill - - - 3, 53 - - - 82, 27 - - - 6 - - - Show reminders - - - MiddleLeft - - - _labelReminders - - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - _layoutOptions - - - 6 - - - True - - - Left - - - 94, 57 - - - 6, 4, 3, 3 - - - 0, 3, 0, 3 - - - 15, 20 - - - 7 - - - checkReminders - - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - _layoutOptions - - 7 @@ -640,7 +643,7 @@ 2 - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="_labelName" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="textName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelSendAs" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="checkSendAs" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelPermissions" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="labelPermissionsValue" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelReminders" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="checkReminders" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100" /><Rows Styles="AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0" /></TableLayoutSettings> + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="_labelName" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="textName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelSendAs" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="checkSendAs" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelReminders" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="checkReminders" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelPermissions" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="labelPermissionsValue" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100" /><Rows Styles="AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0" /></TableLayoutSettings> Fill diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs index 6e2fbca..7781420 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs @@ -1,4 +1,4 @@ -/// Copyright 2016 Kopano b.v. +/// Copyright 2017 Kopano b.v. /// /// This program is free software: you can redistribute it and/or modify /// it under the terms of the GNU Affero General Public License, version 3, @@ -31,8 +31,59 @@ using Acacia.Controls; namespace Acacia.UI { - public partial class GABLookupControl : KComboBox + public partial class GABLookupControl : KComboBoxCustomDraw { + private class GABDataSource : KDataSource + { + private readonly GABHandler _gab; + private readonly List _users; + + public GABDataSource(GABHandler gab) + { + this._gab = gab; + + _users = new List(); + foreach (IItem item in _gab.Contacts.Items.Sort("FullName", false)) + { + if (item is IContactItem) + _users.Add(new GABUser((IContactItem)item)); + } + } + + public override IEnumerable Items + { + get + { + return _users; + } + } + + protected override string GetItemText(GABUser item) + { + // If there is a filter, try to complete that + if (!string.IsNullOrEmpty(Filter?.FilterText)) + { + string s = Filter?.FilterText.ToLower(); + if (item.UserName?.ToLower().StartsWith(s) == true) + return item.UserName; + else if (item.FullName?.ToLower().StartsWith(s) == true) + return item.FullName; + else if (item.EmailAddress?.ToLower().StartsWith(s) == true) + return item.EmailAddress; + } + return item.UserName; + } + + protected override bool MatchesFilter(GABUser item) + { + string s = Filter.FilterText.ToLower(); + return + item.FullName?.ToLower().StartsWith(s) == true || + item.UserName?.ToLower().StartsWith(s) == true || + item.EmailAddress?.ToLower().StartsWith(s) == true; + } + } + public GABLookupControl() : this(null) { } @@ -40,7 +91,7 @@ namespace Acacia.UI public GABLookupControl(GABHandler gab) { InitializeComponent(); - this.GAB = gab; + GAB = gab; } #region Properties and events @@ -64,40 +115,31 @@ namespace Acacia.UI [Category("Behavior")] public event SelectedUserEventHandler SelectedUserChanged; + private GABUser _selectedUser; public GABUser SelectedUser { get { - /*if (SelectedValue == null) - return new GABUser(Text, Text); - else - return (GABUser)SelectedValue;*/ - return null; + return _selectedUser; } set { - /*if (value == null) - { - SelectedIndex = -1; - Text = ""; - } - else - { - - }*/ + _selectedUser = null; + Select(value); } } - private void SetSelectedUser(GABUser user, bool isChosen) + protected override void OnTextChanged(EventArgs e) { - if (SelectedUser != user || isChosen) - { - System.Diagnostics.Trace.WriteLine(string.Format("SELECT: {0} -> {1} : {2}", SelectedUser, user, isChosen)); - if (isChosen) - SelectedUser = user; - if (SelectedUserChanged != null) - SelectedUserChanged(this, new SelectedUserEventArgs(user, isChosen)); - } + base.OnTextChanged(e); + _selectedUser = string.IsNullOrEmpty(Text) ? null : new GABUser(Text); + SelectedUserChanged?.Invoke(this, new SelectedUserEventArgs(_selectedUser, false)); + } + + protected override void OnSelectedItemChanged() + { + _selectedUser = (GABUser)SelectedItem?.Item; + SelectedUserChanged?.Invoke(this, new SelectedUserEventArgs(_selectedUser, true)); } #endregion @@ -117,7 +159,7 @@ namespace Acacia.UI if (_gab != value) { _gab = value; - LookupUsers(false); + DataSource = _gab == null ? null : new GABDataSource(_gab); } } } @@ -126,147 +168,20 @@ namespace Acacia.UI #endregion - protected override void OnTextChanged(EventArgs e) - { - LookupUsers(true); - SelectCurrentUser(false); - } - - private void SelectCurrentUser(bool isChosen) - { - /*GABUser user = null; - // Select whatever is currently in the text box as a user - if (DataSource != null) - { - // Find if there's a user matching - user = ((List)DataSource).FirstOrDefault((u) => u.DisplayName == Text); - } - if (user == null && Text.Length > 0) - { - // Make a new one - user = new GABUser(Text, Text); - } - SetSelectedUser(user, isChosen);*/ - } - - /*private bool _needUpdate; - - protected override void OnTextUpdate(EventArgs e) - { - _needUpdate = true; - } - - protected override void OnSelectedIndexChanged(EventArgs e) - { - base.OnSelectedIndexChanged(e); - SetSelectedUser((GABUser)SelectedItem, true); - } - - protected override void OnKeyDown(KeyEventArgs e) - { - base.OnKeyDown(e); - if (e.KeyCode == Keys.Enter) - { - SelectCurrentUser(true); - } - else - { - SetSelectedUser(null, false); - } - } - - protected override void OnDataSourceChanged(EventArgs e) - { - // Suppress to prevent automatic selection - }*/ - - private string _lastText; - private List _allUsers; - - private void LookupUsers(bool dropDown) - { - // Cannot lookup if there is no GAB - if (_gab == null) - return; - - string text = this.Text; - // Only search if the text actually changed - if (_lastText != text) - { - // Limit search results if there is a filter, otherwise show everything - List users; - if (text.Length == 0) - { - // Cache the list of all users - if (_allUsers == null) - { - _allUsers = Lookup("", int.MaxValue); - } - users = _allUsers; - } - else - { - users = Lookup(text, 8); - } - - // Sort the users if we have them - users.Sort(); - - _lastText = text; - - // Setting the datasource will trigger a select if there is a match - BeginUpdate(); - //DataSource = users; - //SetItemsCore(users); - if (dropDown && text.Length != 0) - DroppedDown = true; - //Cursor.Current = Cursors.Default; - //Text = _lastText; - //SelectionLength = 0; - //SelectionStart = _lastText.Length; - EndUpdate(); - } - } - - #region Lookup helpers - // TODO: these probably belong in GAB - - public List Lookup(string text, int max) - { - // Begin GAB lookup, search on full name or username - using (ISearch search = _gab.Contacts.Search()) - { - ISearchOperator oper = search.AddOperator(SearchOperator.Or); - oper.AddField("urn:schemas:contacts:cn").SetOperation(SearchOperation.Like, text + "%"); - oper.AddField("urn:schemas:contacts:customerid").SetOperation(SearchOperation.Like, text + "%"); - - // Fetch the results up to the limit. - // TODO: make limit a property? - List users = new List(); - foreach (IContactItem result in search.Search(max)) - { - users.Add(new GABUser(result.FullName, result.CustomerID)); - } - - return users; - } - } - public GABUser LookupExact(string username) { - if (_gab?.Contacts != null) + string s = username.ToLower(); + if (DataSource != null) { - // Begin GAB lookup, search on full name or username - using (ISearch search = _gab.Contacts.Search()) + foreach(GABUser user in DataSource.Items) { - search.AddField("urn:schemas:contacts:customerid").SetOperation(SearchOperation.Equal, username); - - // Fetch the result, if any. - List users = new List(); - using (IContactItem result = search.SearchOne()) + if ( + user.FullName?.ToLower().Equals(s) == true || + user.UserName?.ToLower().Equals(s) == true || + user.EmailAddress?.ToLower().Equals(s) == true + ) { - if (result != null) - return new GABUser(result.FullName, result.CustomerID); + return user; } } } @@ -274,6 +189,65 @@ namespace Acacia.UI return new GABUser(username); } + #region Rendering + + private static readonly Size NameSpacing = new Size(12, 4); + private static readonly Padding ItemPadding = new Padding(5); + private static readonly Padding BorderPadding = new Padding(2); + private const int BorderThickness = 1; + + protected override void OnMeasureItem(MeasureItemEventArgs e) + { + GABUser item = (GABUser)e.Item; + + Size nameSize = TextRenderer.MeasureText(e.Graphics, item.FullName, Font); + Size loginSize = TextRenderer.MeasureText(e.Graphics, item.UserName, Font); + Size emailSize = TextRenderer.MeasureText(e.Graphics, item.EmailAddress, Font); + + e.ItemWidth = Math.Max(emailSize.Width, nameSize.Width + loginSize.Width + NameSpacing.Width) + + ItemPadding.Horizontal; + e.ItemHeight = emailSize.Height + Math.Max(nameSize.Height, loginSize.Height) + + ItemPadding.Vertical + + NameSpacing.Height + + BorderThickness + BorderPadding.Vertical; + } + + protected override void OnDrawItem(DrawItemEventArgs e) + { + GABUser item = (GABUser)e.Item; + + // Draw the background + e.DrawBackground(); + + // Get the sizes + Size nameSize = TextRenderer.MeasureText(e.Graphics, item.FullName, Font); + Size loginSize = TextRenderer.MeasureText(e.Graphics, item.UserName, Font); + Size emailSize = TextRenderer.MeasureText(e.Graphics, item.EmailAddress, Font); + + // Draw the full name top-left + Point pt = e.Bounds.TopLeft(); + pt.Y += ItemPadding.Top; + pt.X += ItemPadding.Left; + TextRenderer.DrawText(e.Graphics, item.FullName, Font, pt, e.ForeColor); + + // Draw the username top-right + pt.X = e.Bounds.Right - loginSize.Width - ItemPadding.Right; + TextRenderer.DrawText(e.Graphics, item.UserName, Font, pt, e.ForeColor); + + // Draw the email below + pt.Y += Math.Max(nameSize.Height, loginSize.Height) + NameSpacing.Height; + pt.X = e.Bounds.X + ItemPadding.Left; + TextRenderer.DrawText(e.Graphics, item.EmailAddress, Font, pt, e.ForeColor); + + // Draw a separator line + if (e.Index < DisplayItemCount - 1) + { + int lineY = e.Bounds.Bottom - 1 - BorderThickness - BorderPadding.Bottom; + e.Graphics.DrawLine(Pens.LightGray, BorderPadding.Left, lineY, e.Bounds.Width - BorderPadding.Right, lineY); + } + + } + #endregion } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/GABUser.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/GABUser.cs index f269e45..fd3ff98 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/GABUser.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/GABUser.cs @@ -1,4 +1,6 @@ -/// Copyright 2016 Kopano b.v. + +using Acacia.Stubs; +/// Copyright 2016 Kopano b.v. /// /// This program is free software: you can redistribute it and/or modify /// it under the terms of the GNU Affero General Public License, version 3, @@ -13,7 +15,6 @@ /// along with this program.If not, see. /// /// Consult LICENSE file for details - using System; using System.Collections.Generic; using System.Linq; @@ -36,8 +37,9 @@ namespace Acacia.ZPush public readonly string FullName; public readonly string UserName; + public readonly string EmailAddress; - public GABUser(string displayName, string userName) + private GABUser(string displayName, string userName) { this.FullName = displayName; this.UserName = userName; @@ -49,6 +51,13 @@ namespace Acacia.ZPush this.UserName = userName; } + public GABUser(IContactItem item) + { + this.FullName = item.FullName; + this.EmailAddress = item.Email1Address; + this.UserName = item.CustomerID; + } + public int CompareTo(GABUser other) { return FullName.CompareTo(other.FullName); From 1b509495c8b1fba3d7f5526805d93a5db64f03d7 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 18:46:37 +0200 Subject: [PATCH 13/14] Implemented selection of GAB users. Added fallback item if no users are found. --- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 10 +++ .../AcaciaZPushPlugin/Controls/KDataSource.cs | 6 ++ .../SharedFoldersDialog.Designer.cs | 13 +--- .../SharedFolders/SharedFoldersDialog.cs | 6 +- .../SharedFolders/SharedFoldersDialog.resx | 68 +++++-------------- .../AcaciaZPushPlugin/UI/GABLookupControl.cs | 37 +++++++++- 6 files changed, 73 insertions(+), 67 deletions(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 00aa0eb..8908746 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -268,6 +268,16 @@ namespace Acacia.Controls _list.Items.Add(displayItem); } + if (_list.Items.Count == 0) + { + // Create a not-found item if requested + object item = _dataSource.NotFoundItem; + if (item != null) + { + _list.Items.Add(new DisplayItem(this, item)); + } + } + // Select the current item only if new number of items is smaller. This means we don't keep selection // when the user is removing text, only when they are typing more. _list.ItemsChanged(_list.Items.Count < oldCount ? selected : -1); diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs index 779738d..6c416c5 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KDataSource.cs @@ -23,6 +23,7 @@ namespace Acacia.Controls System.Collections.IEnumerable FilteredItems { get; } KDataFilter Filter { get; set; } string GetItemText(object item); + object NotFoundItem { get; } } abstract public class KDataSource : KDataSourceRaw @@ -73,6 +74,11 @@ namespace Acacia.Controls set; } + virtual public object NotFoundItem + { + get { return null; } + } + IEnumerable KDataSourceRaw.Items { get{return Items;}} IEnumerable KDataSourceRaw.FilteredItems { get { return FilteredItems; } } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs index ae3b9b3..8834769 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs @@ -35,7 +35,6 @@ this._layoutMain = new System.Windows.Forms.TableLayoutPanel(); this._layoutSelectUser = new System.Windows.Forms.TableLayoutPanel(); this.labelSelectUser = new System.Windows.Forms.Label(); - this._layoutCenterGABLookup = new System.Windows.Forms.TableLayoutPanel(); this.gabLookup = new Acacia.UI.GABLookupControl(); this.buttonOpenUser = new System.Windows.Forms.Button(); this.kTreeFolders = new Acacia.Controls.KTree(); @@ -53,7 +52,6 @@ this._mainBusyHider.SuspendLayout(); this._layoutMain.SuspendLayout(); this._layoutSelectUser.SuspendLayout(); - this._layoutCenterGABLookup.SuspendLayout(); this._layoutOptions.SuspendLayout(); this.SuspendLayout(); // @@ -85,7 +83,7 @@ // resources.ApplyResources(this._layoutSelectUser, "_layoutSelectUser"); this._layoutSelectUser.Controls.Add(this.labelSelectUser, 0, 0); - this._layoutSelectUser.Controls.Add(this._layoutCenterGABLookup, 1, 0); + this._layoutSelectUser.Controls.Add(this.gabLookup, 1, 0); this._layoutSelectUser.Controls.Add(this.buttonOpenUser, 2, 0); this._layoutSelectUser.Name = "_layoutSelectUser"; // @@ -94,12 +92,6 @@ resources.ApplyResources(this.labelSelectUser, "labelSelectUser"); this.labelSelectUser.Name = "labelSelectUser"; // - // _layoutCenterGABLookup - // - resources.ApplyResources(this._layoutCenterGABLookup, "_layoutCenterGABLookup"); - this._layoutCenterGABLookup.Controls.Add(this.gabLookup, 0, 1); - this._layoutCenterGABLookup.Name = "_layoutCenterGABLookup"; - // // gabLookup // this.gabLookup.DataSource = null; @@ -221,8 +213,6 @@ this._layoutMain.PerformLayout(); this._layoutSelectUser.ResumeLayout(false); this._layoutSelectUser.PerformLayout(); - this._layoutCenterGABLookup.ResumeLayout(false); - this._layoutCenterGABLookup.PerformLayout(); this._layoutOptions.ResumeLayout(false); this._layoutOptions.PerformLayout(); this.ResumeLayout(false); @@ -246,7 +236,6 @@ private System.Windows.Forms.Label labelPermissionsValue; private Controls.KBusyHider _mainBusyHider; private Controls.KDialogButtons dialogButtons; - private System.Windows.Forms.TableLayoutPanel _layoutCenterGABLookup; private UI.GABLookupControl gabLookup; private System.Windows.Forms.Label _labelReminders; private System.Windows.Forms.CheckBox checkReminders; diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs index 091fdff..bbf8edf 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs @@ -148,8 +148,12 @@ namespace Acacia.Features.SharedFolders }; FocusNode(node); } + kTreeFolders.Focus(); + } + else + { + gabLookup.FocusEdit(); } - kTreeFolders.Focus(); } private void dialogButtons_Apply(object sender, EventArgs e) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx index 2d9e44e..0e2826d 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx @@ -148,7 +148,7 @@ 3, 0 - 105, 33 + 105, 29 0 @@ -171,26 +171,23 @@ 0 - - True - - - 1 - Fill - 3, 3 + 114, 3 200, 0 + + The user was not found + - + Start typing name - 256, 23 + 260, 23 0 @@ -202,44 +199,11 @@ Acacia.UI.GABLookupControl, Kopano, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null - _layoutCenterGABLookup - - - 0 - - - Fill - - - 113, 2 - - - 2, 2, 2, 2 - - - 3 - - - 262, 29 - - - 1 - - - _layoutCenterGABLookup - - - System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - _layoutSelectUser - + 1 - - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="gabLookup" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100" /><Rows Styles="Percent,50,AutoSize,0,Percent,50" /></TableLayoutSettings> - True @@ -262,7 +226,7 @@ 8, 0, 8, 0 - 59, 27 + 59, 23 2 @@ -292,7 +256,7 @@ 1 - 442, 33 + 442, 29 0 @@ -310,16 +274,16 @@ 0 - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="labelSelectUser" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="_layoutCenterGABLookup" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="buttonOpenUser" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100,AutoSize,0" /><Rows Styles="Percent,100,Absolute,33" /></TableLayoutSettings> + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="labelSelectUser" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="gabLookup" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="buttonOpenUser" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100,AutoSize,0" /><Rows Styles="Percent,100,Absolute,29" /></TableLayoutSettings> Fill - 3, 42 + 3, 38 - 442, 273 + 442, 277 0 @@ -658,7 +622,7 @@ 448, 418 - 3 + 0 _layoutMain @@ -685,7 +649,7 @@ 448, 418 - 4 + 0 @@ -721,7 +685,7 @@ 450, 35 - 0 + 1 dialogButtons diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs index 7781420..2eb4053 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs @@ -33,6 +33,13 @@ namespace Acacia.UI { public partial class GABLookupControl : KComboBoxCustomDraw { + private class NotFoundGABUser : GABUser + { + public NotFoundGABUser(string userName) : base(userName) + { + } + } + private class GABDataSource : KDataSource { private readonly GABHandler _gab; @@ -82,6 +89,14 @@ namespace Acacia.UI item.UserName?.ToLower().StartsWith(s) == true || item.EmailAddress?.ToLower().StartsWith(s) == true; } + + public override object NotFoundItem + { + get + { + return new NotFoundGABUser(Filter.FilterText); + } + } } public GABLookupControl() : this(null) @@ -96,6 +111,15 @@ namespace Acacia.UI #region Properties and events + + [Category("Appearance")] + [Localizable(true)] + public string NotFoundText + { + get; + set; + } + #region SelectedUser public class SelectedUserEventArgs : EventArgs @@ -202,7 +226,7 @@ namespace Acacia.UI Size nameSize = TextRenderer.MeasureText(e.Graphics, item.FullName, Font); Size loginSize = TextRenderer.MeasureText(e.Graphics, item.UserName, Font); - Size emailSize = TextRenderer.MeasureText(e.Graphics, item.EmailAddress, Font); + Size emailSize = TextRenderer.MeasureText(e.Graphics, GetSecondLine(item), Font); e.ItemWidth = Math.Max(emailSize.Width, nameSize.Width + loginSize.Width + NameSpacing.Width) + ItemPadding.Horizontal; @@ -212,6 +236,14 @@ namespace Acacia.UI BorderThickness + BorderPadding.Vertical; } + private string GetSecondLine(GABUser item) + { + if (item is NotFoundGABUser) + return NotFoundText; + else + return item.EmailAddress; + } + protected override void OnDrawItem(DrawItemEventArgs e) { GABUser item = (GABUser)e.Item; @@ -237,7 +269,8 @@ namespace Acacia.UI // Draw the email below pt.Y += Math.Max(nameSize.Height, loginSize.Height) + NameSpacing.Height; pt.X = e.Bounds.X + ItemPadding.Left; - TextRenderer.DrawText(e.Graphics, item.EmailAddress, Font, pt, e.ForeColor); + + TextRenderer.DrawText(e.Graphics, GetSecondLine(item), Font, pt, e.ForeColor); // Draw a separator line if (e.Index < DisplayItemCount - 1) From 334aeabc9dae1709b618bbc768cfdc402c02293d Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Thu, 29 Jun 2017 18:57:13 +0200 Subject: [PATCH 14/14] [KOE-125] Fixed mouse wheel handling for combo box --- .../AcaciaZPushPlugin/Controls/KComboBox.cs | 12 ++++++++++++ src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs index 8908746..3d613d5 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Controls/KComboBox.cs @@ -381,5 +381,17 @@ namespace Acacia.Controls break; } } + + protected override void DefWndProc(ref Message m) + { + switch ((WM)m.Msg) + { + // Forward mouse wheel messages to the list + case WM.MOUSEWHEEL: + m.Result = (IntPtr) User32.SendMessage(_list.Handle, m.Msg, m.WParam, m.LParam); + return; + } + base.DefWndProc(ref m); + } } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs index 9b53dff..f7c1f8b 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/WM.cs @@ -45,7 +45,9 @@ namespace Acacia.Native LBUTTONDOWN = 0x0201, RBUTTONDOWN = 0x0204, - MBUTTONDOWN = 0x0207 + MBUTTONDOWN = 0x0207, + + MOUSEWHEEL = 0x020A, } }