/// <summary> /// Initialises a new instance of the <see cref="CanvasItemLayout"/> class /// for the specified container and using the given client bounds. /// </summary> /// <param name="container">The CanvasItem to which this layout belongs</param> /// <param name="clientBounds">A rectangle that indicates the client boundaries</param> public CanvasItemLayout(CanvasItem container, Rectangle clientBounds) { _backColor = SystemColors.Control; Container = container; ResetPosition(clientBounds); Padding = new Size(4, 4); }
/// <summary> /// Remove the selection from the specified item. /// </summary> private void ClearSelection() { if (_selectedItemIndex >= 0) { CanvasItem item = (CanvasItem)Controls[_selectedItemIndex]; item.Selected = false; _selectedItemIndex = -1; } }
/// <summary> /// Respond to a hover over a link in a static text item /// </summary> /// <param name="item">The item that raised this event</param> /// <param name="args">The hover arguments</param> public void HandleHover(CanvasItem item, CanvasHoverArgs args) { Point scrollOffset = AutoScrollPosition; args.Location = new Point(args.Location.X + scrollOffset.X, args.Location.Y + scrollOffset.Y); if (LinkHover != null) { LinkHover(item, args); } }
/// <summary> /// Override the resize event to force a re-layout of the content. /// </summary> /// <param name="e">Resize event arguments</param> protected override void OnResize(EventArgs e) { if (!_inLayout) { int index = 0; while (index < Controls.Count) { CanvasItem item = (CanvasItem)Controls[index]; item.SetBounds(0, 0, ClientRectangle.Width, 0, BoundsSpecified.Width); ++index; } LayoutControls(null); } }
/// <summary> /// If the container control gets focus, set the focus explicitly on the selected /// item. Otherwise set it on the first visible item. /// </summary> /// <param name="e">Focus event arguments</param> protected override void OnGotFocus(EventArgs e) { if (_selectedItemIndex >= 0) { CanvasItem item = (CanvasItem)Controls[_selectedItemIndex]; item.Focus(); } else { CanvasItem item = (CanvasItem)GetChildAtPoint(new Point(0, 0)); if (item != null) { item.Focus(); } } }
/// <summary> /// Called when the Items collection is changed. We make the necessary modifications to the /// Controls collection. /// </summary> private void ItemsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Reset: _selectedItemIndex = -1; TextSelectedItem = null; Controls.Clear(); break; case NotifyCollectionChangedAction.Remove: foreach (CanvasItem item in e.OldItems) { if (item.Selected) { _selectedItemIndex = -1; } if (_selectedItemIndex >= e.OldStartingIndex) { --_selectedItemIndex; } Controls.Remove(item); } _displayRectUpdate = true; if (!IsLayoutSuspended) { LayoutControls(null); } break; case NotifyCollectionChangedAction.Add: int index = e.NewStartingIndex; foreach (CanvasItem item in e.NewItems) { Controls.Add(item); Controls.SetChildIndex(item, index); if (_selectedItemIndex >= index) { ++_selectedItemIndex; } ++index; } _displayRectUpdate = true; if (!IsLayoutSuspended) { LayoutControls((CanvasItem) e.NewItems[0]); } break; } }
/// <summary> /// A selection was made on an item. We keep track of the item with the selection /// separately from the selected item. /// </summary> internal void HandleSelection(CanvasItem canvasItem) { if (TextSelectedItem != null && canvasItem != TextSelectedItem) { TextSelectedItem.ClearSelection(); } TextSelectedItem = canvasItem; }
/// <summary> /// Respond to a canvas item component being selected. ComponentID is the ID /// of the actual component under the mouse cursor. /// </summary> /// <param name="item">The item that raised this select</param> /// <param name="component">The component selected</param> internal void HandleSelect(CanvasItem item, CanvasElementBase component) { if (component == null || component.ID == ActionID.None) { if (AllowSelection) { Focus(); int selectedItem = Controls.IndexOf(item); if (selectedItem != _selectedItemIndex) { SelectItem(selectedItem, true); } } } if (CanvasItemAction != null) { CanvasItemAction(this, new CanvasItemArgs { Item = (component != null) ? component.ID : ActionID.None, Tag = (component != null) ? component.Tag : null, Control = item }); } }
/// <summary> /// Layout all the canvas items from either the given item or from /// the start if null is specified. /// </summary> public void LayoutControls(CanvasItem firstControl) { if (!_inLayout) { bool layoutAll = (firstControl == null); _inLayout = true; int index = (layoutAll) ? 0 : Controls.IndexOf(firstControl); if (index >= 0) { Point point; if (index > 0) { point = Controls[index - 1].Location; point.Y += Controls[index - 1].Height; } else { point = AutoScrollPosition; } int topOffset = Math.Abs(point.Y); int yCount = 0; while (index < Controls.Count && (layoutAll || yCount < ClientRectangle.Bottom * 2)) { CanvasItem item = (CanvasItem) Controls[index]; BoundsSpecified flags = BoundsSpecified.None; if (item.Bounds.X != point.X) { flags |= BoundsSpecified.X; } if (item.Bounds.Y != point.Y) { flags |= BoundsSpecified.Y; } if (item.Bounds.Width != ClientRectangle.Width) { flags |= BoundsSpecified.Width; } if (item.Bounds.Height != item.Height) { flags |= BoundsSpecified.Height; } item.SetBounds(point.X, point.Y, ClientRectangle.Width, item.Height, flags); point.Y += item.Height; yCount += item.Height - topOffset; topOffset = 0; ++index; } // If the collection changed then we need to update the virtual display // rectangle bounds too. if (_displayRectUpdate) { while (index < Controls.Count) { CanvasItem item = (CanvasItem) Controls[index++]; point.Y += item.Height; } AutoScrollMinSize = new Size(DisplayRectangle.Width, point.Y); _displayRectUpdate = false; } } _inLayout = false; } }
/// <summary> /// Respond to a link being clicked in a static text item. /// </summary> /// <param name="item">The item that raised this event</param> /// <param name="link">The URL</param> public void HandleLink(CanvasItem item, string link) { if (LinkClicked != null) { LinkClicked(item, new LinkClickedEventArgs(link)); } }
/// <summary> /// Marks the end of batch update of items and proceeds to render. /// </summary> public void EndUpdate(CanvasItem rootItem) { IsLayoutSuspended = false; LayoutControls(rootItem); ResumeLayout(true); }
/// <summary> /// If the item has selected text, extract that and use it as quotation /// in the message body. /// </summary> /// <param name="item">The item</param> /// <returns>Quoted text or an empty string</returns> private static string GetQuotedText(CanvasItem item) { if (item != null) { string selectedText = item.Selection; if (!string.IsNullOrWhiteSpace(selectedText)) { return selectedText.Quoted(); } } return string.Empty; }
/// <summary> /// Layout all the canvas items from either the given item or from /// the start if null is specified. /// </summary> public void LayoutControls(CanvasItem firstControl) { if (!_inLayout) { bool layoutAll = (firstControl == null); _inLayout = true; int index = (layoutAll) ? 0 : Controls.IndexOf(firstControl); if (index >= 0) { Point point; if (index > 0) { point = Controls[index - 1].Location; point.Y += Controls[index - 1].Height; } else { point = AutoScrollPosition; } int topOffset = Math.Abs(point.Y); int yCount = 0; while (index < Controls.Count && (layoutAll || yCount < ClientRectangle.Bottom * 2)) { CanvasItem item = (CanvasItem)Controls[index]; BoundsSpecified flags = BoundsSpecified.None; if (item.Bounds.X != point.X) { flags |= BoundsSpecified.X; } if (item.Bounds.Y != point.Y) { flags |= BoundsSpecified.Y; } if (item.Bounds.Width != ClientRectangle.Width) { flags |= BoundsSpecified.Width; } if (item.Bounds.Height != item.Height) { flags |= BoundsSpecified.Height; } item.SetBounds(point.X, point.Y, ClientRectangle.Width, item.Height, flags); point.Y += item.Height; yCount += item.Height - topOffset; topOffset = 0; ++index; } // If the collection changed then we need to update the virtual display // rectangle bounds too. if (_displayRectUpdate) { while (index < Controls.Count) { CanvasItem item = (CanvasItem)Controls[index++]; point.Y += item.Height; } AutoScrollMinSize = new Size(DisplayRectangle.Width, point.Y); _displayRectUpdate = false; } } _inLayout = false; } }