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