Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5203 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
15 changed files with 23 additions and 188 deletions
@ -1,174 +0,0 @@
@@ -1,174 +0,0 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
using System; |
||||
using System.Threading; |
||||
using System.Windows; |
||||
using System.Windows.Media; |
||||
using System.Windows.Media.Imaging; |
||||
using System.Windows.Threading; |
||||
|
||||
namespace ICSharpCode.Core.Presentation |
||||
{ |
||||
/// <summary>
|
||||
/// A WPF control that aligns its content on pixel boundaries.
|
||||
/// </summary>
|
||||
public class PixelSnapper : UIElement |
||||
{ |
||||
public PixelSnapper() |
||||
{ |
||||
} |
||||
|
||||
public PixelSnapper(UIElement visualChild) |
||||
: this() |
||||
{ |
||||
this.Child = visualChild; |
||||
} |
||||
|
||||
UIElement _visualChild; |
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the visual child.
|
||||
/// </summary>
|
||||
public UIElement Child { |
||||
get { return _visualChild; } |
||||
set { |
||||
RemoveVisualChild(_visualChild); |
||||
_visualChild = value; |
||||
AddVisualChild(_visualChild); |
||||
InvalidateMeasure(); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the visual child.
|
||||
/// </summary>
|
||||
protected override Visual GetVisualChild(int index) |
||||
{ |
||||
if (index == 0 && _visualChild != null) |
||||
return _visualChild; |
||||
else |
||||
throw new ArgumentOutOfRangeException("index"); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the number of visual children.
|
||||
/// </summary>
|
||||
protected override int VisualChildrenCount { |
||||
get { return _visualChild != null ? 1 : 0; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Measure the visual child.
|
||||
/// </summary>
|
||||
protected override Size MeasureCore(Size availableSize) |
||||
{ |
||||
if (_visualChild != null) { |
||||
_visualChild.Measure(availableSize); |
||||
return _visualChild.DesiredSize; |
||||
} else { |
||||
return base.MeasureCore(availableSize); |
||||
} |
||||
} |
||||
|
||||
protected override void ArrangeCore(Rect finalRect) |
||||
{ |
||||
base.ArrangeCore(finalRect); |
||||
if (_visualChild != null) { |
||||
_pixelOffset = GetPixelOffset(); |
||||
//LoggingService.Debug("Arrange, Pixel Offset=" + _pixelOffset);
|
||||
_visualChild.Arrange(new Rect(new Point(_pixelOffset.X, _pixelOffset.Y), finalRect.Size)); |
||||
|
||||
// check again after the whole layout pass has finished, maybe we need to move
|
||||
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new ThreadStart(CheckLayout)); |
||||
} |
||||
} |
||||
|
||||
private void CheckLayout() |
||||
{ |
||||
Point pixelOffset = GetPixelOffset(); |
||||
if (!AreClose(pixelOffset, _pixelOffset)) { |
||||
InvalidateArrange(); |
||||
} |
||||
} |
||||
|
||||
// Gets the matrix that will convert a point from "above" the
|
||||
// coordinate space of a visual into the the coordinate space
|
||||
// "below" the visual.
|
||||
static Matrix GetVisualTransform(Visual v) |
||||
{ |
||||
if (v != null) { |
||||
Matrix m = Matrix.Identity; |
||||
|
||||
Transform transform = VisualTreeHelper.GetTransform(v); |
||||
if (transform != null) { |
||||
m *= transform.Value; |
||||
} |
||||
|
||||
Vector offset = VisualTreeHelper.GetOffset(v); |
||||
m.Translate(offset.X, offset.Y); |
||||
|
||||
return m; |
||||
} |
||||
|
||||
return Matrix.Identity; |
||||
} |
||||
|
||||
static Point ApplyVisualTransform(Point point, Visual v, bool inverse) |
||||
{ |
||||
if (v != null) { |
||||
Matrix visualTransform = GetVisualTransform(v); |
||||
if (inverse) |
||||
visualTransform.Invert(); |
||||
point = visualTransform.Transform(point); |
||||
} |
||||
return point; |
||||
} |
||||
|
||||
private Point GetPixelOffset() |
||||
{ |
||||
Point pixelOffset = new Point(); |
||||
|
||||
PresentationSource ps = PresentationSource.FromVisual(this); |
||||
if (ps != null) { |
||||
Visual rootVisual = ps.RootVisual; |
||||
|
||||
// Transform (0,0) from this element up to pixels.
|
||||
pixelOffset = this.TransformToAncestor(rootVisual).Transform(pixelOffset); |
||||
pixelOffset = ApplyVisualTransform(pixelOffset, rootVisual, false); |
||||
pixelOffset = ps.CompositionTarget.TransformToDevice.Transform(pixelOffset); |
||||
|
||||
// Round the origin to the nearest whole pixel.
|
||||
pixelOffset.X = Math.Round(pixelOffset.X); |
||||
pixelOffset.Y = Math.Round(pixelOffset.Y); |
||||
|
||||
// Transform the whole-pixel back to this element.
|
||||
pixelOffset = ps.CompositionTarget.TransformFromDevice.Transform(pixelOffset); |
||||
pixelOffset = ApplyVisualTransform(pixelOffset, rootVisual, true); |
||||
pixelOffset = rootVisual.TransformToDescendant(this).Transform(pixelOffset); |
||||
} |
||||
|
||||
return pixelOffset; |
||||
} |
||||
|
||||
static bool AreClose(Point point1, Point point2) |
||||
{ |
||||
return AreClose(point1.X, point2.X) && AreClose(point1.Y, point2.Y); |
||||
} |
||||
|
||||
static bool AreClose(double value1, double value2) |
||||
{ |
||||
if (value1 == value2) |
||||
{ |
||||
return true; |
||||
} |
||||
double delta = value1 - value2; |
||||
return ((delta < 1.53E-06) && (delta > -1.53E-06)); |
||||
} |
||||
|
||||
private Point _pixelOffset; |
||||
} |
||||
} |
Loading…
Reference in new issue