123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- using FastReport.Utils;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Drawing;
- using System.Windows.Forms;
- namespace FastReport.Controls
- {
- internal class BreadCrumbControl : UserControl
- {
- private Image image;
- private bool pathEditMode;
- private TextBox pathEditBox;
- private List<BreadCrumbItemBase> visibleItems;
- private List<BreadCrumbItem> overflowItems;
- private int XOffset => this.LogicalToDevice(3);
- public List<BreadCrumbItem> Items { get; }
- internal List<BreadCrumbItem> OverflowItems => overflowItems;
- public string PathSeparator { get; set; }
- [Browsable(false)]
- public string Path => pathEditMode ? pathEditBox.Text : GetPath();
- public bool AllowPathEdit { get; set; }
- [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public Image Image
- {
- get => image;
- set
- {
- image = value;
- UpdateLayout();
- }
- }
- [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public ToolStripRenderer OverflowMenuRenderer { get; set; }
- public event EventHandler ItemClick;
- public event EventHandler PathEdited;
- private string GetPath()
- {
- var result = "";
- for (int i = 0; i < Items.Count; i++)
- {
- result += Items[i].Text;
- if (i < Items.Count - 1)
- result += PathSeparator;
- }
- return result;
- }
- private void CreatePathEdit()
- {
- pathEditMode = true;
- if (pathEditBox == null)
- {
- pathEditBox = new TextBox();
- pathEditBox.BorderStyle = BorderStyle.None;
- pathEditBox.Parent = this;
- pathEditBox.PreviewKeyDown += (s, e) =>
- {
- if (e.KeyCode == Keys.Escape)
- e.IsInputKey = true;
- };
- pathEditBox.KeyPress += (s, e) =>
- {
- if (e.KeyChar == (char)13)
- {
- e.Handled = true;
- PathEdited?.Invoke(this, EventArgs.Empty);
- HidePathEdit();
- }
- else if (e.KeyChar == (char)27)
- {
- e.Handled = true;
- HidePathEdit();
- }
- };
- pathEditBox.LostFocus += (s, e) =>
- {
- HidePathEdit();
- };
- }
- pathEditBox.Location = new Point(XOffset, (Height - pathEditBox.PreferredHeight) / 2);
- pathEditBox.Size = new Size(Width - XOffset * 2, pathEditBox.PreferredHeight);
- pathEditBox.Text = GetPath();
- pathEditBox.Visible = true;
- pathEditBox.SelectAll();
- pathEditBox.Focus();
- }
- private void HidePathEdit()
- {
- pathEditBox.Visible = false;
- pathEditMode = false;
- Refresh();
- }
- private int LayoutItem(BreadCrumbItemBase item, int offset)
- {
- item.SetOwner(this);
- visibleItems.Add(item);
- item.Measure();
- return item.Layout(offset);
- }
- private bool LayoutItems(int startIndex)
- {
- visibleItems.Clear();
- bool rtl = RightToLeft == RightToLeft.Yes;
- int x = rtl ? Width - XOffset : XOffset;
- x = LayoutItem(new BreadCrumbImageItem(), x);
- if (startIndex > 0)
- x = LayoutItem(new BreadCrumbOverflowItem(), x);
- for (int i = startIndex; i < Items.Count; i++)
- {
- var item = Items[i];
- x = LayoutItem(item, x);
- if (i < Items.Count - 1)
- x = LayoutItem(new BreadCrumbSeparatorItem(), x);
- }
- return Width < 20 || // startup bug workaround
- (rtl ? x > 20 : x < Width - 20);
- }
- private void DrawItems(Graphics g)
- {
- for (int i = 0; i < visibleItems.Count; i++)
- {
- var item = visibleItems[i];
- item.Paint(g);
- }
- }
- protected override void OnPaint(PaintEventArgs e)
- {
- base.OnPaint(e);
- Graphics g = e.Graphics;
- g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
- if (!pathEditMode)
- DrawItems(g);
- g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
- this.DrawVisualStyleBorder(g, new Rectangle(0, 0, Width - 1, Height - 1));
- }
- protected override void OnMouseMove(MouseEventArgs e)
- {
- base.OnMouseMove(e);
- foreach (var item in visibleItems)
- {
- item.IsMouseOver = item.Bounds.Contains(e.Location);
- }
- Refresh();
- }
- protected override void OnMouseUp(MouseEventArgs e)
- {
- base.OnMouseUp(e);
- if (pathEditMode)
- return;
- foreach (var item in visibleItems)
- {
- if (item.IsMouseOver)
- {
- item.Click();
- return;
- }
- }
- if (AllowPathEdit)
- {
- CreatePathEdit();
- Refresh();
- }
- }
- protected override void OnMouseLeave(EventArgs e)
- {
- base.OnMouseLeave(e);
- foreach (var item in visibleItems)
- {
- item.IsMouseOver = false;
- }
- Refresh();
- }
- internal void OnItemClick(BreadCrumbItem item) => ItemClick?.Invoke(item, EventArgs.Empty);
- public void UpdateLayout()
- {
- overflowItems.Clear();
- int startIndex = 0;
- while (!LayoutItems(startIndex))
- {
- overflowItems.Add(Items[startIndex]);
- startIndex++;
- }
- Refresh();
- }
- public BreadCrumbControl()
- {
- Items = new List<BreadCrumbItem>();
- visibleItems = new List<BreadCrumbItemBase>();
- overflowItems = new List<BreadCrumbItem>();
- PathSeparator = System.IO.Path.DirectorySeparatorChar.ToString();
- BackColor = SystemColors.Window;
- SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
- }
- }
- internal class BreadCrumbItemBase
- {
- private BreadCrumbControl owner;
- internal BreadCrumbControl Owner => owner;
- internal int Left { get; set; }
- internal int Top { get; set; }
- internal int Width { get; set; }
- internal int Height { get; set; }
- internal Rectangle Bounds => new Rectangle(Left, Top, Width, Height);
- internal bool IsMouseOver { get; set; }
- internal void SetOwner(BreadCrumbControl owner) => this.owner = owner;
- internal virtual void Measure()
- {
- Height = Owner.Height;
- }
- internal virtual int Layout(int xOffset)
- {
- bool rtl = Owner.RightToLeft == RightToLeft.Yes;
- Left = rtl ? xOffset - Width : xOffset;
- Top = 0;
- return rtl ? xOffset - Width : xOffset + Width;
- }
- internal virtual void Paint(Graphics g)
- {
- if (IsMouseOver)
- g.FillRectangle(SystemBrushes.ControlLight, Bounds);
- }
- internal virtual void Click()
- {
- }
- }
- internal class BreadCrumbItem : BreadCrumbItemBase
- {
- private int textHeight;
- public string Text { get; set; }
- public object Tag { get; set; }
- internal override void Measure()
- {
- #if AVALONIA
- TextRenderer.FontScale = (float)Owner.DpiScale;
- #endif
- var size = TextRenderer.MeasureText(Text, Owner.Font);
- Width = size.Width;
- textHeight = size.Height;
- Height = Owner.Height;
- }
- internal override void Paint(Graphics g)
- {
- base.Paint(g);
- TextRenderer.DrawText(g, Text, Owner.Font, new Point(Left, Top + (Height - textHeight) / 2), Owner.ForeColor,
- Owner.RightToLeft == RightToLeft.Yes ? TextFormatFlags.RightToLeft : TextFormatFlags.Default);
- }
- internal override void Click()
- {
- Owner.OnItemClick(this);
- }
- }
- internal class BreadCrumbImageItem : BreadCrumbItemBase
- {
- internal override void Measure()
- {
- Width = Owner.Image?.Width ?? 0;
- Height = Owner.Height;
- }
- internal override void Paint(Graphics g)
- {
- if (Owner.Image != null)
- {
- g.DrawImage(Owner.Image, Left, (Height - Owner.Image.Height) / 2, Owner.Image.Width, Owner.Image.Height);
- }
- }
- }
- internal class BreadCrumbOverflowItem : BreadCrumbItemBase
- {
- private ContextMenuStrip menu;
- private void CreateContextMenu()
- {
- if (menu == null)
- menu = new ContextMenuStrip();
- menu.Font = Owner.Font;
- menu.Renderer = Owner.OverflowMenuRenderer;
- menu.Items.Clear();
- foreach (var item in Owner.OverflowItems)
- {
- var menuItem = new ToolStripMenuItem() { Text = item.Text, Tag = item, Image = Owner.Image };
- menuItem.Click += (s, e) => Owner.OnItemClick((s as ToolStripMenuItem).Tag as BreadCrumbItem);
- menu.Items.Add(menuItem);
- }
- }
- internal override void Measure()
- {
- Width = Owner.LogicalToDevice(15);
- Height = Owner.Height;
- }
- internal override void Paint(Graphics g)
- {
- base.Paint(g);
- using (var pen = new Pen(Owner.ForeColor, Owner.LogicalToDevice(1f)))
- {
- bool rtl = Owner.RightToLeft == RightToLeft.Yes;
- int _3 = Owner.LogicalToDevice(3);
- int _6 = Owner.LogicalToDevice(6);
- int x = Left + _3;
- int y = Height / 2;
- g.DrawLines(pen, new Point[]
- {
- new Point(x + (rtl ? _3 : _6), y - _3),
- new Point(x + (rtl ? _6 : _3), y),
- new Point(x + (rtl ? _3 : _6), y + _3)
- });
- }
- }
- internal override void Click()
- {
- CreateContextMenu();
- menu.Show(Owner, new Point(Owner.RightToLeft == RightToLeft.Yes ? Left + Width : Left, Top + Height));
- }
- }
- internal class BreadCrumbSeparatorItem : BreadCrumbItemBase
- {
- internal override void Measure()
- {
- Width = Owner.LogicalToDevice(12);
- Height = Owner.Height;
- }
- internal override void Paint(Graphics g)
- {
- base.Paint(g);
- using (var pen = new Pen(Owner.ForeColor, Owner.LogicalToDevice(1f)))
- {
- bool rtl = Owner.RightToLeft == RightToLeft.Yes;
- int _3 = Owner.LogicalToDevice(3);
- int _6 = Owner.LogicalToDevice(6);
- int x = Left;
- int y = Height / 2;
- g.DrawLines(pen, new Point[]
- {
- new Point(x + (rtl ? _6 : _3), y - _3),
- new Point(x + (rtl ? _3 : _6), y),
- new Point(x + (rtl ? _6 : _3), y + _3)
- });
- }
- }
- }
- }
|