// COPYRIGHT (C) Tom. ALL RIGHTS RESERVED.
// THE AntdUI PROJECT IS AN WINFORM LIBRARY LICENSED UNDER THE Apache-2.0 License.
// LICENSED UNDER THE Apache License, VERSION 2.0 (THE "License")
// YOU MAY NOT USE THIS FILE EXCEPT IN COMPLIANCE WITH THE License.
// YOU MAY OBTAIN A COPY OF THE LICENSE AT
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE
// DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
// SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND
// LIMITATIONS UNDER THE License.
// GITEE: https://gitee.com/antdui/AntdUI
// GITHUB: https://github.com/AntdUI/AntdUI
// CSDN: https://blog.csdn.net/v_132
// QQ: 17379620
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace AntdUI.Chat
{
///
/// MsgList 好友消息列表
///
/// 好友消息列表。
[Description("MsgList 好友消息列表")]
[ToolboxItem(true)]
public class MsgList : IControl
{
#region 属性
MsgItemCollection? items;
///
/// 数据集合
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Description("数据集合"), Category("数据")]
public MsgItemCollection Items
{
get
{
items ??= new MsgItemCollection(this);
return items;
}
set => items = value.BindData(this);
}
///
/// 滚动条
///
[Browsable(false)]
public ScrollBar ScrollBar;
#endregion
#region 渲染
protected override void OnPaint(PaintEventArgs e)
{
if (items == null || items.Count == 0) return;
var rect = ClientRectangle;
if (rect.Width == 0 || rect.Height == 0) return;
var g = e.Graphics.High();
int sy = ScrollBar.Value;
g.TranslateTransform(0, -sy);
using (var font_text = new Font(Font.FontFamily, Font.Size * 0.9F))
using (var font_time = new Font(Font.FontFamily, Font.Size * 0.82F))
{
foreach (MsgItem it in items) PaintItem(g, it, rect, sy, font_text, font_time);
}
g.ResetTransform();
ScrollBar.Paint(g);
base.OnPaint(e);
}
StringFormat SFBage = Helper.SF();
StringFormat SFL = Helper.SF_ALL(lr: StringAlignment.Near);
StringFormat SFR = Helper.SF_ALL(lr: StringAlignment.Far);
void PaintItem(Graphics g, MsgItem it, Rectangle rect, float sy, Font font_text, Font font_time)
{
it.show = it.Show && it.Visible && it.rect.Y > sy - rect.Height && it.rect.Bottom < ScrollBar.Value + ScrollBar.ReadSize + it.rect.Height;
if (it.show)
{
if (it.Select)
{
using (var brush = new SolidBrush(Color.FromArgb(0, 153, 255)))
{
g.FillRectangle(brush, it.rect);
}
using (var brush = new SolidBrush(Color.White))
{
try
{
g.DrawStr(it.Name, Font, brush, it.rect_name, SFL);
g.DrawStr(it.Text, font_text, brush, it.rect_text, SFL);
g.DrawStr(it.Time, font_time, brush, it.rect_time, SFR);
}
catch { }
}
}
else
{
if (it.Hover)
{
using (var brush = new SolidBrush(Style.Db.FillTertiary))
{
g.FillRectangle(brush, it.rect);
}
}
using (var brush = new SolidBrush(ForeColor))
{
try
{
g.DrawStr(it.Name, Font, brush, it.rect_name, SFL);
g.DrawStr(it.Text, font_text, brush, it.rect_text, SFL);
g.DrawStr(it.Time, font_time, brush, it.rect_time, SFR);
}
catch { }
}
}
if (it.Icon != null)
{
g.PaintImg(it.rect_icon, it.Icon, TFit.Cover, 0, true);
if (it.Count > 0)
{
if (it.Count > 99)
{
var badgesize = g.MeasureString("99+", font_time).Size();
var rect_badge = new Rectangle(it.rect_icon.Right - badgesize.Width + badgesize.Width / 3, it.rect_icon.Y - badgesize.Height / 3, badgesize.Width, badgesize.Height);
using (var path = rect_badge.RoundPath(6 * Config.Dpi))
{
g.FillPath(Brushes.Red, path);
}
g.DrawStr("99+", font_time, Brushes.White, rect_badge, SFBage);
}
else if (it.Count > 1)
{
var badgesize = g.MeasureString(it.Count.ToString(), font_time).Size();
int badge_size = badgesize.Width > badgesize.Height ? badgesize.Width : badgesize.Height, xy = badge_size / 3;
var rect_badge = new Rectangle(it.rect_icon.Right - badge_size + xy, it.rect_icon.Y - xy, badge_size, badge_size);
g.FillEllipse(Brushes.Red, rect_badge);
g.DrawStr(it.Count.ToString(), font_time, Brushes.White, rect_badge, SFBage);
}
else
{
int badge_size = it.rect_time.Height / 2, xy = badge_size / 3;
var rect_badge = new Rectangle(it.rect_icon.Right - badge_size + xy, it.rect_icon.Y - xy, badge_size, badge_size);
g.FillEllipse(Brushes.Red, rect_badge);
}
}
}
}
}
public MsgList() { ScrollBar = new ScrollBar(this); }
protected override void Dispose(bool disposing)
{
ScrollBar.Dispose();
base.Dispose(disposing);
}
#endregion
#region 鼠标
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (ScrollBar.MouseDown(e.Location))
{
if (items == null || items.Count == 0) return;
foreach (MsgItem it in Items)
{
if (it.Visible && it.Contains(e.Location, 0, ScrollBar.Value, out _))
{
it.Select = true;
// 触发ItemSelected事件
OnItemSelected(it);
return;
}
}
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
ScrollBar.MouseUp();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (ScrollBar.MouseMove(e.Location))
{
if (items == null || items.Count == 0) return;
int count = 0, hand = 0;
foreach (MsgItem it in Items)
{
if (it.show)
{
if (it.Contains(e.Location, 0, ScrollBar.Value, out var change))
{
hand++;
}
if (change) count++;
}
}
SetCursor(hand > 0);
if (count > 0) Invalidate();
}
else ILeave();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
ScrollBar.Leave();
ILeave();
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
ScrollBar.Leave();
ILeave();
}
void ILeave()
{
SetCursor(false);
if (items == null || items.Count == 0) return;
int count = 0;
foreach (MsgItem it in Items)
{
if (it.Hover) count++;
it.Hover = false;
}
if (count > 0) Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
ScrollBar.MouseWheel(e.Delta);
base.OnMouseWheel(e);
}
#endregion
#region 事件
public event ItemSelectedEventHandler? ItemSelected;
protected virtual void OnItemSelected(MsgItem selectedItem)
{
ItemSelected?.Invoke(this, new MsgItemEventArgs(selectedItem));
}
#endregion
#region 布局
protected override void OnFontChanged(EventArgs e)
{
var rect = ChangeList();
ScrollBar.SizeChange(rect);
base.OnFontChanged(e);
}
protected override void OnSizeChanged(EventArgs e)
{
var rect = ChangeList();
ScrollBar.SizeChange(rect);
base.OnSizeChanged(e);
}
internal Rectangle ChangeList()
{
var rect = ClientRectangle;
if (items == null || items.Count == 0) return rect;
if (rect.Width == 0 || rect.Height == 0) return rect;
int y = 0;
Helper.GDI(g =>
{
var size = (int)Math.Ceiling(g.MeasureString(Config.NullText, Font).Height);
int item_height = (int)Math.Ceiling(size * 3.856),
gap = (int)Math.Round(item_height * 0.212),
spilt = (int)Math.Round(gap * 0.478),
gap_name = (int)Math.Round(gap * 0.304),
gap_desc = (int)Math.Round(gap * 0.217),
name_height = (int)Math.Round(item_height * 0.185),
desc_height = (int)Math.Round(item_height * 0.157),
image_size = item_height - gap * 2;
using (var font_time = new Font(Font.FontFamily, Font.Size * 0.82F))
{
foreach (MsgItem it in items)
{
it.PARENT = this;
int time_width = 0;
if (!string.IsNullOrEmpty(it.Time)) time_width = (int)Math.Ceiling(g.MeasureString(it.Time, font_time).Width);
it.SetRect(new Rectangle(rect.X, rect.Y + y, rect.Width, item_height), time_width, gap, spilt, gap_name, gap_desc, image_size, name_height, desc_height);
if (it.Visible) y += item_height;
}
}
});
ScrollBar.SetVrSize(y);
return rect;
}
#endregion
}
public class MsgItemCollection : iCollection
{
public MsgItemCollection(MsgList it)
{
BindData(it);
}
internal MsgItemCollection BindData(MsgList it)
{
action = render =>
{
if (render) it.ChangeList();
it.Invalidate();
};
return this;
}
}
public class MsgItem : NotifyProperty
{
public MsgItem() { }
public MsgItem(string name)
{
_name = name;
}
public MsgItem(string name, Bitmap? icon)
{
_name = name;
_icon = icon;
}
Image? _icon = null;
///
/// 图标
///
[Description("图标"), Category("外观"), DefaultValue(null)]
public Image? Icon
{
get => _icon;
set
{
if (_icon == value) return;
_icon = value;
OnPropertyChanged("Icon");
}
}
string _name;
///
/// 名称
///
[Description("名称"), Category("外观")]
public string Name
{
get => _name;
set
{
if (_name == value) return;
_name = value;
OnPropertyChanged("Name");
}
}
string? _text = null;
///
/// 文本
///
[Description("文本"), Category("外观"), DefaultValue(null)]
public string? Text
{
get => _text;
set
{
if (_text == value) return;
_text = value;
OnPropertyChanged("Text");
}
}
int count = 0;
///
/// 消息数量
///
[Description("消息数量"), Category("外观"), DefaultValue(0)]
public int Count
{
get => count;
set
{
if (count == value) return;
count = value;
OnPropertyChanged("Count");
}
}
string? time = null;
///
/// 时间
///
[Description("时间"), Category("外观"), DefaultValue(null)]
public string? Time
{
get => time;
set
{
if (time == value) return;
time = value;
Invalidates();
}
}
bool visible = true;
///
/// 是否显示
///
[Description("是否显示"), Category("外观"), DefaultValue(true)]
public bool Visible
{
get => visible;
set
{
if (visible == value) return;
visible = value;
Invalidates();
}
}
///
/// 用户定义数据
///
[Description("用户定义数据"), Category("数据"), DefaultValue(null)]
public object? Tag { get; set; }
void Invalidate()
{
PARENT?.Invalidate();
}
void Invalidates()
{
if (PARENT == null) return;
PARENT.ChangeList();
PARENT.Invalidate();
}
internal bool show { get; set; }
internal bool Show { get; set; }
///
/// 是否移动
///
internal bool Hover = false;
internal bool select = false;
[Description("是否选中"), Category("外观"), DefaultValue(false)]
public bool Select
{
get => select;
set
{
if (select == value) return;
select = value;
if (value && PARENT != null)
{
foreach (MsgItem it in PARENT.Items)
{
if (it != this) it.select = false;
}
}
Invalidate();
}
}
internal MsgList? PARENT { get; set; }
internal void SetRect(Rectangle _rect, int time_width, int gap, int spilt, int gap_name, int gap_desc, int image_size, int name_height, int desc_height)
{
rect = _rect;
int text_width = _rect.Width - image_size - gap - spilt * 2;
rect_icon = new Rectangle(_rect.X + gap, _rect.Y + gap, image_size, image_size);
rect_name = new Rectangle(rect_icon.Right + spilt, rect_icon.Y + gap_name - gap_desc, text_width - time_width, name_height + gap_desc * 2);
rect_time = new Rectangle(rect_name.Right, rect_name.Y, time_width, rect_name.Height);
rect_text = new Rectangle(rect_name.X, rect_icon.Bottom - gap_desc - desc_height - gap_desc, text_width, desc_height + gap_desc * 2);
Show = true;
}
internal Rectangle rect { get; set; }
internal bool Contains(Point point, int x, int y, out bool change)
{
if (rect.Contains(new Point(point.X + x, point.Y + y)))
{
change = SetHover(true);
return true;
}
else
{
change = SetHover(false);
return false;
}
}
internal bool SetHover(bool val)
{
bool change = false;
if (val)
{
if (!Hover) change = true;
Hover = true;
}
else
{
if (Hover) change = true;
Hover = false;
}
return change;
}
internal Rectangle rect_name { get; set; }
internal Rectangle rect_time { get; set; }
internal Rectangle rect_text { get; set; }
internal Rectangle rect_icon { get; set; }
}
}