Files
wg_cpso/UserControls/AutocompleteMenu/AutocompleteListView.cs
2026-03-25 18:20:24 +08:00

416 lines
14 KiB
C#

using CaeGlobals;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AutocompleteMenuNS
{
[System.ComponentModel.ToolboxItem(false)]
public class AutocompleteListView : UserControl, IAutocompleteListView
{
private readonly ToolTip toolTip = new ToolTip();
public int HighlightedItemIndex { get; set; }
private int oldItemCount;
private int selectedItemIndex = -1;
private IList<AutocompleteItem> visibleItems;
private MouseHook mouseHook;
private bool inside;
/// <summary>
/// Duration (ms) of tooltip showing
/// </summary>
public int ToolTipDuration { get; set; }
/// <summary>
/// Occurs when user selected item for inserting into text
/// </summary>
public event EventHandler ItemSelected;
/// <summary>
/// Occurs when current hovered item is changing
/// </summary>
public event EventHandler<HoveredEventArgs> ItemHovered;
/// <summary>
/// Occurs when mouse click is outside the controls area
/// </summary>
public event EventHandler OutsideMouseClick;
/// <summary>
/// Colors
/// </summary>
public Colors Colors { get; set; }
internal AutocompleteListView()
{
SetStyle(
ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
base.Font = new Font(FontFamily.GenericSansSerif, 9);
ItemHeight = Font.Height + 2;
VerticalScroll.SmallChange = ItemHeight;
BackColor = Color.White;
LeftPadding = 18;
ToolTipDuration = 3000;
Colors = new Colors();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
toolTip.Dispose();
}
base.Dispose(disposing);
}
private int itemHeight;
public int ItemHeight
{
get { return itemHeight; }
set {
itemHeight = value;
VerticalScroll.SmallChange = value;
oldItemCount = -1;
AdjustScroll();
}
}
public override Font Font
{
get { return base.Font; }
set
{
base.Font = value;
ItemHeight = Font.Height + 2;
}
}
public int LeftPadding { get; set; }
public ImageList ImageList { get; set; }
public IList<AutocompleteItem> VisibleItems
{
get { return visibleItems; }
set
{
visibleItems = value;
SelectedItemIndex = -1;
AdjustScroll();
Invalidate();
}
}
public int SelectedItemIndex
{
get { return selectedItemIndex; }
set
{
AutocompleteItem item = null;
if (value >= 0 && value < VisibleItems.Count)
item = VisibleItems[value];
selectedItemIndex = value;
OnItemHovered(new HoveredEventArgs() { Item = item });
if (item != null)
{
ShowToolTip(item, this);
ScrollToSelected();
}
Invalidate();
}
}
private void OnItemHovered(HoveredEventArgs e)
{
if (ItemHovered != null)
ItemHovered(this, e);
}
private void AdjustScroll()
{
if (VisibleItems == null)
return;
if (oldItemCount == VisibleItems.Count)
return;
// Height
int needHeight = ItemHeight*VisibleItems.Count + 1;
Height = Math.Min(needHeight, MaximumSize.Height);
AutoScrollMinSize = new Size(0, needHeight);
oldItemCount = VisibleItems.Count;
// Width
int maxWidth = 0;
using (Graphics g = this.CreateGraphics())
{
foreach (var item in VisibleItems)
{
string text = item.ToString();
SizeF size = g.MeasureString(text, Font);
maxWidth = Math.Max(maxWidth, (int)Math.Ceiling(size.Width));
}
}
int padding = 35;
Width = Math.Min(MaximumSize.Width, maxWidth + padding);
}
private void ScrollToSelected()
{
int y = SelectedItemIndex*ItemHeight - VerticalScroll.Value;
if (y < 0)
VerticalScroll.Value = SelectedItemIndex*ItemHeight;
if (y > ClientSize.Height - ItemHeight)
VerticalScroll.Value = Math.Min(VerticalScroll.Maximum,
SelectedItemIndex*ItemHeight - ClientSize.Height + ItemHeight);
//some magic for update scrolls
AutoScrollMinSize -= new Size(1, 0);
AutoScrollMinSize += new Size(1, 0);
}
public Rectangle GetItemRectangle(int itemIndex)
{
var y = itemIndex * ItemHeight - VerticalScroll.Value;
return new Rectangle(0, y, ClientSize.Width - 1, ItemHeight - 1);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.Clear(Colors.BackColor);
}
protected override void OnPaint(PaintEventArgs e)
{
bool rtl = RightToLeft == RightToLeft.Yes;
AdjustScroll();
int startI = VerticalScroll.Value/ItemHeight - 1;
int finishI = (VerticalScroll.Value + ClientSize.Height)/ItemHeight + 1;
startI = Math.Max(startI, 0);
finishI = Math.Min(finishI, VisibleItems.Count);
int y = 0;
for (int i = startI; i < finishI; i++)
{
y = i*ItemHeight - VerticalScroll.Value;
if (ImageList != null && VisibleItems[i].ImageIndex >= 0)
if (rtl)
e.Graphics.DrawImage(ImageList.Images[VisibleItems[i].ImageIndex], Width - 1 - LeftPadding, y);
else
e.Graphics.DrawImage(ImageList.Images[VisibleItems[i].ImageIndex], 1, y);
var textRect = new Rectangle(LeftPadding, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
if (rtl)
textRect = new Rectangle(1, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
if (i == SelectedItemIndex)
{
Brush selectedBrush = new LinearGradientBrush(new Point(0, y - 3), new Point(0, y + ItemHeight),
Colors.SelectedBackColor2, Colors.SelectedBackColor);
e.Graphics.FillRectangle(selectedBrush, textRect);
using(var pen = new Pen(Colors.SelectedBackColor2))
e.Graphics.DrawRectangle(pen, textRect);
}
if (i == HighlightedItemIndex)
using (var pen = new Pen(Colors.HighlightingColor))
e.Graphics.DrawRectangle(pen, textRect);
var sf = new StringFormat();
if (rtl)
sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;
var args = new PaintItemEventArgs(e.Graphics, e.ClipRectangle)
{
Font = Font,
TextRect = new RectangleF(textRect.Location, textRect.Size),
StringFormat = sf,
IsSelected = i == SelectedItemIndex,
IsHovered = i == HighlightedItemIndex,
Colors = Colors
};
//call drawing
VisibleItems[i].OnPaint(args);
}
}
protected override void OnScroll(ScrollEventArgs se)
{
base.OnScroll(se);
Invalidate(true);
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
if (e.Button == MouseButtons.Left)
{
SelectedItemIndex = PointToItemIndex(e.Location);
ScrollToSelected();
Invalidate();
}
}
private Point mouseEnterPoint;
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
//
inside = true;
mouseEnterPoint = Control.MousePosition;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
//
inside = false;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (mouseEnterPoint != Control.MousePosition)
{
HighlightedItemIndex = PointToItemIndex(e.Location);
Invalidate();
}
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
SelectedItemIndex = PointToItemIndex(e.Location);
Invalidate();
OnItemSelected();
}
private void OnItemSelected()
{
if (ItemSelected != null)
ItemSelected(this, EventArgs.Empty);
}
private int PointToItemIndex(Point p)
{
return (p.Y + VerticalScroll.Value)/ItemHeight;
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
var host = Parent as AutocompleteMenuHost;
if (host != null)
if (host.Menu.ProcessKey((char) keyData, Keys.None))
return true;
return base.ProcessCmdKey(ref msg, keyData);
}
public void SelectItem(int itemIndex)
{
SelectedItemIndex = itemIndex;
ScrollToSelected();
Invalidate();
}
public void SetItems(List<AutocompleteItem> items)
{
VisibleItems = items;
SelectedItemIndex = -1;
AdjustScroll();
Invalidate();
}
public void ShowToolTip(AutocompleteItem autocompleteItem, Control control = null)
{
string title = autocompleteItem.ToolTipTitle;
string text = autocompleteItem.ToolTipText;
if (control == null)
control = this;
if (string.IsNullOrEmpty(title))
{
toolTip.ToolTipTitle = null;
toolTip.SetToolTip(control, null);
return;
}
if (string.IsNullOrEmpty(text))
{
toolTip.ToolTipTitle = null;
toolTip.Show(title, control, Width + 3, 0, ToolTipDuration);
}
else
{
toolTip.ToolTipTitle = title;
toolTip.Show(text, control, Width + 3, 0, ToolTipDuration);
}
}
// Mouse hook
public void InstallMouseHook()
{
mouseHook = new MouseHook();
mouseHook.LeftButtonDown += MouseHook_LeftButtonDown;
mouseHook.DoubleClick += MouseHook_DoubleClick;
mouseHook.RightButtonDown += MouseHook_RightButtonDown;
mouseHook.MiddleButtonDown += MouseHook_MiddleButtonDown;
mouseHook.Install();
}
public void UninstallMouseHook()
{
if (mouseHook != null)
{
mouseHook.LeftButtonDown -= MouseHook_LeftButtonDown;
mouseHook.DoubleClick -= MouseHook_DoubleClick;
mouseHook.RightButtonDown -= MouseHook_RightButtonDown;
mouseHook.MiddleButtonDown -= MouseHook_MiddleButtonDown;
mouseHook.Uninstall();
mouseHook = null;
}
}
private bool MouseHook_LeftButtonDown(MouseHook.MSLLHOOKSTRUCT mouseStruct)
{
if (inside)
{
Point p = new Point(mouseStruct.pt.x, mouseStruct.pt.y);
p = PointToClient(p);
MouseEventArgs mea = new MouseEventArgs(MouseButtons.Left, 1, p.X, p.Y, 0);
OnMouseClick(mea);
}
else if (OutsideMouseClick != null) OutsideMouseClick(null, null);
//
return false;
}
private bool MouseHook_DoubleClick(MouseHook.MSLLHOOKSTRUCT mouseStruct)
{
if (inside)
{
Point p = new Point(mouseStruct.pt.x, mouseStruct.pt.y);
p = PointToClient(p);
MouseEventArgs mea = new MouseEventArgs(MouseButtons.Left, 2, p.X, p.Y, 0);
OnMouseDoubleClick(mea);
}
else if (OutsideMouseClick != null) OutsideMouseClick(null, null);
//
return false;
}
private bool MouseHook_RightButtonDown(MouseHook.MSLLHOOKSTRUCT mouseStruct)
{
return MouseHook_LeftButtonDown(mouseStruct);
}
private bool MouseHook_MiddleButtonDown(MouseHook.MSLLHOOKSTRUCT mouseStruct)
{
return MouseHook_LeftButtonDown(mouseStruct);
}
}
}