[KOE-125] Added focused border to tree control

This commit is contained in:
Patrick Simpson 2017-06-30 14:55:51 +02:00
parent b17ebb2ec4
commit 008c27026d
5 changed files with 98 additions and 75 deletions

View File

@ -1,6 +1,6 @@
/// Project : Kopano OL Extension
///
/// Copyright 2016 Kopano b.v.
/// Copyright 2017 Kopano b.v.
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License, version 3,
@ -138,6 +138,13 @@ namespace Acacia.Controls
set { _nodeIdent = value; Rerender(); }
}
public int BorderThickness
{
get
{
return BorderStyle == BorderStyle.FixedSingle ? 1 : 0;
}
}
#endregion
#region Images
@ -416,6 +423,8 @@ namespace Acacia.Controls
{
if (newHighlight != _highlightNode || _highlightPart != newPart)
{
bool oldFocused = Focused;
KTreeNode old = _highlightNode;
if (newHighlight != null && !newHighlight.IsSelectable)
@ -429,6 +438,10 @@ namespace Acacia.Controls
_highlightPart = newPart;
}
// Update the border if required
if (oldFocused != Focused)
RedrawBorder();
// Render old node without highlight
if (old != null)
Rerender(old);
@ -612,21 +625,6 @@ namespace Acacia.Controls
#endregion
#region Columns
// TODO
/*
private readonly TreeViewColumnCollection _columns;
[Category("Columns")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public TreeViewColumnCollection Columns
{
get { return _columns; }
}*/
#endregion
#region Rendering
private KTreeRenderer _renderer;
@ -864,6 +862,10 @@ namespace Acacia.Controls
e.Graphics.FillRectangle(SystemBrushes.Control,
ClientSize.Width - VerticalScrollBarWidth, ClientSize.Height - HorizontalScrollBarHeight,
VerticalScrollBarWidth, HorizontalScrollBarHeight);
// The scrollbars sometimes get lost, force a repaint
_verticalScrollBar.Refresh();
_horizontalScrollBar.Refresh();
}
#endregion
@ -1081,20 +1083,69 @@ namespace Acacia.Controls
#endregion
#region Focus
#region Border
public override bool Focused
{
get
{
return base.Focused || _highlightNode != null;
}
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
Invalidate();
RedrawBorder();
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
Invalidate();
RedrawBorder();
}
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
RedrawBorder();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WM.NCPAINT)
{
IntPtr hDC = User32.GetWindowDC(m.HWnd);
try
{
using (Graphics g = Graphics.FromHdc(hDC))
{
_renderer.RenderControlBorder(g, new Rectangle(0, 0, Width, Height));
}
}
finally
{
User32.ReleaseDC(m.HWnd, hDC);
}
return;
}
base.WndProc(ref m);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
RedrawBorder();
}
private void RedrawBorder()
{
// Force NCPaint update
User32.RedrawWindow(this.Handle, IntPtr.Zero, IntPtr.Zero,
User32.RedrawWindowFlags.Frame | User32.RedrawWindowFlags.Invalidate);
}
#endregion
#region Winforms Autogenerated
@ -1111,56 +1162,5 @@ namespace Acacia.Controls
#endregion
#region Disabled state
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
RedrawBorder();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WM.NCPAINT)
{
WmNcPaint(ref m);
return;
}
base.WndProc(ref m);
}
private void WmNcPaint(ref Message m)
{
if (BorderStyle == BorderStyle.None)
return;
IntPtr hDC = User32.GetWindowDC(m.HWnd);
try
{
using (Graphics g = Graphics.FromHdc(hDC))
{
_renderer.RenderControlBorder(g, new Rectangle(0, 0, Width, Height));
}
}
finally
{
User32.ReleaseDC(m.HWnd, hDC);
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
RedrawBorder();
}
private void RedrawBorder()
{
// Force NCPaint update
User32.RedrawWindow(this.Handle, IntPtr.Zero, IntPtr.Zero,
User32.RedrawWindowFlags.Frame | User32.RedrawWindowFlags.Invalidate /*| User32.RedrawWindowFlags.UpdateNow*/);
}
#endregion
}
}

View File

@ -117,6 +117,8 @@ namespace Acacia.Controls
Rectangle containerRect = dims.NodeRect;
containerRect.X = _clientRect.X;
containerRect.Width = Math.Max(_totalRect.Width, _clientRect.Width);
// Overlap the rectangle with the control border, to prevent duplicate lines
containerRect = containerRect.Expand(new Padding(_tree.BorderThickness));
// Selection background
RenderNodeOutline(graphics, node, _tree.FullRowSelect ? containerRect : dims.NodeRect, highlight);

View File

@ -66,6 +66,11 @@ namespace Acacia.Controls
private readonly VisualStyleRenderer _treeViewGlyphHotClosed = new VisualStyleRenderer(TREEVIEW, 4, 1);
private readonly VisualStyleRenderer _treeViewGlyphHotOpened = new VisualStyleRenderer(TREEVIEW, 4, 2);
// We use the combo box styles for the outline
private readonly VisualStyleRenderer _treeViewBorderNormal = new VisualStyleRenderer("COMBOBOX", 4, 1);
private readonly VisualStyleRenderer _treeViewBorderFocus = new VisualStyleRenderer("COMBOBOX", 4, 2);
private readonly VisualStyleRenderer _treeViewBorderDisabled = new VisualStyleRenderer("COMBOBOX", 4, 4);
private Size? _glyphSize;
protected override Size GetExpanderSize(Graphics graphics, KTreeNode node)
@ -124,8 +129,10 @@ namespace Acacia.Controls
protected override void RenderNodeOutline(Graphics graphics, KTreeNode node, Rectangle rect, KTreeNodeMeasurements.Part? highlight)
{
// Draw one pixel too far, to overlap top and bottom borders for a continuous selection
Rectangle highlightRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height + 1);
// If full-row selecting, compensate for shifted rectangle.
if (_tree.FullRowSelect)
highlightRect.Height -= 2 * _tree.BorderThickness;
if (_tree.ActiveNode == node && _tree.Focused)
{
if (node.IsSelected)
@ -148,11 +155,20 @@ namespace Acacia.Controls
public override void RenderControlBorder(Graphics graphics, Rectangle rect)
{
Color color = (_tree.Enabled ? _treeViewItemNormal : _treeViewItemDisabled).GetColor(ColorProperty.BorderColor);
using (Pen pen = new Pen(_tree.Enabled ? Color.Black : SystemColors.GrayText))
VisualStyleRenderer style;
if (_tree.Enabled)
{
graphics.DrawRectangle(pen, new Rectangle(rect.X, rect.Y, rect.Width - 1, rect.Height - 1));
if (_tree.Focused)
style = _treeViewBorderFocus;
else
style = _treeViewBorderNormal;
}
else
{
style = _treeViewBorderDisabled;
}
style.DrawBackground(graphics, rect, graphics.ClipBounds.ToRectangle());
}
}
}

View File

@ -131,6 +131,11 @@ namespace Acacia.Controls
return new Point(_this.Left, _this.Top);
}
public static Rectangle ToRectangle(this RectangleF _this)
{
return new Rectangle((int)_this.X, (int)_this.Y, (int)_this.Width, (int)_this.Height);
}
#endregion
}
}

View File

@ -1,4 +1,4 @@
/// Copyright 2016 Kopano b.v.
/// Copyright 2017 Kopano b.v.
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License, version 3,