Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion Src/Common/Controls/DetailControls/BasicTypeSlices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// </remarks>

using System;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Forms;
using System.Xml;
Expand Down Expand Up @@ -307,13 +308,58 @@ public override void PropChanged(int hvo, int tag, int ivMin, int cvIns, int cvD
/// </summary>
public class DateSlice : FieldSlice, IVwNotifyChange
{
private static readonly TraceSwitch s_wheelTraceSwitch = new TraceSwitch("DateSliceWheel", "");
private const int kWmMouseWheel = 0x020A;

private sealed class WheelForwardingRichTextBox : RichTextBox
{
protected override void WndProc(ref Message m)
{
if (m.Msg == kWmMouseWheel)
{
int delta = (short)((long)m.WParam >> 16);
bool handled = TryScrollOwningDataTree(this, delta);
TraceWheel(string.Format("RichTextBoxWndProc delta={0} handled={1}", delta, handled));
if (handled)
return;
}

base.WndProc(ref m);
}
}

private static void TraceWheel(string message)
{
if (s_wheelTraceSwitch.TraceInfo || s_wheelTraceSwitch.TraceVerbose)
Trace.WriteLine("DateSliceWheel: " + message);
}

internal static bool TryScrollOwningDataTree(Control control, int delta)
{
for (Control current = control; current != null; current = current.Parent)
{
var dataTree = current as DataTree;
if (dataTree != null)
{
bool handled = DataTree.TryHandleWheelScroll(dataTree, delta);
TraceWheel(string.Format(
"TryScrollOwningDataTree delta={0} dataTreeVisible={1} handled={2}",
delta, dataTree.Visible, handled));
return handled;
}
}

TraceWheel(string.Format("TryScrollOwningDataTree delta={0} no-datatree", delta));
return false;
}

/// -----------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="DateSlice"/> class.
/// </summary>
/// -----------------------------------------------------------------------------------
public DateSlice(LcmCache cache, ICmObject obj, int flid)
: base(new RichTextBox(), cache, obj, flid)
: base(new WheelForwardingRichTextBox(), cache, obj, flid)
{
// JohnT: per comment at the end of LT-7073, we want the normal window color for this
// slice. It's also nice to be able to select and copy. Setting ReadOnly is enough to prevent
Expand Down
44 changes: 44 additions & 0 deletions Src/Common/Controls/DetailControls/DataTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4531,6 +4531,50 @@ private bool EquivalentKeys(object[] newKey, object[] oldKey, bool fCheckInts)
}
return true;
}

internal static int GetWheelScrollPixels(DataTree dataTree, int delta)
{
if (delta == 0)
return 0;

int scrollLines = SystemInformation.MouseWheelScrollLines;
if (scrollLines == 0)
return 0;

if (scrollLines == int.MaxValue)
return Math.Sign(delta) * dataTree.ClientRectangle.Height;

double linesToScroll = (double)delta / SystemInformation.MouseWheelScrollDelta * scrollLines;
return (int)Math.Round(linesToScroll * dataTree.Font.Height, MidpointRounding.AwayFromZero);
}

internal static bool TryGetWheelScrollPosition(DataTree dataTree, int delta, out int newY)
{
int currentY = -dataTree.AutoScrollPosition.Y;
int maxScroll = Math.Max(0,
dataTree.AutoScrollMinSize.Height - dataTree.ClientRectangle.Height);
int pixelDelta = GetWheelScrollPixels(dataTree, delta);
newY = Math.Max(0, Math.Min(currentY - pixelDelta, maxScroll));
return newY != currentY;
}

internal static bool CanRedirectWheelMessage(DataTree dataTree)
{
return dataTree.IsHandleCreated && !dataTree.IsDisposed && dataTree.Visible;
}

internal static bool TryHandleWheelScroll(DataTree dataTree, int delta)
{
if (!CanRedirectWheelMessage(dataTree))
return false;

int newY;
if (!TryGetWheelScrollPosition(dataTree, delta, out newY))
return false;

dataTree.AutoScrollPosition = new Point(0, newY);
return true;
}
}

class DummyObjectSlice : Slice
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using NUnit.Framework;
Expand All @@ -31,6 +33,13 @@ public class DataTreeTests : MemoryOnlyBackendProviderRestoredForEachTestTestBas
private DataTree m_dtree;
private Form m_parent;

private sealed class ScrollTestDataTree : DataTree
{
protected override void OnPaint(PaintEventArgs e)
{
}
}

private CustomFieldForTest m_customField;
#region Fixture Setup and Teardown
internal static Inventory GenerateParts()
Expand Down Expand Up @@ -77,6 +86,30 @@ public override void FixtureSetup()
}
#endregion

private static DataTree CreateScrollableDataTree(Form parent)
{
var dataTree = new ScrollTestDataTree();
parent.Size = new Size(400, 200);
dataTree.Dock = DockStyle.Fill;
parent.Controls.Add(dataTree);

for (int i = 0; i < 12; i++)
{
var slice = new Slice(new Panel { Dock = DockStyle.Fill })
{
Visible = true,
Size = new Size(360, 50),
Location = new Point(0, i * 50)
};
dataTree.Controls.Add(slice);
slice.Install(dataTree);
}

parent.Show();
Application.DoEvents();
return dataTree;
}

#region Test setup and teardown
/// ------------------------------------------------------------------------------------
/// <summary>
Expand Down Expand Up @@ -275,6 +308,102 @@ public void GetGuidForJumpToTool_UsesRootObject_WhenNoCurrentSlice()
}
}

[Test]
public void GetWheelScrollPixels_UsesSystemWheelSettings()
{
m_dtree.Bounds = new Rectangle(0, 0, 200, 100);

int delta = SystemInformation.MouseWheelScrollDelta;
int scrollLines = SystemInformation.MouseWheelScrollLines;
int expectedPixels;
if (scrollLines == 0)
{
expectedPixels = 0;
}
else if (scrollLines == int.MaxValue)
{
expectedPixels = m_dtree.ClientRectangle.Height;
}
else
{
expectedPixels = (int)Math.Round((double)scrollLines * m_dtree.Font.Height,
MidpointRounding.AwayFromZero);
}

Assert.That(DataTree.GetWheelScrollPixels(m_dtree, delta), Is.EqualTo(expectedPixels));
Assert.That(DataTree.GetWheelScrollPixels(m_dtree, -delta), Is.EqualTo(-expectedPixels));
}

[Test]
public void TryGetWheelScrollPosition_ReturnsFalse_WhenAlreadyAtTop()
{
m_dtree.Bounds = new Rectangle(0, 0, 200, 100);
m_dtree.AutoScrollMinSize = new Size(200, 1000);
m_dtree.AutoScrollPosition = new Point(0, 0);

int newY;
bool handled = DataTree.TryGetWheelScrollPosition(m_dtree,
SystemInformation.MouseWheelScrollDelta, out newY);

Assert.That(handled, Is.False);
Assert.That(newY, Is.EqualTo(0));
}

[Test]
public void CanRedirectWheelMessage_ReturnsFalse_WhenDataTreeHidden()
{
m_parent.Show();
m_dtree.Show();
Assert.That(m_dtree.IsHandleCreated, Is.True);

m_dtree.Hide();

Assert.That(DataTree.CanRedirectWheelMessage(m_dtree), Is.False);
}

[Test]
public void TryHandleWheelScroll_UpdatesScrollPosition_WhenScrollingIsPossible()
{
using (var parent = new Form())
{
var dataTree = CreateScrollableDataTree(parent);
dataTree.AutoScrollPosition = new Point(0, 0);

bool handled = DataTree.TryHandleWheelScroll(dataTree, -SystemInformation.MouseWheelScrollDelta);

Assert.That(handled, Is.True);
Assert.That(-dataTree.AutoScrollPosition.Y, Is.GreaterThan(0));
}
}

[Test]
public void TryScrollOwningDataTree_ScrollsContainingDataTree_FromDateSliceHostedControl()
{
using (var parent = new Form())
{
var dataTree = CreateScrollableDataTree(parent);
dataTree.AutoScrollPosition = new Point(0, 0);

using (var host = new Panel())
using (var control = new RichTextBox())
{
host.Bounds = new Rectangle(0, 0, 150, 20);
control.Dock = DockStyle.Fill;
host.Controls.Add(control);
dataTree.Controls.Add(host);
host.Show();
control.Show();
Application.DoEvents();

bool handled = DateSlice.TryScrollOwningDataTree(control,
-SystemInformation.MouseWheelScrollDelta);

Assert.That(handled, Is.True);
Assert.That(-dataTree.AutoScrollPosition.Y, Is.GreaterThan(0));
}
}
}

/// <summary></summary>
[Test]
public void OwnedObjects()
Expand Down
Loading